move range aware aget to array.c from mruby-array-ext gem
[mruby.git] / src / string.c
index 860551c..6e5f91e 100644 (file)
@@ -5,25 +5,26 @@
 */
 
 #include <ctype.h>
-#ifndef SIZE_MAX
- /* Some versions of VC++
-  * has SIZE_MAX in stdint.h
-  */
-# include <limits.h>
-#endif
+#include <limits.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
 #include "mruby.h"
 #include "mruby/array.h"
 #include "mruby/class.h"
-#include "mruby/numeric.h"
 #include "mruby/range.h"
 #include "mruby/string.h"
 #include "re.h"
 
 const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz";
 
+typedef struct mrb_shared_string {
+  mrb_bool nofree;
+  int refcnt;
+  char *ptr;
+  mrb_int len;
+} mrb_shared_string;
+
 static mrb_value str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2);
 static mrb_value mrb_str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len);
 
@@ -32,12 +33,14 @@ static mrb_value mrb_str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_
       s->aux.capa = capacity;\
 } while(0)
 
-void
-mrb_str_decref(mrb_state *mrb, mrb_shared_string *shared)
+static void
+str_decref(mrb_state *mrb, mrb_shared_string *shared)
 {
   shared->refcnt--;
   if (shared->refcnt == 0) {
-    mrb_free(mrb, shared->ptr);
+    if (!shared->nofree) {
+      mrb_free(mrb, shared->ptr);
+    }
     mrb_free(mrb, shared);
   }
 }
@@ -51,6 +54,7 @@ mrb_str_modify(mrb_state *mrb, struct RString *s)
     if (shared->refcnt == 1 && s->ptr == shared->ptr) {
       s->ptr = shared->ptr;
       s->aux.capa = shared->len;
+      s->ptr[s->len] = '\0';
       mrb_free(mrb, shared);
     }
     else {
@@ -63,17 +67,30 @@ mrb_str_modify(mrb_state *mrb, struct RString *s)
       if (p) {
         memcpy(ptr, p, len);
       }
-      ptr[len] = 0;
+      ptr[len] =  '\0';
       s->ptr = ptr;
       s->aux.capa = len;
-      mrb_str_decref(mrb, shared);
+      str_decref(mrb, shared);
     }
     s->flags &= ~MRB_STR_SHARED;
+    return;
+  }
+  if (s->flags & MRB_STR_NOFREE) {
+    char *p = s->ptr;
+
+    s->ptr = (char *)mrb_malloc(mrb, (size_t)s->len+1);
+    if (p) {
+      memcpy(s->ptr, p, s->len);
+    }
+    s->ptr[s->len] = '\0';
+    s->aux.capa = s->len;
+    s->flags &= ~MRB_STR_NOFREE;
+    return;
   }
 }
 
 mrb_value
-mrb_str_resize(mrb_state *mrb, mrb_value str, int len)
+mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len)
 {
   int slen;
   struct RString *s = mrb_str_ptr(str);
@@ -81,26 +98,15 @@ mrb_str_resize(mrb_state *mrb, mrb_value str, int len)
   mrb_str_modify(mrb, s);
   slen = s->len;
   if (len != slen) {
-    if (slen < len || slen -len > 1024) {
-      s->ptr = (char *)mrb_realloc(mrb, s->ptr, len+1);
+    if (slen < len || slen - len > 256) {
+      RESIZE_CAPA(s, len);
     }
-    s->aux.capa = len;
     s->len = len;
     s->ptr[len] = '\0';   /* sentinel */
   }
   return str;
 }
 
-static inline void
-str_mod_check(mrb_state *mrb, mrb_value str, char *p, mrb_int len)
-{
-  struct RString *s = mrb_str_ptr(str);
-
-  if (s->ptr != p || s->len != len) {
-    mrb_raise(mrb, E_RUNTIME_ERROR, "string modified");
-  }
-}
-
 #define mrb_obj_alloc_string(mrb) ((struct RString*)mrb_obj_alloc((mrb), MRB_TT_STRING, (mrb)->string_class))
 
 /* char offset to byte offset */
@@ -111,14 +117,14 @@ mrb_str_offset(mrb_state *mrb, mrb_value str, int pos)
 }
 
 static struct RString*
-str_new(mrb_state *mrb, const char *p, int len)
+str_new(mrb_state *mrb, const char *p, mrb_int len)
 {
   struct RString *s;
 
   s = mrb_obj_alloc_string(mrb);
   s->len = len;
   s->aux.capa = len;
-  s->ptr = (char *)mrb_malloc(mrb, len+1);
+  s->ptr = (char *)mrb_malloc(mrb, (size_t)len+1);
   if (p) {
     memcpy(s->ptr, p, len);
   }
@@ -146,7 +152,7 @@ mrb_str_new_empty(mrb_state *mrb, mrb_value str)
 #endif
 
 mrb_value
-mrb_str_buf_new(mrb_state *mrb, int capa)
+mrb_str_buf_new(mrb_state *mrb, mrb_int capa)
 {
   struct RString *s;
 
@@ -176,7 +182,7 @@ str_buf_cat(mrb_state *mrb, struct RString *s, const char *ptr, size_t len)
   }
   if (len == 0) return;
   capa = s->aux.capa;
-  if (s->len >= MRB_INT_MAX - len) {
+  if (s->len >= MRB_INT_MAX - (mrb_int)len) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "string sizes too big");
   }
   total = s->len+len;
@@ -238,32 +244,47 @@ mrb_str_new_cstr(mrb_state *mrb, const char *p)
     len = 0;
   }
 
+  s = str_new(mrb, p, len);
+
+  return mrb_obj_value(s);
+}
+
+mrb_value
+mrb_str_new_static(mrb_state *mrb, const char *p, size_t len)
+{
+  struct RString *s;
+
   s = mrb_obj_alloc_string(mrb);
-  s->ptr = (char *)mrb_malloc(mrb, len+1);
-  if (p) {
-    memcpy(s->ptr, p, len);
-  }
-  s->ptr[len] = 0;
   s->len = len;
-  s->aux.capa = len;
-
+  s->aux.capa = 0;             /* nofree */
+  s->ptr = (char *)p;
+  s->flags = MRB_STR_NOFREE;
   return mrb_obj_value(s);
 }
 
+void
+mrb_gc_free_str(mrb_state *mrb, struct RString *str)
+{
+  if (str->flags & MRB_STR_SHARED)
+    str_decref(mrb, str->aux.shared);
+  else if ((str->flags & MRB_STR_NOFREE) == 0)
+    mrb_free(mrb, str->ptr);
+}
+
 char *
 mrb_str_to_cstr(mrb_state *mrb, mrb_value str0)
 {
-  mrb_value str;
+  struct RString *s;
 
   if (!mrb_string_p(str0)) {
       mrb_raise(mrb, E_TYPE_ERROR, "expected String");
   }
 
-  str = mrb_str_new(mrb, RSTRING_PTR(str0), RSTRING_LEN(str0));
-  if (strlen(RSTRING_PTR(str)) != RSTRING_LEN(str)) {
+  s = str_new(mrb, RSTRING_PTR(str0), RSTRING_LEN(str0));
+  if ((strlen(s->ptr) ^ s->len) != 0) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte");
   }
-  return RSTRING_PTR(str);
+  return s->ptr;
 }
 
 static void
@@ -273,11 +294,19 @@ str_make_shared(mrb_state *mrb, struct RString *s)
     mrb_shared_string *shared = (mrb_shared_string *)mrb_malloc(mrb, sizeof(mrb_shared_string));
 
     shared->refcnt = 1;
-    if (s->aux.capa > s->len) {
-      s->ptr = shared->ptr = (char *)mrb_realloc(mrb, s->ptr, s->len+1);
+    if (s->flags & MRB_STR_NOFREE) {
+      shared->nofree = TRUE;
+      shared->ptr = s->ptr;
+      s->flags &= ~MRB_STR_NOFREE;
     }
     else {
-      shared->ptr = s->ptr;
+      shared->nofree = FALSE;
+      if (s->aux.capa > s->len) {
+        s->ptr = shared->ptr = (char *)mrb_realloc(mrb, s->ptr, s->len+1);
+      }
+      else {
+        shared->ptr = s->ptr;
+      }
     }
     shared->len = s->len;
     s->aux.shared = shared;
@@ -286,35 +315,6 @@ str_make_shared(mrb_state *mrb, struct RString *s)
 }
 
 /*
- *  call-seq: (Caution! string literal)
- *     String.new(str="")   => new_str
- *
- *  Returns a new string object containing a copy of <i>str</i>.
- */
-
-mrb_value
-mrb_str_literal(mrb_state *mrb, mrb_value str)
-{
-  struct RString *s, *orig;
-  mrb_shared_string *shared;
-
-  s = mrb_obj_alloc_string(mrb);
-  orig = mrb_str_ptr(str);
-  if (!(orig->flags & MRB_STR_SHARED)) {
-    str_make_shared(mrb, mrb_str_ptr(str));
-  }
-  shared = orig->aux.shared;
-  shared->refcnt++;
-  s->ptr = shared->ptr;
-  s->len = shared->len;
-  s->aux.capa = 0;
-  s->aux.shared = shared;
-  s->flags |= MRB_STR_SHARED;
-
-  return mrb_obj_value(s);
-}
-
-/*
  *  call-seq:
  *     char* str = String("abcd"), len=strlen("abcd")
  *
@@ -339,7 +339,7 @@ void
 mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other)
 {
   struct RString *s1 = mrb_str_ptr(self), *s2;
-  int len;
+  mrb_int len;
 
   mrb_str_modify(mrb, s1);
   if (!mrb_string_p(other)) {
@@ -354,7 +354,7 @@ mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other)
   }
   memcpy(s1->ptr+s1->len, s2->ptr, s2->len);
   s1->len = len;
-  s1->ptr[len] = 0;
+  s1->ptr[len] =  '\0';
 }
 
 /*
@@ -529,10 +529,10 @@ mrb_str_cmp_m(mrb_state *mrb, mrb_value str1)
 
   mrb_get_args(mrb, "o", &str2);
   if (!mrb_string_p(str2)) {
-    if (!mrb_respond_to(mrb, str2, mrb_intern2(mrb, "to_s", 4))) {
+    if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "to_s"))) {
       return mrb_nil_value();
     }
-    else if (!mrb_respond_to(mrb, str2, mrb_intern2(mrb, "<=>", 3))) {
+    else if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "<=>"))) {
       return mrb_nil_value();
     }
     else {
@@ -551,25 +551,24 @@ mrb_str_cmp_m(mrb_state *mrb, mrb_value str1)
   return mrb_fixnum_value(result);
 }
 
-static int
+static mrb_bool
 str_eql(mrb_state *mrb, const mrb_value str1, const mrb_value str2)
 {
-  const size_t len = RSTRING_LEN(str1);
+  const mrb_int len = RSTRING_LEN(str1);
 
-  /* assert(SIZE_MAX >= MRB_INT_MAX) */
   if (len != RSTRING_LEN(str2)) return FALSE;
-  if (memcmp(RSTRING_PTR(str1), RSTRING_PTR(str2), len) == 0)
+  if (memcmp(RSTRING_PTR(str1), RSTRING_PTR(str2), (size_t)len) == 0)
     return TRUE;
   return FALSE;
 }
 
-int
+mrb_bool
 mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2)
 {
   if (mrb_obj_equal(mrb, str1, str2)) return TRUE;
   if (!mrb_string_p(str2)) {
     if (mrb_nil_p(str2)) return FALSE;
-    if (!mrb_respond_to(mrb, str2, mrb_intern2(mrb, "to_str", 6))) {
+    if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "to_str"))) {
       return FALSE;
     }
     str2 = mrb_funcall(mrb, str2, "to_str", 0);
@@ -633,7 +632,7 @@ noregexp(mrb_state *mrb, mrb_value self)
 static void
 regexp_check(mrb_state *mrb, mrb_value obj)
 {
-  if (!strcmp(mrb_obj_classname(mrb, obj), REGEXP_CLASS)) {
+  if (!memcmp(mrb_obj_classname(mrb, obj), REGEXP_CLASS, sizeof(REGEXP_CLASS) - 1)) {
     noregexp(mrb, obj);
   }
 }
@@ -742,12 +741,10 @@ num_index:
       /* check if indx is Range */
       {
         mrb_int beg, len;
-        mrb_value tmp;
 
         len = RSTRING_LEN(str);
         if (mrb_range_beg_len(mrb, indx, &beg, &len, len)) {
-          tmp = mrb_str_subseq(mrb, str, beg, len);
-          return tmp;
+          return mrb_str_subseq(mrb, str, beg, len);
         }
         else {
           return mrb_nil_value();
@@ -1234,8 +1231,8 @@ mrb_str_include(mrb_state *mrb, mrb_value self)
   mrb_bool include_p;
 
   mrb_get_args(mrb, "o", &str2);
-  if (mrb_type(str2) == MRB_TT_FIXNUM) {
-    include_p = memchr(RSTRING_PTR(self), mrb_fixnum(str2), RSTRING_LEN(self));
+  if (mrb_fixnum_p(str2)) {
+    include_p = (memchr(RSTRING_PTR(self), mrb_fixnum(str2), RSTRING_LEN(self)) != NULL);
   }
   else {
     str2 = mrb_str_to_str(mrb, str2);
@@ -1337,7 +1334,7 @@ str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2)
   if (s2->flags & MRB_STR_SHARED) {
   L_SHARE:
     if (s1->flags & MRB_STR_SHARED){
-      mrb_str_decref(mrb, s1->aux.shared);
+      str_decref(mrb, s1->aux.shared);
     }
     else {
       mrb_free(mrb, s1->ptr);
@@ -1354,7 +1351,7 @@ str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2)
   }
   else {
     if (s1->flags & MRB_STR_SHARED) {
-      mrb_str_decref(mrb, s1->aux.shared);
+      str_decref(mrb, s1->aux.shared);
       s1->flags &= ~MRB_STR_SHARED;
       s1->ptr = (char *)mrb_malloc(mrb, s2->len+1);
     }
@@ -1456,7 +1453,7 @@ mrb_ptr_to_str(mrb_state *mrb, void *p)
   struct RString *p_str;
   char *p1;
   char *p2;
-  intptr_t n = (intptr_t)p;
+  uintptr_t n = (uintptr_t)p;
 
   p_str = str_new(mrb, NULL, 2 + sizeof(uintptr_t) * CHAR_BIT / 4);
   p1 = p_str->ptr;
@@ -1481,6 +1478,12 @@ mrb_ptr_to_str(mrb_state *mrb, void *p)
 }
 
 mrb_value
+mrb_string_type(mrb_state *mrb, mrb_value str)
+{
+  return mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str");
+}
+
+mrb_value
 mrb_check_string_type(mrb_state *mrb, mrb_value str)
 {
   return mrb_check_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str");
@@ -2171,7 +2174,7 @@ mrb_str_to_dbl(mrb_state *mrb, mrb_value str, int badcheck)
 static mrb_value
 mrb_str_to_f(mrb_state *mrb, mrb_value self)
 {
-  return mrb_float_value(mrb_str_to_dbl(mrb, self, 0/*Qfalse*/));
+  return mrb_float_value(mrb, mrb_str_to_dbl(mrb, self, 0/*Qfalse*/));
 }
 
 /* 15.2.10.5.40 */
@@ -2304,62 +2307,57 @@ mrb_str_dump(mrb_state *mrb, mrb_value str)
         *q++ = '\\';
         *q++ = 'n';
         break;
-    
+
       case '\r':
         *q++ = '\\';
         *q++ = 'r';
         break;
-    
+
       case '\t':
         *q++ = '\\';
         *q++ = 't';
         break;
-    
+
       case '\f':
         *q++ = '\\';
         *q++ = 'f';
         break;
-    
+
       case '\013':
         *q++ = '\\';
         *q++ = 'v';
         break;
-    
+
       case '\010':
         *q++ = '\\';
         *q++ = 'b';
         break;
-    
+
       case '\007':
         *q++ = '\\';
         *q++ = 'a';
         break;
-    
+
       case '\033':
         *q++ = '\\';
         *q++ = 'e';
         break;
-    
+
       case '#':
         if (IS_EVSTR(p, pend)) *q++ = '\\';
         *q++ = '#';
         break;
-  
+
       default:
         if (ISPRINT(c)) {
           *q++ = c;
         }
         else {
-          mrb_value octstr;
-          mrb_value chr;
-          const char *ptr;
-          int len;
-          chr = mrb_fixnum_value(c & 0xff);
-          octstr = mrb_fixnum_to_str(mrb, chr, 8);
-          ptr = mrb_str_body(octstr, &len);
-          memcpy(q, "\\000", 4);
-          memcpy(q + 4 - len, ptr, len);
-          q += 4;
+          *q++ = '\\';
+          q[2] = '0' + c % 8; c /= 8;
+          q[1] = '0' + c % 8; c /= 8;
+          q[0] = '0' + c % 8;
+          q += 3;
         }
     }
   }
@@ -2368,9 +2366,9 @@ mrb_str_dump(mrb_state *mrb, mrb_value str)
 }
 
 mrb_value
-mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, mrb_int len)
+mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len)
 {
-  if (len < 0) {
+  if ((mrb_int)len < 0) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "negative string size (or size too big)");
   }
   str_buf_cat(mrb, mrb_str_ptr(str), ptr, len);
@@ -2412,7 +2410,7 @@ mrb_str_inspect(mrb_state *mrb, mrb_value str)
 
     p = RSTRING_PTR(str); pend = RSTRING_END(str);
     for (;p < pend; p++) {
-      unsigned int c, cc;
+      unsigned char c, cc;
 
       c = *p;
       if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p, pend))) {
@@ -2443,15 +2441,10 @@ mrb_str_inspect(mrb_state *mrb, mrb_value str)
           continue;
       }
       else {
-        mrb_value octstr;
-        mrb_value chr;
-        const char *ptr;
-        int len;
-        chr = mrb_fixnum_value(c & 0xff);
-        octstr = mrb_fixnum_to_str(mrb, chr, 8);
-        ptr = mrb_str_body(octstr, &len);
-        memcpy(buf, "\\000", 4);
-        memcpy(buf + 4 - len, ptr, len);
+        buf[0] = '\\';
+        buf[3] = '0' + c % 8; c /= 8;
+        buf[2] = '0' + c % 8; c /= 8;
+        buf[1] = '0' + c % 8;
         mrb_str_buf_cat(mrb, result, buf, 4);
         continue;
       }
@@ -2494,54 +2487,47 @@ mrb_init_string(mrb_state *mrb)
   MRB_SET_INSTANCE_TT(s, MRB_TT_STRING);
   mrb_include_module(mrb, s, mrb_class_get(mrb, "Comparable"));
 
-  mrb_define_method(mrb, s, "+",               mrb_str_plus_m,          ARGS_REQ(1));              /* 15.2.10.5.2  */
-  mrb_define_method(mrb, s, "bytesize",        mrb_str_bytesize,        ARGS_NONE());
-  mrb_define_method(mrb, s, "size",            mrb_str_size,            ARGS_NONE());              /* 15.2.10.5.33 */
-  mrb_define_method(mrb, s, "length",          mrb_str_size,            ARGS_NONE());              /* 15.2.10.5.26 */
-  mrb_define_method(mrb, s, "*",               mrb_str_times,           ARGS_REQ(1));              /* 15.2.10.5.1  */
-  mrb_define_method(mrb, s, "<=>",             mrb_str_cmp_m,           ARGS_REQ(1));              /* 15.2.10.5.3  */
-  mrb_define_method(mrb, s, "==",              mrb_str_equal_m,         ARGS_REQ(1));              /* 15.2.10.5.4  */
-  mrb_define_method(mrb, s, "=~",              noregexp,                ARGS_REQ(1));              /* 15.2.10.5.5  */
-  mrb_define_method(mrb, s, "[]",              mrb_str_aref_m,          ARGS_ANY());               /* 15.2.10.5.6  */
-  mrb_define_method(mrb, s, "capitalize",      mrb_str_capitalize,      ARGS_NONE());              /* 15.2.10.5.7  */
-  mrb_define_method(mrb, s, "capitalize!",     mrb_str_capitalize_bang, ARGS_REQ(1));              /* 15.2.10.5.8  */
-  mrb_define_method(mrb, s, "chomp",           mrb_str_chomp,           ARGS_ANY());               /* 15.2.10.5.9  */
-  mrb_define_method(mrb, s, "chomp!",          mrb_str_chomp_bang,      ARGS_ANY());               /* 15.2.10.5.10 */
-  mrb_define_method(mrb, s, "chop",            mrb_str_chop,            ARGS_REQ(1));              /* 15.2.10.5.11 */
-  mrb_define_method(mrb, s, "chop!",           mrb_str_chop_bang,       ARGS_REQ(1));              /* 15.2.10.5.12 */
-  mrb_define_method(mrb, s, "downcase",        mrb_str_downcase,        ARGS_NONE());              /* 15.2.10.5.13 */
-  mrb_define_method(mrb, s, "downcase!",       mrb_str_downcase_bang,   ARGS_NONE());              /* 15.2.10.5.14 */
-  mrb_define_method(mrb, s, "empty?",          mrb_str_empty_p,         ARGS_NONE());              /* 15.2.10.5.16 */
-  mrb_define_method(mrb, s, "eql?",            mrb_str_eql,             ARGS_REQ(1));              /* 15.2.10.5.17 */
-
-  // NOTE: Regexp not implemented
-  mrb_define_method(mrb, s, "gsub",            noregexp,                ARGS_REQ(1));              /* 15.2.10.5.18 */
-  mrb_define_method(mrb, s, "gsub!",           noregexp,                ARGS_REQ(1));              /* 15.2.10.5.19 */
-
-  mrb_define_method(mrb, s, "hash",            mrb_str_hash_m,          ARGS_REQ(1));              /* 15.2.10.5.20 */
-  mrb_define_method(mrb, s, "include?",        mrb_str_include,         ARGS_REQ(1));              /* 15.2.10.5.21 */
-  mrb_define_method(mrb, s, "index",           mrb_str_index_m,         ARGS_ANY());               /* 15.2.10.5.22 */
-  mrb_define_method(mrb, s, "initialize",      mrb_str_init,            ARGS_REQ(1));              /* 15.2.10.5.23 */
-  mrb_define_method(mrb, s, "initialize_copy", mrb_str_replace,         ARGS_REQ(1));              /* 15.2.10.5.24 */
-  mrb_define_method(mrb, s, "intern",          mrb_str_intern,          ARGS_NONE());              /* 15.2.10.5.25 */
-  mrb_define_method(mrb, s, "match",           noregexp,                ARGS_REQ(1));              /* 15.2.10.5.27 */
-  mrb_define_method(mrb, s, "replace",         mrb_str_replace,         ARGS_REQ(1));              /* 15.2.10.5.28 */
-  mrb_define_method(mrb, s, "reverse",         mrb_str_reverse,         ARGS_NONE());              /* 15.2.10.5.29 */
-  mrb_define_method(mrb, s, "reverse!",        mrb_str_reverse_bang,    ARGS_NONE());              /* 15.2.10.5.30 */
-  mrb_define_method(mrb, s, "rindex",          mrb_str_rindex_m,        ARGS_ANY());               /* 15.2.10.5.31 */
-  mrb_define_method(mrb, s, "scan",            noregexp,                ARGS_REQ(1));              /* 15.2.10.5.32 */
-  mrb_define_method(mrb, s, "slice",           mrb_str_aref_m,          ARGS_ANY());               /* 15.2.10.5.34 */
-  mrb_define_method(mrb, s, "split",           mrb_str_split_m,         ARGS_ANY());               /* 15.2.10.5.35 */
-  mrb_define_method(mrb, s, "sub",             noregexp,                ARGS_REQ(1));              /* 15.2.10.5.36 */
-  mrb_define_method(mrb, s, "sub!",            noregexp,                ARGS_REQ(1));              /* 15.2.10.5.37 */
-
-  mrb_define_method(mrb, s, "to_i",            mrb_str_to_i,            ARGS_ANY());               /* 15.2.10.5.38 */
-  mrb_define_method(mrb, s, "to_f",            mrb_str_to_f,            ARGS_NONE());              /* 15.2.10.5.39 */
-  mrb_define_method(mrb, s, "to_s",            mrb_str_to_s,            ARGS_NONE());              /* 15.2.10.5.40 */
-  mrb_define_method(mrb, s, "to_str",          mrb_str_to_s,            ARGS_NONE());              /* 15.2.10.5.40 */
-  mrb_define_method(mrb, s, "to_sym",          mrb_str_intern,          ARGS_NONE());              /* 15.2.10.5.41 */
-  mrb_define_method(mrb, s, "upcase",          mrb_str_upcase,          ARGS_REQ(1));              /* 15.2.10.5.42 */
-  mrb_define_method(mrb, s, "upcase!",         mrb_str_upcase_bang,     ARGS_REQ(1));              /* 15.2.10.5.43 */
-  mrb_define_method(mrb, s, "inspect",         mrb_str_inspect,         ARGS_NONE());              /* 15.2.10.5.46(x) */
-  mrb_define_method(mrb, s, "bytes",           mrb_str_bytes,           ARGS_NONE());
+
+  mrb_define_method(mrb, s, "bytesize",        mrb_str_bytesize,        MRB_ARGS_NONE());
+
+  mrb_define_method(mrb, s, "<=>",             mrb_str_cmp_m,           MRB_ARGS_REQ(1)); /* 15.2.10.5.1  */
+  mrb_define_method(mrb, s, "==",              mrb_str_equal_m,         MRB_ARGS_REQ(1)); /* 15.2.10.5.2  */
+  mrb_define_method(mrb, s, "+",               mrb_str_plus_m,          MRB_ARGS_REQ(1)); /* 15.2.10.5.4  */
+  mrb_define_method(mrb, s, "*",               mrb_str_times,           MRB_ARGS_REQ(1)); /* 15.2.10.5.5  */
+  mrb_define_method(mrb, s, "[]",              mrb_str_aref_m,          MRB_ARGS_ANY());  /* 15.2.10.5.6  */
+  mrb_define_method(mrb, s, "capitalize",      mrb_str_capitalize,      MRB_ARGS_NONE()); /* 15.2.10.5.7  */
+  mrb_define_method(mrb, s, "capitalize!",     mrb_str_capitalize_bang, MRB_ARGS_REQ(1)); /* 15.2.10.5.8  */
+  mrb_define_method(mrb, s, "chomp",           mrb_str_chomp,           MRB_ARGS_ANY());  /* 15.2.10.5.9  */
+  mrb_define_method(mrb, s, "chomp!",          mrb_str_chomp_bang,      MRB_ARGS_ANY());  /* 15.2.10.5.10 */
+  mrb_define_method(mrb, s, "chop",            mrb_str_chop,            MRB_ARGS_REQ(1)); /* 15.2.10.5.11 */
+  mrb_define_method(mrb, s, "chop!",           mrb_str_chop_bang,       MRB_ARGS_REQ(1)); /* 15.2.10.5.12 */
+  mrb_define_method(mrb, s, "downcase",        mrb_str_downcase,        MRB_ARGS_NONE()); /* 15.2.10.5.13 */
+  mrb_define_method(mrb, s, "downcase!",       mrb_str_downcase_bang,   MRB_ARGS_NONE()); /* 15.2.10.5.14 */
+  mrb_define_method(mrb, s, "empty?",          mrb_str_empty_p,         MRB_ARGS_NONE()); /* 15.2.10.5.16 */
+  mrb_define_method(mrb, s, "eql?",            mrb_str_eql,             MRB_ARGS_REQ(1)); /* 15.2.10.5.17 */
+
+  mrb_define_method(mrb, s, "hash",            mrb_str_hash_m,          MRB_ARGS_REQ(1)); /* 15.2.10.5.20 */
+  mrb_define_method(mrb, s, "include?",        mrb_str_include,         MRB_ARGS_REQ(1)); /* 15.2.10.5.21 */
+  mrb_define_method(mrb, s, "index",           mrb_str_index_m,         MRB_ARGS_ANY());  /* 15.2.10.5.22 */
+  mrb_define_method(mrb, s, "initialize",      mrb_str_init,            MRB_ARGS_REQ(1)); /* 15.2.10.5.23 */
+  mrb_define_method(mrb, s, "initialize_copy", mrb_str_replace,         MRB_ARGS_REQ(1)); /* 15.2.10.5.24 */
+  mrb_define_method(mrb, s, "intern",          mrb_str_intern,          MRB_ARGS_NONE()); /* 15.2.10.5.25 */
+  mrb_define_method(mrb, s, "length",          mrb_str_size,            MRB_ARGS_NONE()); /* 15.2.10.5.26 */
+  mrb_define_method(mrb, s, "replace",         mrb_str_replace,         MRB_ARGS_REQ(1)); /* 15.2.10.5.28 */
+  mrb_define_method(mrb, s, "reverse",         mrb_str_reverse,         MRB_ARGS_NONE()); /* 15.2.10.5.29 */
+  mrb_define_method(mrb, s, "reverse!",        mrb_str_reverse_bang,    MRB_ARGS_NONE()); /* 15.2.10.5.30 */
+  mrb_define_method(mrb, s, "rindex",          mrb_str_rindex_m,        MRB_ARGS_ANY());  /* 15.2.10.5.31 */
+  mrb_define_method(mrb, s, "size",            mrb_str_size,            MRB_ARGS_NONE()); /* 15.2.10.5.33 */
+  mrb_define_method(mrb, s, "slice",           mrb_str_aref_m,          MRB_ARGS_ANY());  /* 15.2.10.5.34 */
+  mrb_define_method(mrb, s, "split",           mrb_str_split_m,         MRB_ARGS_ANY());  /* 15.2.10.5.35 */
+
+  mrb_define_method(mrb, s, "to_f",            mrb_str_to_f,            MRB_ARGS_NONE()); /* 15.2.10.5.38 */
+  mrb_define_method(mrb, s, "to_i",            mrb_str_to_i,            MRB_ARGS_ANY());  /* 15.2.10.5.39 */
+  mrb_define_method(mrb, s, "to_s",            mrb_str_to_s,            MRB_ARGS_NONE()); /* 15.2.10.5.40 */
+  mrb_define_method(mrb, s, "to_str",          mrb_str_to_s,            MRB_ARGS_NONE());
+  mrb_define_method(mrb, s, "to_sym",          mrb_str_intern,          MRB_ARGS_NONE()); /* 15.2.10.5.41 */
+  mrb_define_method(mrb, s, "upcase",          mrb_str_upcase,          MRB_ARGS_REQ(1)); /* 15.2.10.5.42 */
+  mrb_define_method(mrb, s, "upcase!",         mrb_str_upcase_bang,     MRB_ARGS_REQ(1)); /* 15.2.10.5.43 */
+  mrb_define_method(mrb, s, "inspect",         mrb_str_inspect,         MRB_ARGS_NONE()); /* 15.2.10.5.46(x) */
+  mrb_define_method(mrb, s, "bytes",           mrb_str_bytes,           MRB_ARGS_NONE());
 }