source: EcnlProtoTool/trunk/mruby-1.2.0/src/string.c@ 270

Last change on this file since 270 was 270, checked in by coas-nagasima, 7 years ago

mruby版ECNLプロトタイピング・ツールを追加

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-csrc
File size: 66.6 KB
Line 
1/*
2** string.c - String class
3**
4** See Copyright Notice in mruby.h
5*/
6
7#include <float.h>
8#include <limits.h>
9#include <stddef.h>
10#include <stdlib.h>
11#include <string.h>
12#include "mruby.h"
13#include "mruby/array.h"
14#include "mruby/class.h"
15#include "mruby/range.h"
16#include "mruby/string.h"
17#include "mruby/re.h"
18
19typedef struct mrb_shared_string {
20 mrb_bool nofree : 1;
21 int refcnt;
22 char *ptr;
23 mrb_int len;
24} mrb_shared_string;
25
26const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz";
27
28#define mrb_obj_alloc_string(mrb) ((struct RString*)mrb_obj_alloc((mrb), MRB_TT_STRING, (mrb)->string_class))
29
30static struct RString*
31str_new_static(mrb_state *mrb, const char *p, size_t len)
32{
33 struct RString *s;
34
35 if (len >= MRB_INT_MAX) {
36 mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
37 }
38 s = mrb_obj_alloc_string(mrb);
39 s->as.heap.len = len;
40 s->as.heap.aux.capa = 0; /* nofree */
41 s->as.heap.ptr = (char *)p;
42 s->flags = MRB_STR_NOFREE;
43
44 return s;
45}
46
47static struct RString*
48str_new(mrb_state *mrb, const char *p, size_t len)
49{
50 struct RString *s;
51
52 if (p && mrb_ro_data_p(p)) {
53 return str_new_static(mrb, p, len);
54 }
55 s = mrb_obj_alloc_string(mrb);
56 if (len < RSTRING_EMBED_LEN_MAX) {
57 RSTR_SET_EMBED_FLAG(s);
58 RSTR_SET_EMBED_LEN(s, len);
59 if (p) {
60 memcpy(s->as.ary, p, len);
61 }
62 } else {
63 if (len >= MRB_INT_MAX) {
64 mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
65 }
66 s->as.heap.len = len;
67 s->as.heap.aux.capa = len;
68 s->as.heap.ptr = (char *)mrb_malloc(mrb, len+1);
69 if (p) {
70 memcpy(s->as.heap.ptr, p, len);
71 }
72 }
73 RSTR_PTR(s)[len] = '\0';
74 return s;
75}
76
77static inline void
78str_with_class(mrb_state *mrb, struct RString *s, mrb_value obj)
79{
80 s->c = mrb_str_ptr(obj)->c;
81}
82
83static mrb_value
84mrb_str_new_empty(mrb_state *mrb, mrb_value str)
85{
86 struct RString *s = str_new(mrb, 0, 0);
87
88 str_with_class(mrb, s, str);
89 return mrb_obj_value(s);
90}
91
92#ifndef MRB_STR_BUF_MIN_SIZE
93# define MRB_STR_BUF_MIN_SIZE 128
94#endif
95
96MRB_API mrb_value
97mrb_str_buf_new(mrb_state *mrb, size_t capa)
98{
99 struct RString *s;
100
101 s = mrb_obj_alloc_string(mrb);
102
103 if (capa >= MRB_INT_MAX) {
104 mrb_raise(mrb, E_ARGUMENT_ERROR, "string capacity size too big");
105 }
106 if (capa < MRB_STR_BUF_MIN_SIZE) {
107 capa = MRB_STR_BUF_MIN_SIZE;
108 }
109 s->as.heap.len = 0;
110 s->as.heap.aux.capa = capa;
111 s->as.heap.ptr = (char *)mrb_malloc(mrb, capa+1);
112 RSTR_PTR(s)[0] = '\0';
113
114 return mrb_obj_value(s);
115}
116
117static inline void
118resize_capa(mrb_state *mrb, struct RString *s, mrb_int capacity)
119{
120 if (RSTR_EMBED_P(s)) {
121 if (RSTRING_EMBED_LEN_MAX < capacity) {
122 char *const tmp = (char *)mrb_malloc(mrb, capacity+1);
123 const mrb_int len = RSTR_EMBED_LEN(s);
124 memcpy(tmp, s->as.ary, len);
125 RSTR_UNSET_EMBED_FLAG(s);
126 s->as.heap.ptr = tmp;
127 s->as.heap.len = len;
128 s->as.heap.aux.capa = capacity;
129 }
130 }
131 else {
132 s->as.heap.ptr = (char *)mrb_realloc(mrb, RSTR_PTR(s), capacity+1);
133 s->as.heap.aux.capa = capacity;
134 }
135}
136
137static void
138str_buf_cat(mrb_state *mrb, struct RString *s, const char *ptr, size_t len)
139{
140 size_t capa;
141 size_t total;
142 ptrdiff_t off = -1;
143
144 if (len == 0) return;
145 mrb_str_modify(mrb, s);
146 if (ptr >= RSTR_PTR(s) && ptr <= RSTR_PTR(s) + (size_t)RSTR_LEN(s)) {
147 off = ptr - RSTR_PTR(s);
148 }
149
150 if (RSTR_EMBED_P(s))
151 capa = RSTRING_EMBED_LEN_MAX;
152 else
153 capa = s->as.heap.aux.capa;
154
155 if (RSTR_LEN(s) >= MRB_INT_MAX - (mrb_int)len) {
156 mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
157 }
158 total = RSTR_LEN(s)+len;
159 if (capa <= total) {
160 while (total > capa) {
161 if (capa + 1 >= MRB_INT_MAX / 2) {
162 capa = (total + 4095) / 4096;
163 break;
164 }
165 capa = (capa + 1) * 2;
166 }
167 resize_capa(mrb, s, capa);
168 }
169 if (off != -1) {
170 ptr = RSTR_PTR(s) + off;
171 }
172 memcpy(RSTR_PTR(s) + RSTR_LEN(s), ptr, len);
173 mrb_assert_int_fit(size_t, total, mrb_int, MRB_INT_MAX);
174 RSTR_SET_LEN(s, total);
175 RSTR_PTR(s)[total] = '\0'; /* sentinel */
176}
177
178MRB_API mrb_value
179mrb_str_new(mrb_state *mrb, const char *p, size_t len)
180{
181 return mrb_obj_value(str_new(mrb, p, len));
182}
183
184/*
185 * call-seq: (Caution! NULL string)
186 * String.new(str="") => new_str
187 *
188 * Returns a new string object containing a copy of <i>str</i>.
189 */
190
191MRB_API mrb_value
192mrb_str_new_cstr(mrb_state *mrb, const char *p)
193{
194 struct RString *s;
195 size_t len;
196
197 if (p) {
198 len = strlen(p);
199 }
200 else {
201 len = 0;
202 }
203
204 s = str_new(mrb, p, len);
205
206 return mrb_obj_value(s);
207}
208
209MRB_API mrb_value
210mrb_str_new_static(mrb_state *mrb, const char *p, size_t len)
211{
212 struct RString *s = str_new_static(mrb, p, len);
213 return mrb_obj_value(s);
214}
215
216static void
217str_decref(mrb_state *mrb, mrb_shared_string *shared)
218{
219 shared->refcnt--;
220 if (shared->refcnt == 0) {
221 if (!shared->nofree) {
222 mrb_free(mrb, shared->ptr);
223 }
224 mrb_free(mrb, shared);
225 }
226}
227
228void
229mrb_gc_free_str(mrb_state *mrb, struct RString *str)
230{
231 if (RSTR_EMBED_P(str))
232 /* no code */;
233 else if (RSTR_SHARED_P(str))
234 str_decref(mrb, str->as.heap.aux.shared);
235 else if (!RSTR_NOFREE_P(str))
236 mrb_free(mrb, str->as.heap.ptr);
237}
238
239#ifdef MRB_UTF8_STRING
240static const char utf8len_codepage[256] =
241{
242 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
243 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
244 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
245 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
246 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
247 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
248 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
249 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1,
250};
251
252static mrb_int
253utf8len(const char* p, const char* e)
254{
255 mrb_int len;
256 mrb_int i;
257
258 len = utf8len_codepage[(unsigned char)*p];
259 if (p + len > e) return 1;
260 for (i = 1; i < len; ++i)
261 if ((p[i] & 0xc0) != 0x80)
262 return 1;
263 return len;
264}
265
266static mrb_int
267utf8_strlen(mrb_value str, mrb_int len)
268{
269 mrb_int total = 0;
270 char* p = RSTRING_PTR(str);
271 char* e = p;
272 e += len < 0 ? RSTRING_LEN(str) : len;
273 while (p<e) {
274 p += utf8len(p, e);
275 total++;
276 }
277 return total;
278}
279
280#define RSTRING_CHAR_LEN(s) utf8_strlen(s, -1)
281
282/* map character index to byte offset index */
283static mrb_int
284chars2bytes(mrb_value s, mrb_int off, mrb_int idx)
285{
286 mrb_int i, b, n;
287 const char *p = RSTRING_PTR(s) + off;
288 const char *e = RSTRING_END(s);
289
290 for (b=i=0; p<e && i<idx; i++) {
291 n = utf8len(p, e);
292 b += n;
293 p += n;
294 }
295 return b;
296}
297
298/* map byte offset to character index */
299static mrb_int
300bytes2chars(char *p, mrb_int bi)
301{
302 mrb_int i, b, n;
303
304 for (b=i=0; b<bi; i++) {
305 n = utf8len(p, p+bi);
306 b += n;
307 p += n;
308 }
309 return i;
310}
311
312#else
313#define RSTRING_CHAR_LEN(s) RSTRING_LEN(s)
314#define chars2bytes(p, off, ci) (ci)
315#define bytes2chars(p, bi) (bi)
316#endif
317
318static inline mrb_int
319mrb_memsearch_qs(const unsigned char *xs, mrb_int m, const unsigned char *ys, mrb_int n)
320{
321 const unsigned char *x = xs, *xe = xs + m;
322 const unsigned char *y = ys;
323 int i, qstable[256];
324
325 /* Preprocessing */
326 for (i = 0; i < 256; ++i)
327 qstable[i] = m + 1;
328 for (; x < xe; ++x)
329 qstable[*x] = xe - x;
330 /* Searching */
331 for (; y + m <= ys + n; y += *(qstable + y[m])) {
332 if (*xs == *y && memcmp(xs, y, m) == 0)
333 return y - ys;
334 }
335 return -1;
336}
337
338static mrb_int
339mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n)
340{
341 const unsigned char *x = (const unsigned char *)x0, *y = (const unsigned char *)y0;
342
343 if (m > n) return -1;
344 else if (m == n) {
345 return memcmp(x0, y0, m) == 0 ? 0 : -1;
346 }
347 else if (m < 1) {
348 return 0;
349 }
350 else if (m == 1) {
351 const unsigned char *ys = y, *ye = ys + n;
352 for (; y < ye; ++y) {
353 if (*x == *y)
354 return y - ys;
355 }
356 return -1;
357 }
358 return mrb_memsearch_qs((const unsigned char *)x0, m, (const unsigned char *)y0, n);
359}
360
361static void
362str_make_shared(mrb_state *mrb, struct RString *s)
363{
364 if (!RSTR_SHARED_P(s)) {
365 mrb_shared_string *shared = (mrb_shared_string *)mrb_malloc(mrb, sizeof(mrb_shared_string));
366
367 shared->refcnt = 1;
368 if (RSTR_EMBED_P(s)) {
369 const mrb_int len = RSTR_EMBED_LEN(s);
370 char *const tmp = (char *)mrb_malloc(mrb, len+1);
371 memcpy(tmp, s->as.ary, len);
372 tmp[len] = '\0';
373 RSTR_UNSET_EMBED_FLAG(s);
374 s->as.heap.ptr = tmp;
375 s->as.heap.len = len;
376 shared->nofree = FALSE;
377 shared->ptr = s->as.heap.ptr;
378 }
379 else if (RSTR_NOFREE_P(s)) {
380 shared->nofree = TRUE;
381 shared->ptr = s->as.heap.ptr;
382 RSTR_UNSET_NOFREE_FLAG(s);
383 }
384 else {
385 shared->nofree = FALSE;
386 if (s->as.heap.aux.capa > s->as.heap.len) {
387 s->as.heap.ptr = shared->ptr = (char *)mrb_realloc(mrb, s->as.heap.ptr, s->as.heap.len+1);
388 }
389 else {
390 shared->ptr = s->as.heap.ptr;
391 }
392 }
393 shared->len = s->as.heap.len;
394 s->as.heap.aux.shared = shared;
395 RSTR_SET_SHARED_FLAG(s);
396 }
397}
398
399static mrb_value
400byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
401{
402 struct RString *orig, *s;
403 mrb_shared_string *shared;
404
405 orig = mrb_str_ptr(str);
406 if (RSTR_EMBED_P(orig)) {
407 s = str_new(mrb, orig->as.ary+beg, len);
408 }
409 else {
410 str_make_shared(mrb, orig);
411 shared = orig->as.heap.aux.shared;
412 s = mrb_obj_alloc_string(mrb);
413 s->as.heap.ptr = orig->as.heap.ptr + beg;
414 s->as.heap.len = len;
415 s->as.heap.aux.shared = shared;
416 RSTR_SET_SHARED_FLAG(s);
417 shared->refcnt++;
418 }
419
420 return mrb_obj_value(s);
421}
422#ifdef MRB_UTF8_STRING
423static inline mrb_value
424str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
425{
426 beg = chars2bytes(str, 0, beg);
427 len = chars2bytes(str, beg, len);
428
429 return byte_subseq(mrb, str, beg, len);
430}
431#else
432#define str_subseq(mrb, str, beg, len) byte_subseq(mrb, str, beg, len)
433#endif
434
435static mrb_value
436str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
437{
438 mrb_int clen = RSTRING_CHAR_LEN(str);
439
440 if (len < 0) return mrb_nil_value();
441 if (clen == 0) {
442 len = 0;
443 }
444 else if (beg < 0) {
445 beg = clen + beg;
446 }
447 if (beg > clen) return mrb_nil_value();
448 if (beg < 0) {
449 beg += clen;
450 if (beg < 0) return mrb_nil_value();
451 }
452 if (beg + len > clen)
453 len = clen - beg;
454 if (len <= 0) {
455 len = 0;
456 }
457 return str_subseq(mrb, str, beg, len);
458}
459
460static mrb_int
461str_index(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int offset)
462{
463 mrb_int pos;
464 char *s, *sptr;
465 mrb_int len, slen;
466
467 len = RSTRING_LEN(str);
468 slen = RSTRING_LEN(sub);
469 if (offset < 0) {
470 offset += len;
471 if (offset < 0) return -1;
472 }
473 if (len - offset < slen) return -1;
474 s = RSTRING_PTR(str);
475 if (offset) {
476 s += offset;
477 }
478 if (slen == 0) return offset;
479 /* need proceed one character at a time */
480 sptr = RSTRING_PTR(sub);
481 slen = RSTRING_LEN(sub);
482 len = RSTRING_LEN(str) - offset;
483 pos = mrb_memsearch(sptr, slen, s, len);
484 if (pos < 0) return pos;
485 return pos + offset;
486}
487
488static void
489check_frozen(mrb_state *mrb, struct RString *s)
490{
491 if (RSTR_FROZEN_P(s)) {
492 mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen string");
493 }
494}
495
496static mrb_value
497str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2)
498{
499 long len;
500
501 check_frozen(mrb, s1);
502 len = RSTR_LEN(s2);
503 if (RSTR_SHARED_P(s1)) {
504 str_decref(mrb, s1->as.heap.aux.shared);
505 }
506 else if (!RSTR_EMBED_P(s1) && !RSTR_NOFREE_P(s1)) {
507 mrb_free(mrb, s1->as.heap.ptr);
508 }
509
510 RSTR_UNSET_NOFREE_FLAG(s1);
511
512 if (RSTR_SHARED_P(s2)) {
513L_SHARE:
514 RSTR_UNSET_EMBED_FLAG(s1);
515 s1->as.heap.ptr = s2->as.heap.ptr;
516 s1->as.heap.len = len;
517 s1->as.heap.aux.shared = s2->as.heap.aux.shared;
518 RSTR_SET_SHARED_FLAG(s1);
519 s1->as.heap.aux.shared->refcnt++;
520 }
521 else {
522 if (len <= RSTRING_EMBED_LEN_MAX) {
523 RSTR_UNSET_SHARED_FLAG(s1);
524 RSTR_SET_EMBED_FLAG(s1);
525 memcpy(s1->as.ary, RSTR_PTR(s2), len);
526 RSTR_SET_EMBED_LEN(s1, len);
527 }
528 else {
529 str_make_shared(mrb, s2);
530 goto L_SHARE;
531 }
532 }
533
534 return mrb_obj_value(s1);
535}
536
537static mrb_int
538str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos)
539{
540 char *s, *sbeg, *t;
541 struct RString *ps = mrb_str_ptr(str);
542 mrb_int len = RSTRING_LEN(sub);
543
544 /* substring longer than string */
545 if (RSTR_LEN(ps) < len) return -1;
546 if (RSTR_LEN(ps) - pos < len) {
547 pos = RSTR_LEN(ps) - len;
548 }
549 sbeg = RSTR_PTR(ps);
550 s = RSTR_PTR(ps) + pos;
551 t = RSTRING_PTR(sub);
552 if (len) {
553 while (sbeg <= s) {
554 if (memcmp(s, t, len) == 0) {
555 return s - RSTR_PTR(ps);
556 }
557 s--;
558 }
559 return -1;
560 }
561 else {
562 return pos;
563 }
564}
565
566MRB_API mrb_int
567mrb_str_strlen(mrb_state *mrb, struct RString *s)
568{
569 mrb_int i, max = RSTR_LEN(s);
570 char *p = RSTR_PTR(s);
571
572 if (!p) return 0;
573 for (i=0; i<max; i++) {
574 if (p[i] == '\0') {
575 mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte");
576 }
577 }
578 return max;
579}
580
581#ifdef _WIN32
582#include <windows.h>
583
584char*
585mrb_utf8_from_locale(const char *str, size_t len)
586{
587 wchar_t* wcsp;
588 char* mbsp;
589 size_t mbssize, wcssize;
590
591 if (len == 0)
592 return strdup("");
593 if (len == -1)
594 len = strlen(str);
595 wcssize = MultiByteToWideChar(GetACP(), 0, str, len, NULL, 0);
596 wcsp = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t));
597 if (!wcsp)
598 return NULL;
599 wcssize = MultiByteToWideChar(GetACP(), 0, str, len, wcsp, wcssize + 1);
600 wcsp[wcssize] = 0;
601
602 mbssize = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) wcsp, -1, NULL, 0, NULL, NULL);
603 mbsp = (char*) malloc((mbssize + 1));
604 if (!mbsp) {
605 free(wcsp);
606 return NULL;
607 }
608 mbssize = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) wcsp, -1, mbsp, mbssize, NULL, NULL);
609 mbsp[mbssize] = 0;
610 free(wcsp);
611 return mbsp;
612}
613
614char*
615mrb_locale_from_utf8(const char *utf8, size_t len)
616{
617 wchar_t* wcsp;
618 char* mbsp;
619 size_t mbssize, wcssize;
620
621 if (len == 0)
622 return strdup("");
623 if (len == -1)
624 len = strlen(utf8);
625 wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, len, NULL, 0);
626 wcsp = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t));
627 if (!wcsp)
628 return NULL;
629 wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, len, wcsp, wcssize + 1);
630 wcsp[wcssize] = 0;
631 mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsp, -1, NULL, 0, NULL, NULL);
632 mbsp = (char*) malloc((mbssize + 1));
633 if (!mbsp) {
634 free(wcsp);
635 return NULL;
636 }
637 mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsp, -1, mbsp, mbssize, NULL, NULL);
638 mbsp[mbssize] = 0;
639 free(wcsp);
640 return mbsp;
641}
642#endif
643
644MRB_API void
645mrb_str_modify(mrb_state *mrb, struct RString *s)
646{
647 check_frozen(mrb, s);
648 if (RSTR_SHARED_P(s)) {
649 mrb_shared_string *shared = s->as.heap.aux.shared;
650
651 if (shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) {
652 s->as.heap.ptr = shared->ptr;
653 s->as.heap.aux.capa = shared->len;
654 RSTR_PTR(s)[s->as.heap.len] = '\0';
655 mrb_free(mrb, shared);
656 }
657 else {
658 char *ptr, *p;
659 mrb_int len;
660
661 p = RSTR_PTR(s);
662 len = s->as.heap.len;
663 ptr = (char *)mrb_malloc(mrb, (size_t)len + 1);
664 if (p) {
665 memcpy(ptr, p, len);
666 }
667 ptr[len] = '\0';
668 s->as.heap.ptr = ptr;
669 s->as.heap.aux.capa = len;
670 str_decref(mrb, shared);
671 }
672 RSTR_UNSET_SHARED_FLAG(s);
673 return;
674 }
675 if (RSTR_NOFREE_P(s)) {
676 char *p = s->as.heap.ptr;
677
678 s->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)s->as.heap.len+1);
679 if (p) {
680 memcpy(RSTR_PTR(s), p, s->as.heap.len);
681 }
682 RSTR_PTR(s)[s->as.heap.len] = '\0';
683 s->as.heap.aux.capa = s->as.heap.len;
684 RSTR_UNSET_NOFREE_FLAG(s);
685 return;
686 }
687}
688
689static mrb_value
690mrb_str_freeze(mrb_state *mrb, mrb_value str)
691{
692 struct RString *s = mrb_str_ptr(str);
693
694 RSTR_SET_FROZEN_FLAG(s);
695 return str;
696}
697
698MRB_API mrb_value
699mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len)
700{
701 mrb_int slen;
702 struct RString *s = mrb_str_ptr(str);
703
704 mrb_str_modify(mrb, s);
705 slen = RSTR_LEN(s);
706 if (len != slen) {
707 if (slen < len || slen - len > 256) {
708 resize_capa(mrb, s, len);
709 }
710 RSTR_SET_LEN(s, len);
711 RSTR_PTR(s)[len] = '\0'; /* sentinel */
712 }
713 return str;
714}
715
716MRB_API char*
717mrb_str_to_cstr(mrb_state *mrb, mrb_value str0)
718{
719 struct RString *s;
720
721 if (!mrb_string_p(str0)) {
722 mrb_raise(mrb, E_TYPE_ERROR, "expected String");
723 }
724
725 s = str_new(mrb, RSTRING_PTR(str0), RSTRING_LEN(str0));
726 if ((strlen(RSTR_PTR(s)) ^ RSTR_LEN(s)) != 0) {
727 mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte");
728 }
729 return RSTR_PTR(s);
730}
731
732/*
733 * call-seq: (Caution! String("abcd") change)
734 * String("abcdefg") = String("abcd") + String("efg")
735 *
736 * Returns a new string object containing a copy of <i>str</i>.
737 */
738MRB_API void
739mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other)
740{
741 struct RString *s1 = mrb_str_ptr(self), *s2;
742 mrb_int len;
743
744 mrb_str_modify(mrb, s1);
745 if (!mrb_string_p(other)) {
746 other = mrb_str_to_str(mrb, other);
747 }
748 s2 = mrb_str_ptr(other);
749 len = RSTR_LEN(s1) + RSTR_LEN(s2);
750
751 if (RSTRING_CAPA(self) < len) {
752 resize_capa(mrb, s1, len);
753 }
754 memcpy(RSTR_PTR(s1)+RSTR_LEN(s1), RSTR_PTR(s2), RSTR_LEN(s2));
755 RSTR_SET_LEN(s1, len);
756 RSTR_PTR(s1)[len] = '\0';
757}
758
759/*
760 * call-seq: (Caution! String("abcd") remain)
761 * String("abcdefg") = String("abcd") + String("efg")
762 *
763 * Returns a new string object containing a copy of <i>str</i>.
764 */
765MRB_API mrb_value
766mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b)
767{
768 struct RString *s = mrb_str_ptr(a);
769 struct RString *s2 = mrb_str_ptr(b);
770 struct RString *t;
771
772 t = str_new(mrb, 0, RSTR_LEN(s) + RSTR_LEN(s2));
773 memcpy(RSTR_PTR(t), RSTR_PTR(s), RSTR_LEN(s));
774 memcpy(RSTR_PTR(t) + RSTR_LEN(s), RSTR_PTR(s2), RSTR_LEN(s2));
775
776 return mrb_obj_value(t);
777}
778
779/* 15.2.10.5.2 */
780
781/*
782 * call-seq: (Caution! String("abcd") remain) for stack_argument
783 * String("abcdefg") = String("abcd") + String("efg")
784 *
785 * Returns a new string object containing a copy of <i>str</i>.
786 */
787static mrb_value
788mrb_str_plus_m(mrb_state *mrb, mrb_value self)
789{
790 mrb_value str;
791
792 mrb_get_args(mrb, "S", &str);
793 return mrb_str_plus(mrb, self, str);
794}
795
796/* 15.2.10.5.26 */
797/* 15.2.10.5.33 */
798/*
799 * call-seq:
800 * "abcd".size => int
801 *
802 * Returns the length of string.
803 */
804static mrb_value
805mrb_str_size(mrb_state *mrb, mrb_value self)
806{
807 mrb_int len = RSTRING_CHAR_LEN(self);
808 return mrb_fixnum_value(len);
809}
810
811static mrb_value
812mrb_str_bytesize(mrb_state *mrb, mrb_value self)
813{
814 mrb_int len = RSTRING_LEN(self);
815 return mrb_fixnum_value(len);
816}
817
818/* 15.2.10.5.1 */
819/*
820 * call-seq:
821 * str * integer => new_str
822 *
823 * Copy---Returns a new <code>String</code> containing <i>integer</i> copies of
824 * the receiver.
825 *
826 * "Ho! " * 3 #=> "Ho! Ho! Ho! "
827 */
828static mrb_value
829mrb_str_times(mrb_state *mrb, mrb_value self)
830{
831 mrb_int n,len,times;
832 struct RString *str2;
833 char *p;
834
835 mrb_get_args(mrb, "i", &times);
836 if (times < 0) {
837 mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument");
838 }
839 if (times && MRB_INT_MAX / times < RSTRING_LEN(self)) {
840 mrb_raise(mrb, E_ARGUMENT_ERROR, "argument too big");
841 }
842
843 len = RSTRING_LEN(self)*times;
844 str2 = str_new(mrb, 0, len);
845 str_with_class(mrb, str2, self);
846 p = RSTR_PTR(str2);
847 if (len > 0) {
848 n = RSTRING_LEN(self);
849 memcpy(p, RSTRING_PTR(self), n);
850 while (n <= len/2) {
851 memcpy(p + n, p, n);
852 n *= 2;
853 }
854 memcpy(p + n, p, len-n);
855 }
856 p[RSTR_LEN(str2)] = '\0';
857
858 return mrb_obj_value(str2);
859}
860/* -------------------------------------------------------------- */
861
862#define lesser(a,b) (((a)>(b))?(b):(a))
863
864/* ---------------------------*/
865/*
866 * call-seq:
867 * mrb_value str1 <=> mrb_value str2 => int
868 * > 1
869 * = 0
870 * < -1
871 */
872MRB_API int
873mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2)
874{
875 mrb_int len;
876 mrb_int retval;
877 struct RString *s1 = mrb_str_ptr(str1);
878 struct RString *s2 = mrb_str_ptr(str2);
879
880 len = lesser(RSTR_LEN(s1), RSTR_LEN(s2));
881 retval = memcmp(RSTR_PTR(s1), RSTR_PTR(s2), len);
882 if (retval == 0) {
883 if (RSTR_LEN(s1) == RSTR_LEN(s2)) return 0;
884 if (RSTR_LEN(s1) > RSTR_LEN(s2)) return 1;
885 return -1;
886 }
887 if (retval > 0) return 1;
888 return -1;
889}
890
891/* 15.2.10.5.3 */
892
893/*
894 * call-seq:
895 * str <=> other_str => -1, 0, +1
896 *
897 * Comparison---Returns -1 if <i>other_str</i> is less than, 0 if
898 * <i>other_str</i> is equal to, and +1 if <i>other_str</i> is greater than
899 * <i>str</i>. If the strings are of different lengths, and the strings are
900 * equal when compared up to the shortest length, then the longer string is
901 * considered greater than the shorter one. If the variable <code>$=</code> is
902 * <code>false</code>, the comparison is based on comparing the binary values
903 * of each character in the string. In older versions of Ruby, setting
904 * <code>$=</code> allowed case-insensitive comparisons; this is now deprecated
905 * in favor of using <code>String#casecmp</code>.
906 *
907 * <code><=></code> is the basis for the methods <code><</code>,
908 * <code><=</code>, <code>></code>, <code>>=</code>, and <code>between?</code>,
909 * included from module <code>Comparable</code>. The method
910 * <code>String#==</code> does not use <code>Comparable#==</code>.
911 *
912 * "abcdef" <=> "abcde" #=> 1
913 * "abcdef" <=> "abcdef" #=> 0
914 * "abcdef" <=> "abcdefg" #=> -1
915 * "abcdef" <=> "ABCDEF" #=> 1
916 */
917static mrb_value
918mrb_str_cmp_m(mrb_state *mrb, mrb_value str1)
919{
920 mrb_value str2;
921 mrb_int result;
922
923 mrb_get_args(mrb, "o", &str2);
924 if (!mrb_string_p(str2)) {
925 if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "to_s"))) {
926 return mrb_nil_value();
927 }
928 else if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "<=>"))) {
929 return mrb_nil_value();
930 }
931 else {
932 mrb_value tmp = mrb_funcall(mrb, str2, "<=>", 1, str1);
933
934 if (mrb_nil_p(tmp)) return mrb_nil_value();
935 if (!mrb_fixnum(tmp)) {
936 return mrb_funcall(mrb, mrb_fixnum_value(0), "-", 1, tmp);
937 }
938 result = -mrb_fixnum(tmp);
939 }
940 }
941 else {
942 result = mrb_str_cmp(mrb, str1, str2);
943 }
944 return mrb_fixnum_value(result);
945}
946
947static mrb_bool
948str_eql(mrb_state *mrb, const mrb_value str1, const mrb_value str2)
949{
950 const mrb_int len = RSTRING_LEN(str1);
951
952 if (len != RSTRING_LEN(str2)) return FALSE;
953 if (memcmp(RSTRING_PTR(str1), RSTRING_PTR(str2), (size_t)len) == 0)
954 return TRUE;
955 return FALSE;
956}
957
958MRB_API mrb_bool
959mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2)
960{
961 if (mrb_immediate_p(str2)) return FALSE;
962 if (!mrb_string_p(str2)) {
963 if (mrb_nil_p(str2)) return FALSE;
964 if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "to_str"))) {
965 return FALSE;
966 }
967 str2 = mrb_funcall(mrb, str2, "to_str", 0);
968 return mrb_equal(mrb, str2, str1);
969 }
970 return str_eql(mrb, str1, str2);
971}
972
973/* 15.2.10.5.4 */
974/*
975 * call-seq:
976 * str == obj => true or false
977 *
978 * Equality---
979 * If <i>obj</i> is not a <code>String</code>, returns <code>false</code>.
980 * Otherwise, returns <code>false</code> or <code>true</code>
981 *
982 * caution:if <i>str</i> <code><=></code> <i>obj</i> returns zero.
983 */
984static mrb_value
985mrb_str_equal_m(mrb_state *mrb, mrb_value str1)
986{
987 mrb_value str2;
988
989 mrb_get_args(mrb, "o", &str2);
990
991 return mrb_bool_value(mrb_str_equal(mrb, str1, str2));
992}
993/* ---------------------------------- */
994MRB_API mrb_value
995mrb_str_to_str(mrb_state *mrb, mrb_value str)
996{
997 mrb_value s;
998
999 if (!mrb_string_p(str)) {
1000 s = mrb_check_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str");
1001 if (mrb_nil_p(s)) {
1002 s = mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_s");
1003 }
1004 return s;
1005 }
1006 return str;
1007}
1008
1009MRB_API const char*
1010mrb_string_value_ptr(mrb_state *mrb, mrb_value ptr)
1011{
1012 mrb_value str = mrb_str_to_str(mrb, ptr);
1013 return RSTRING_PTR(str);
1014}
1015
1016void
1017mrb_noregexp(mrb_state *mrb, mrb_value self)
1018{
1019 mrb_raise(mrb, E_NOTIMP_ERROR, "Regexp class not implemented");
1020}
1021
1022void
1023mrb_regexp_check(mrb_state *mrb, mrb_value obj)
1024{
1025 if (mrb_regexp_p(mrb, obj)) {
1026 mrb_noregexp(mrb, obj);
1027 }
1028}
1029
1030MRB_API mrb_value
1031mrb_str_dup(mrb_state *mrb, mrb_value str)
1032{
1033 struct RString *s = mrb_str_ptr(str);
1034 struct RString *dup = str_new(mrb, 0, 0);
1035
1036 str_with_class(mrb, dup, str);
1037 return str_replace(mrb, dup, s);
1038}
1039
1040static mrb_value
1041mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx)
1042{
1043 mrb_int idx;
1044
1045 mrb_regexp_check(mrb, indx);
1046 switch (mrb_type(indx)) {
1047 case MRB_TT_FIXNUM:
1048 idx = mrb_fixnum(indx);
1049
1050num_index:
1051 str = str_substr(mrb, str, idx, 1);
1052 if (!mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value();
1053 return str;
1054
1055 case MRB_TT_STRING:
1056 if (str_index(mrb, str, indx, 0) != -1)
1057 return mrb_str_dup(mrb, indx);
1058 return mrb_nil_value();
1059
1060 case MRB_TT_RANGE:
1061 /* check if indx is Range */
1062 {
1063 mrb_int beg, len;
1064
1065 len = RSTRING_CHAR_LEN(str);
1066 if (mrb_range_beg_len(mrb, indx, &beg, &len, len)) {
1067 return str_subseq(mrb, str, beg, len);
1068 }
1069 else {
1070 return mrb_nil_value();
1071 }
1072 }
1073 case MRB_TT_FLOAT:
1074 default:
1075 indx = mrb_Integer(mrb, indx);
1076 if (mrb_nil_p(indx)) {
1077 mrb_raise(mrb, E_TYPE_ERROR, "can't convert to Fixnum");
1078 }
1079 idx = mrb_fixnum(indx);
1080 goto num_index;
1081 }
1082 return mrb_nil_value(); /* not reached */
1083}
1084
1085/* 15.2.10.5.6 */
1086/* 15.2.10.5.34 */
1087/*
1088 * call-seq:
1089 * str[fixnum] => fixnum or nil
1090 * str[fixnum, fixnum] => new_str or nil
1091 * str[range] => new_str or nil
1092 * str[regexp] => new_str or nil
1093 * str[regexp, fixnum] => new_str or nil
1094 * str[other_str] => new_str or nil
1095 * str.slice(fixnum) => fixnum or nil
1096 * str.slice(fixnum, fixnum) => new_str or nil
1097 * str.slice(range) => new_str or nil
1098 * str.slice(other_str) => new_str or nil
1099 *
1100 * Element Reference---If passed a single <code>Fixnum</code>, returns the code
1101 * of the character at that position. If passed two <code>Fixnum</code>
1102 * objects, returns a substring starting at the offset given by the first, and
1103 * a length given by the second. If given a range, a substring containing
1104 * characters at offsets given by the range is returned. In all three cases, if
1105 * an offset is negative, it is counted from the end of <i>str</i>. Returns
1106 * <code>nil</code> if the initial offset falls outside the string, the length
1107 * is negative, or the beginning of the range is greater than the end.
1108 *
1109 * If a <code>String</code> is given, that string is returned if it occurs in
1110 * <i>str</i>. In both cases, <code>nil</code> is returned if there is no
1111 * match.
1112 *
1113 * a = "hello there"
1114 * a[1] #=> 101(1.8.7) "e"(1.9.2)
1115 * a[1.1] #=> "e"(1.9.2)
1116 * a[1,3] #=> "ell"
1117 * a[1..3] #=> "ell"
1118 * a[-3,2] #=> "er"
1119 * a[-4..-2] #=> "her"
1120 * a[12..-1] #=> nil
1121 * a[-2..-4] #=> ""
1122 * a["lo"] #=> "lo"
1123 * a["bye"] #=> nil
1124 */
1125static mrb_value
1126mrb_str_aref_m(mrb_state *mrb, mrb_value str)
1127{
1128 mrb_value a1, a2;
1129 int argc;
1130
1131 argc = mrb_get_args(mrb, "o|o", &a1, &a2);
1132 if (argc == 2) {
1133 mrb_regexp_check(mrb, a1);
1134 return str_substr(mrb, str, mrb_fixnum(a1), mrb_fixnum(a2));
1135 }
1136 if (argc != 1) {
1137 mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1)", mrb_fixnum_value(argc));
1138 }
1139 return mrb_str_aref(mrb, str, a1);
1140}
1141
1142/* 15.2.10.5.8 */
1143/*
1144 * call-seq:
1145 * str.capitalize! => str or nil
1146 *
1147 * Modifies <i>str</i> by converting the first character to uppercase and the
1148 * remainder to lowercase. Returns <code>nil</code> if no changes are made.
1149 *
1150 * a = "hello"
1151 * a.capitalize! #=> "Hello"
1152 * a #=> "Hello"
1153 * a.capitalize! #=> nil
1154 */
1155static mrb_value
1156mrb_str_capitalize_bang(mrb_state *mrb, mrb_value str)
1157{
1158 char *p, *pend;
1159 mrb_bool modify = FALSE;
1160 struct RString *s = mrb_str_ptr(str);
1161
1162 mrb_str_modify(mrb, s);
1163 if (RSTR_LEN(s) == 0 || !RSTR_PTR(s)) return mrb_nil_value();
1164 p = RSTR_PTR(s); pend = RSTR_PTR(s) + RSTR_LEN(s);
1165 if (ISLOWER(*p)) {
1166 *p = TOUPPER(*p);
1167 modify = TRUE;
1168 }
1169 while (++p < pend) {
1170 if (ISUPPER(*p)) {
1171 *p = TOLOWER(*p);
1172 modify = TRUE;
1173 }
1174 }
1175 if (modify) return str;
1176 return mrb_nil_value();
1177}
1178
1179/* 15.2.10.5.7 */
1180/*
1181 * call-seq:
1182 * str.capitalize => new_str
1183 *
1184 * Returns a copy of <i>str</i> with the first character converted to uppercase
1185 * and the remainder to lowercase.
1186 *
1187 * "hello".capitalize #=> "Hello"
1188 * "HELLO".capitalize #=> "Hello"
1189 * "123ABC".capitalize #=> "123abc"
1190 */
1191static mrb_value
1192mrb_str_capitalize(mrb_state *mrb, mrb_value self)
1193{
1194 mrb_value str;
1195
1196 str = mrb_str_dup(mrb, self);
1197 mrb_str_capitalize_bang(mrb, str);
1198 return str;
1199}
1200
1201/* 15.2.10.5.10 */
1202/*
1203 * call-seq:
1204 * str.chomp!(separator="\n") => str or nil
1205 *
1206 * Modifies <i>str</i> in place as described for <code>String#chomp</code>,
1207 * returning <i>str</i>, or <code>nil</code> if no modifications were made.
1208 */
1209static mrb_value
1210mrb_str_chomp_bang(mrb_state *mrb, mrb_value str)
1211{
1212 mrb_value rs;
1213 mrb_int newline;
1214 char *p, *pp;
1215 mrb_int rslen;
1216 mrb_int len;
1217 struct RString *s = mrb_str_ptr(str);
1218
1219 mrb_str_modify(mrb, s);
1220 len = RSTR_LEN(s);
1221 if (mrb_get_args(mrb, "|S", &rs) == 0) {
1222 if (len == 0) return mrb_nil_value();
1223 smart_chomp:
1224 if (RSTR_PTR(s)[len-1] == '\n') {
1225 RSTR_SET_LEN(s, RSTR_LEN(s) - 1);
1226 if (RSTR_LEN(s) > 0 &&
1227 RSTR_PTR(s)[RSTR_LEN(s)-1] == '\r') {
1228 RSTR_SET_LEN(s, RSTR_LEN(s) - 1);
1229 }
1230 }
1231 else if (RSTR_PTR(s)[len-1] == '\r') {
1232 RSTR_SET_LEN(s, RSTR_LEN(s) - 1);
1233 }
1234 else {
1235 return mrb_nil_value();
1236 }
1237 RSTR_PTR(s)[RSTR_LEN(s)] = '\0';
1238 return str;
1239 }
1240
1241 if (len == 0 || mrb_nil_p(rs)) return mrb_nil_value();
1242 p = RSTR_PTR(s);
1243 rslen = RSTRING_LEN(rs);
1244 if (rslen == 0) {
1245 while (len>0 && p[len-1] == '\n') {
1246 len--;
1247 if (len>0 && p[len-1] == '\r')
1248 len--;
1249 }
1250 if (len < RSTR_LEN(s)) {
1251 RSTR_SET_LEN(s, len);
1252 p[len] = '\0';
1253 return str;
1254 }
1255 return mrb_nil_value();
1256 }
1257 if (rslen > len) return mrb_nil_value();
1258 newline = RSTRING_PTR(rs)[rslen-1];
1259 if (rslen == 1 && newline == '\n')
1260 newline = RSTRING_PTR(rs)[rslen-1];
1261 if (rslen == 1 && newline == '\n')
1262 goto smart_chomp;
1263
1264 pp = p + len - rslen;
1265 if (p[len-1] == newline &&
1266 (rslen <= 1 ||
1267 memcmp(RSTRING_PTR(rs), pp, rslen) == 0)) {
1268 RSTR_SET_LEN(s, len - rslen);
1269 p[RSTR_LEN(s)] = '\0';
1270 return str;
1271 }
1272 return mrb_nil_value();
1273}
1274
1275/* 15.2.10.5.9 */
1276/*
1277 * call-seq:
1278 * str.chomp(separator="\n") => new_str
1279 *
1280 * Returns a new <code>String</code> with the given record separator removed
1281 * from the end of <i>str</i> (if present). If <code>$/</code> has not been
1282 * changed from the default Ruby record separator, then <code>chomp</code> also
1283 * removes carriage return characters (that is it will remove <code>\n</code>,
1284 * <code>\r</code>, and <code>\r\n</code>).
1285 *
1286 * "hello".chomp #=> "hello"
1287 * "hello\n".chomp #=> "hello"
1288 * "hello\r\n".chomp #=> "hello"
1289 * "hello\n\r".chomp #=> "hello\n"
1290 * "hello\r".chomp #=> "hello"
1291 * "hello \n there".chomp #=> "hello \n there"
1292 * "hello".chomp("llo") #=> "he"
1293 */
1294static mrb_value
1295mrb_str_chomp(mrb_state *mrb, mrb_value self)
1296{
1297 mrb_value str;
1298
1299 str = mrb_str_dup(mrb, self);
1300 mrb_str_chomp_bang(mrb, str);
1301 return str;
1302}
1303
1304/* 15.2.10.5.12 */
1305/*
1306 * call-seq:
1307 * str.chop! => str or nil
1308 *
1309 * Processes <i>str</i> as for <code>String#chop</code>, returning <i>str</i>,
1310 * or <code>nil</code> if <i>str</i> is the empty string. See also
1311 * <code>String#chomp!</code>.
1312 */
1313static mrb_value
1314mrb_str_chop_bang(mrb_state *mrb, mrb_value str)
1315{
1316 struct RString *s = mrb_str_ptr(str);
1317
1318 mrb_str_modify(mrb, s);
1319 if (RSTR_LEN(s) > 0) {
1320 mrb_int len;
1321#ifdef MRB_UTF8_STRING
1322 const char* t = RSTR_PTR(s), *p = t;
1323 const char* e = p + RSTR_LEN(s);
1324 while (p<e) {
1325 mrb_int clen = utf8len(p, e);
1326 if (p + clen>=e) break;
1327 p += clen;
1328 }
1329 len = p - t;
1330#else
1331 len = RSTR_LEN(s) - 1;
1332#endif
1333 if (RSTR_PTR(s)[len] == '\n') {
1334 if (len > 0 &&
1335 RSTR_PTR(s)[len-1] == '\r') {
1336 len--;
1337 }
1338 }
1339 RSTR_SET_LEN(s, len);
1340 RSTR_PTR(s)[len] = '\0';
1341 return str;
1342 }
1343 return mrb_nil_value();
1344}
1345
1346/* 15.2.10.5.11 */
1347/*
1348 * call-seq:
1349 * str.chop => new_str
1350 *
1351 * Returns a new <code>String</code> with the last character removed. If the
1352 * string ends with <code>\r\n</code>, both characters are removed. Applying
1353 * <code>chop</code> to an empty string returns an empty
1354 * string. <code>String#chomp</code> is often a safer alternative, as it leaves
1355 * the string unchanged if it doesn't end in a record separator.
1356 *
1357 * "string\r\n".chop #=> "string"
1358 * "string\n\r".chop #=> "string\n"
1359 * "string\n".chop #=> "string"
1360 * "string".chop #=> "strin"
1361 * "x".chop #=> ""
1362 */
1363static mrb_value
1364mrb_str_chop(mrb_state *mrb, mrb_value self)
1365{
1366 mrb_value str;
1367 str = mrb_str_dup(mrb, self);
1368 mrb_str_chop_bang(mrb, str);
1369 return str;
1370}
1371
1372/* 15.2.10.5.14 */
1373/*
1374 * call-seq:
1375 * str.downcase! => str or nil
1376 *
1377 * Downcases the contents of <i>str</i>, returning <code>nil</code> if no
1378 * changes were made.
1379 */
1380static mrb_value
1381mrb_str_downcase_bang(mrb_state *mrb, mrb_value str)
1382{
1383 char *p, *pend;
1384 mrb_bool modify = FALSE;
1385 struct RString *s = mrb_str_ptr(str);
1386
1387 mrb_str_modify(mrb, s);
1388 p = RSTR_PTR(s);
1389 pend = RSTR_PTR(s) + RSTR_LEN(s);
1390 while (p < pend) {
1391 if (ISUPPER(*p)) {
1392 *p = TOLOWER(*p);
1393 modify = TRUE;
1394 }
1395 p++;
1396 }
1397
1398 if (modify) return str;
1399 return mrb_nil_value();
1400}
1401
1402/* 15.2.10.5.13 */
1403/*
1404 * call-seq:
1405 * str.downcase => new_str
1406 *
1407 * Returns a copy of <i>str</i> with all uppercase letters replaced with their
1408 * lowercase counterparts. The operation is locale insensitive---only
1409 * characters 'A' to 'Z' are affected.
1410 *
1411 * "hEllO".downcase #=> "hello"
1412 */
1413static mrb_value
1414mrb_str_downcase(mrb_state *mrb, mrb_value self)
1415{
1416 mrb_value str;
1417
1418 str = mrb_str_dup(mrb, self);
1419 mrb_str_downcase_bang(mrb, str);
1420 return str;
1421}
1422
1423/* 15.2.10.5.16 */
1424/*
1425 * call-seq:
1426 * str.empty? => true or false
1427 *
1428 * Returns <code>true</code> if <i>str</i> has a length of zero.
1429 *
1430 * "hello".empty? #=> false
1431 * "".empty? #=> true
1432 */
1433static mrb_value
1434mrb_str_empty_p(mrb_state *mrb, mrb_value self)
1435{
1436 struct RString *s = mrb_str_ptr(self);
1437
1438 return mrb_bool_value(RSTR_LEN(s) == 0);
1439}
1440
1441/* 15.2.10.5.17 */
1442/*
1443 * call-seq:
1444 * str.eql?(other) => true or false
1445 *
1446 * Two strings are equal if the have the same length and content.
1447 */
1448static mrb_value
1449mrb_str_eql(mrb_state *mrb, mrb_value self)
1450{
1451 mrb_value str2;
1452 mrb_bool eql_p;
1453
1454 mrb_get_args(mrb, "o", &str2);
1455 eql_p = (mrb_type(str2) == MRB_TT_STRING) && str_eql(mrb, self, str2);
1456
1457 return mrb_bool_value(eql_p);
1458}
1459
1460MRB_API mrb_value
1461mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
1462{
1463 return str_substr(mrb, str, beg, len);
1464}
1465
1466mrb_int
1467mrb_str_hash(mrb_state *mrb, mrb_value str)
1468{
1469 /* 1-8-7 */
1470 struct RString *s = mrb_str_ptr(str);
1471 mrb_int len = RSTR_LEN(s);
1472 char *p = RSTR_PTR(s);
1473 mrb_int key = 0;
1474
1475 while (len--) {
1476 key = key*65599 + *p;
1477 p++;
1478 }
1479 return key + (key>>5);
1480}
1481
1482/* 15.2.10.5.20 */
1483/*
1484 * call-seq:
1485 * str.hash => fixnum
1486 *
1487 * Return a hash based on the string's length and content.
1488 */
1489static mrb_value
1490mrb_str_hash_m(mrb_state *mrb, mrb_value self)
1491{
1492 mrb_int key = mrb_str_hash(mrb, self);
1493 return mrb_fixnum_value(key);
1494}
1495
1496/* 15.2.10.5.21 */
1497/*
1498 * call-seq:
1499 * str.include? other_str => true or false
1500 * str.include? fixnum => true or false
1501 *
1502 * Returns <code>true</code> if <i>str</i> contains the given string or
1503 * character.
1504 *
1505 * "hello".include? "lo" #=> true
1506 * "hello".include? "ol" #=> false
1507 * "hello".include? ?h #=> true
1508 */
1509static mrb_value
1510mrb_str_include(mrb_state *mrb, mrb_value self)
1511{
1512 mrb_int i;
1513 mrb_value str2;
1514 mrb_bool include_p;
1515
1516 mrb_get_args(mrb, "o", &str2);
1517 if (mrb_fixnum_p(str2)) {
1518 include_p = (memchr(RSTRING_PTR(self), mrb_fixnum(str2), RSTRING_LEN(self)) != NULL);
1519 }
1520 else {
1521 str2 = mrb_str_to_str(mrb, str2);
1522 i = str_index(mrb, self, str2, 0);
1523
1524 include_p = (i != -1);
1525 }
1526
1527 return mrb_bool_value(include_p);
1528}
1529
1530/* 15.2.10.5.22 */
1531/*
1532 * call-seq:
1533 * str.index(substring [, offset]) => fixnum or nil
1534 * str.index(fixnum [, offset]) => fixnum or nil
1535 * str.index(regexp [, offset]) => fixnum or nil
1536 *
1537 * Returns the index of the first occurrence of the given
1538 * <i>substring</i>,
1539 * character (<i>fixnum</i>), or pattern (<i>regexp</i>) in <i>str</i>.
1540 * Returns
1541 * <code>nil</code> if not found.
1542 * If the second parameter is present, it
1543 * specifies the position in the string to begin the search.
1544 *
1545 * "hello".index('e') #=> 1
1546 * "hello".index('lo') #=> 3
1547 * "hello".index('a') #=> nil
1548 * "hello".index(101) #=> 1(101=0x65='e')
1549 * "hello".index(/[aeiou]/, -3) #=> 4
1550 */
1551static mrb_value
1552mrb_str_index(mrb_state *mrb, mrb_value str)
1553{
1554 mrb_value *argv;
1555 mrb_int argc;
1556 mrb_value sub;
1557 mrb_int pos, clen;
1558
1559 mrb_get_args(mrb, "*", &argv, &argc);
1560 if (argc == 2) {
1561 pos = mrb_fixnum(argv[1]);
1562 sub = argv[0];
1563 }
1564 else {
1565 pos = 0;
1566 if (argc > 0)
1567 sub = argv[0];
1568 else
1569 sub = mrb_nil_value();
1570 }
1571 mrb_regexp_check(mrb, sub);
1572 clen = RSTRING_CHAR_LEN(str);
1573 if (pos < 0) {
1574 pos += clen;
1575 if (pos < 0) {
1576 return mrb_nil_value();
1577 }
1578 }
1579 if (pos >= clen) return mrb_nil_value();
1580 pos = chars2bytes(str, 0, pos);
1581
1582 switch (mrb_type(sub)) {
1583 default: {
1584 mrb_value tmp;
1585
1586 tmp = mrb_check_string_type(mrb, sub);
1587 if (mrb_nil_p(tmp)) {
1588 mrb_raisef(mrb, E_TYPE_ERROR, "type mismatch: %S given", sub);
1589 }
1590 sub = tmp;
1591 }
1592 /* fall through */
1593 case MRB_TT_STRING:
1594 pos = str_index(mrb, str, sub, pos);
1595 break;
1596 }
1597
1598 if (pos == -1) return mrb_nil_value();
1599 pos = bytes2chars(RSTRING_PTR(str), pos);
1600 return mrb_fixnum_value(pos);
1601}
1602
1603#define STR_REPLACE_SHARED_MIN 10
1604
1605/* 15.2.10.5.24 */
1606/* 15.2.10.5.28 */
1607/*
1608 * call-seq:
1609 * str.replace(other_str) => str
1610 *
1611 * s = "hello" #=> "hello"
1612 * s.replace "world" #=> "world"
1613 */
1614static mrb_value
1615mrb_str_replace(mrb_state *mrb, mrb_value str)
1616{
1617 mrb_value str2;
1618
1619 mrb_get_args(mrb, "S", &str2);
1620 return str_replace(mrb, mrb_str_ptr(str), mrb_str_ptr(str2));
1621}
1622
1623/* 15.2.10.5.23 */
1624/*
1625 * call-seq:
1626 * String.new(str="") => new_str
1627 *
1628 * Returns a new string object containing a copy of <i>str</i>.
1629 */
1630static mrb_value
1631mrb_str_init(mrb_state *mrb, mrb_value self)
1632{
1633 mrb_value str2;
1634
1635 if (mrb_get_args(mrb, "|S", &str2) == 1) {
1636 str_replace(mrb, mrb_str_ptr(self), mrb_str_ptr(str2));
1637 }
1638 return self;
1639}
1640
1641/* 15.2.10.5.25 */
1642/* 15.2.10.5.41 */
1643/*
1644 * call-seq:
1645 * str.intern => symbol
1646 * str.to_sym => symbol
1647 *
1648 * Returns the <code>Symbol</code> corresponding to <i>str</i>, creating the
1649 * symbol if it did not previously exist. See <code>Symbol#id2name</code>.
1650 *
1651 * "Koala".intern #=> :Koala
1652 * s = 'cat'.to_sym #=> :cat
1653 * s == :cat #=> true
1654 * s = '@cat'.to_sym #=> :@cat
1655 * s == :@cat #=> true
1656 *
1657 * This can also be used to create symbols that cannot be represented using the
1658 * <code>:xxx</code> notation.
1659 *
1660 * 'cat and dog'.to_sym #=> :"cat and dog"
1661 */
1662MRB_API mrb_value
1663mrb_str_intern(mrb_state *mrb, mrb_value self)
1664{
1665 return mrb_symbol_value(mrb_intern_str(mrb, self));
1666}
1667/* ---------------------------------- */
1668MRB_API mrb_value
1669mrb_obj_as_string(mrb_state *mrb, mrb_value obj)
1670{
1671 mrb_value str;
1672
1673 if (mrb_string_p(obj)) {
1674 return obj;
1675 }
1676 str = mrb_funcall(mrb, obj, "to_s", 0);
1677 if (!mrb_string_p(str))
1678 return mrb_any_to_s(mrb, obj);
1679 return str;
1680}
1681
1682MRB_API mrb_value
1683mrb_ptr_to_str(mrb_state *mrb, void *p)
1684{
1685 struct RString *p_str;
1686 char *p1;
1687 char *p2;
1688 uintptr_t n = (uintptr_t)p;
1689
1690 p_str = str_new(mrb, NULL, 2 + sizeof(uintptr_t) * CHAR_BIT / 4);
1691 p1 = RSTR_PTR(p_str);
1692 *p1++ = '0';
1693 *p1++ = 'x';
1694 p2 = p1;
1695
1696 do {
1697 *p2++ = mrb_digitmap[n % 16];
1698 n /= 16;
1699 } while (n > 0);
1700 *p2 = '\0';
1701 RSTR_SET_LEN(p_str, (mrb_int)(p2 - RSTR_PTR(p_str)));
1702
1703 while (p1 < p2) {
1704 const char c = *p1;
1705 *p1++ = *--p2;
1706 *p2 = c;
1707 }
1708
1709 return mrb_obj_value(p_str);
1710}
1711
1712MRB_API mrb_value
1713mrb_string_type(mrb_state *mrb, mrb_value str)
1714{
1715 return mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str");
1716}
1717
1718MRB_API mrb_value
1719mrb_check_string_type(mrb_state *mrb, mrb_value str)
1720{
1721 return mrb_check_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str");
1722}
1723
1724/* 15.2.10.5.30 */
1725/*
1726 * call-seq:
1727 * str.reverse! => str
1728 *
1729 * Reverses <i>str</i> in place.
1730 */
1731static mrb_value
1732mrb_str_reverse_bang(mrb_state *mrb, mrb_value str)
1733{
1734#ifdef MRB_UTF8_STRING
1735 mrb_int utf8_len = RSTRING_CHAR_LEN(str);
1736 mrb_int len = RSTRING_LEN(str);
1737
1738 if (utf8_len == len) goto bytes;
1739 if (utf8_len > 1) {
1740 char *buf;
1741 char *p, *e, *r;
1742
1743 mrb_str_modify(mrb, mrb_str_ptr(str));
1744 len = RSTRING_LEN(str);
1745 buf = mrb_malloc(mrb, (size_t)len);
1746 p = buf;
1747 e = buf + len;
1748
1749 memcpy(buf, RSTRING_PTR(str), len);
1750 r = RSTRING_PTR(str) + len;
1751
1752 while (p<e) {
1753 mrb_int clen = utf8len(p, e);
1754 r -= clen;
1755 memcpy(r, p, clen);
1756 p += clen;
1757 }
1758 mrb_free(mrb, buf);
1759 }
1760 return str;
1761
1762 bytes:
1763#endif
1764 {
1765 struct RString *s = mrb_str_ptr(str);
1766 char *p, *e;
1767 char c;
1768
1769 mrb_str_modify(mrb, s);
1770 if (RSTR_LEN(s) > 1) {
1771 p = RSTR_PTR(s);
1772 e = p + RSTR_LEN(s) - 1;
1773 while (p < e) {
1774 c = *p;
1775 *p++ = *e;
1776 *e-- = c;
1777 }
1778 }
1779 return str;
1780 }
1781}
1782
1783/* ---------------------------------- */
1784/* 15.2.10.5.29 */
1785/*
1786 * call-seq:
1787 * str.reverse => new_str
1788 *
1789 * Returns a new string with the characters from <i>str</i> in reverse order.
1790 *
1791 * "stressed".reverse #=> "desserts"
1792 */
1793static mrb_value
1794mrb_str_reverse(mrb_state *mrb, mrb_value str)
1795{
1796 mrb_value str2 = mrb_str_dup(mrb, str);
1797 mrb_str_reverse_bang(mrb, str2);
1798 return str2;
1799}
1800
1801/* 15.2.10.5.31 */
1802/*
1803 * call-seq:
1804 * str.rindex(substring [, fixnum]) => fixnum or nil
1805 * str.rindex(fixnum [, fixnum]) => fixnum or nil
1806 * str.rindex(regexp [, fixnum]) => fixnum or nil
1807 *
1808 * Returns the index of the last occurrence of the given <i>substring</i>,
1809 * character (<i>fixnum</i>), or pattern (<i>regexp</i>) in <i>str</i>. Returns
1810 * <code>nil</code> if not found. If the second parameter is present, it
1811 * specifies the position in the string to end the search---characters beyond
1812 * this point will not be considered.
1813 *
1814 * "hello".rindex('e') #=> 1
1815 * "hello".rindex('l') #=> 3
1816 * "hello".rindex('a') #=> nil
1817 * "hello".rindex(101) #=> 1
1818 * "hello".rindex(/[aeiou]/, -2) #=> 1
1819 */
1820static mrb_value
1821mrb_str_rindex(mrb_state *mrb, mrb_value str)
1822{
1823 mrb_value *argv;
1824 mrb_int argc;
1825 mrb_value sub;
1826 mrb_value vpos;
1827 mrb_int pos, len = RSTRING_CHAR_LEN(str);
1828
1829 mrb_get_args(mrb, "*", &argv, &argc);
1830 if (argc == 2) {
1831 sub = argv[0];
1832 vpos = argv[1];
1833 pos = mrb_fixnum(vpos);
1834 if (pos < 0) {
1835 pos += len;
1836 if (pos < 0) {
1837 mrb_regexp_check(mrb, sub);
1838 return mrb_nil_value();
1839 }
1840 }
1841 if (pos > len) pos = len;
1842 }
1843 else {
1844 pos = len;
1845 if (argc > 0)
1846 sub = argv[0];
1847 else
1848 sub = mrb_nil_value();
1849 }
1850 pos = chars2bytes(str, 0, pos);
1851 len = chars2bytes(str, pos, len);
1852 mrb_regexp_check(mrb, sub);
1853
1854 switch (mrb_type(sub)) {
1855 default: {
1856 mrb_value tmp;
1857
1858 tmp = mrb_check_string_type(mrb, sub);
1859 if (mrb_nil_p(tmp)) {
1860 mrb_raisef(mrb, E_TYPE_ERROR, "type mismatch: %S given", sub);
1861 }
1862 sub = tmp;
1863 }
1864 /* fall through */
1865 case MRB_TT_STRING:
1866 pos = str_rindex(mrb, str, sub, pos);
1867 if (pos >= 0) {
1868 pos = bytes2chars(RSTRING_PTR(str), pos);
1869 return mrb_fixnum_value(pos);
1870 }
1871 break;
1872
1873 } /* end of switch (TYPE(sub)) */
1874 return mrb_nil_value();
1875}
1876
1877/* 15.2.10.5.35 */
1878
1879/*
1880 * call-seq:
1881 * str.split(pattern="\n", [limit]) => anArray
1882 *
1883 * Divides <i>str</i> into substrings based on a delimiter, returning an array
1884 * of these substrings.
1885 *
1886 * If <i>pattern</i> is a <code>String</code>, then its contents are used as
1887 * the delimiter when splitting <i>str</i>. If <i>pattern</i> is a single
1888 * space, <i>str</i> is split on whitespace, with leading whitespace and runs
1889 * of contiguous whitespace characters ignored.
1890 *
1891 * If <i>pattern</i> is a <code>Regexp</code>, <i>str</i> is divided where the
1892 * pattern matches. Whenever the pattern matches a zero-length string,
1893 * <i>str</i> is split into individual characters.
1894 *
1895 * If <i>pattern</i> is omitted, the value of <code>$;</code> is used. If
1896 * <code>$;</code> is <code>nil</code> (which is the default), <i>str</i> is
1897 * split on whitespace as if ' ' were specified.
1898 *
1899 * If the <i>limit</i> parameter is omitted, trailing null fields are
1900 * suppressed. If <i>limit</i> is a positive number, at most that number of
1901 * fields will be returned (if <i>limit</i> is <code>1</code>, the entire
1902 * string is returned as the only entry in an array). If negative, there is no
1903 * limit to the number of fields returned, and trailing null fields are not
1904 * suppressed.
1905 *
1906 * " now's the time".split #=> ["now's", "the", "time"]
1907 * " now's the time".split(' ') #=> ["now's", "the", "time"]
1908 * " now's the time".split(/ /) #=> ["", "now's", "", "the", "time"]
1909 * "hello".split(//) #=> ["h", "e", "l", "l", "o"]
1910 * "hello".split(//, 3) #=> ["h", "e", "llo"]
1911 *
1912 * "mellow yellow".split("ello") #=> ["m", "w y", "w"]
1913 * "1,2,,3,4,,".split(',') #=> ["1", "2", "", "3", "4"]
1914 * "1,2,,3,4,,".split(',', 4) #=> ["1", "2", "", "3,4,,"]
1915 * "1,2,,3,4,,".split(',', -4) #=> ["1", "2", "", "3", "4", "", ""]
1916 */
1917
1918static mrb_value
1919mrb_str_split_m(mrb_state *mrb, mrb_value str)
1920{
1921 int argc;
1922 mrb_value spat = mrb_nil_value();
1923 enum {awk, string, regexp} split_type = string;
1924 long i = 0, lim_p;
1925 mrb_int beg;
1926 mrb_int end;
1927 mrb_int lim = 0;
1928 mrb_value result, tmp;
1929
1930 argc = mrb_get_args(mrb, "|oi", &spat, &lim);
1931 lim_p = (lim > 0 && argc == 2);
1932 if (argc == 2) {
1933 if (lim == 1) {
1934 if (RSTRING_LEN(str) == 0)
1935 return mrb_ary_new_capa(mrb, 0);
1936 return mrb_ary_new_from_values(mrb, 1, &str);
1937 }
1938 i = 1;
1939 }
1940
1941 if (argc == 0 || mrb_nil_p(spat)) {
1942 split_type = awk;
1943 }
1944 else {
1945 if (mrb_string_p(spat)) {
1946 split_type = string;
1947 if (RSTRING_LEN(spat) == 1 && RSTRING_PTR(spat)[0] == ' ') {
1948 split_type = awk;
1949 }
1950 }
1951 else {
1952 mrb_noregexp(mrb, str);
1953 }
1954 }
1955
1956 result = mrb_ary_new(mrb);
1957 beg = 0;
1958 if (split_type == awk) {
1959 mrb_bool skip = TRUE;
1960 mrb_int idx = 0;
1961 mrb_int str_len = RSTRING_LEN(str);
1962 unsigned int c;
1963 int ai = mrb_gc_arena_save(mrb);
1964
1965 idx = end = beg;
1966 while (idx < str_len) {
1967 c = (unsigned char)RSTRING_PTR(str)[idx++];
1968 if (skip) {
1969 if (ISSPACE(c)) {
1970 beg = idx;
1971 }
1972 else {
1973 end = idx;
1974 skip = FALSE;
1975 if (lim_p && lim <= i) break;
1976 }
1977 }
1978 else if (ISSPACE(c)) {
1979 mrb_ary_push(mrb, result, byte_subseq(mrb, str, beg, end-beg));
1980 mrb_gc_arena_restore(mrb, ai);
1981 skip = TRUE;
1982 beg = idx;
1983 if (lim_p) ++i;
1984 }
1985 else {
1986 end = idx;
1987 }
1988 }
1989 }
1990 else if (split_type == string) {
1991 mrb_int str_len = RSTRING_LEN(str);
1992 mrb_int pat_len = RSTRING_LEN(spat);
1993 mrb_int idx = 0;
1994 int ai = mrb_gc_arena_save(mrb);
1995
1996 while (idx < str_len) {
1997 if (pat_len > 0) {
1998 end = mrb_memsearch(RSTRING_PTR(spat), pat_len, RSTRING_PTR(str)+idx, str_len - idx);
1999 if (end < 0) break;
2000 } else {
2001 end = chars2bytes(str, idx, 1);
2002 }
2003 mrb_ary_push(mrb, result, byte_subseq(mrb, str, idx, end));
2004 mrb_gc_arena_restore(mrb, ai);
2005 idx += end + pat_len;
2006 if (lim_p && lim <= ++i) break;
2007 }
2008 beg = idx;
2009 }
2010 else {
2011 mrb_noregexp(mrb, str);
2012 }
2013 if (RSTRING_LEN(str) > 0 && (lim_p || RSTRING_LEN(str) > beg || lim < 0)) {
2014 if (RSTRING_LEN(str) == beg) {
2015 tmp = mrb_str_new_empty(mrb, str);
2016 }
2017 else {
2018 tmp = byte_subseq(mrb, str, beg, RSTRING_LEN(str)-beg);
2019 }
2020 mrb_ary_push(mrb, result, tmp);
2021 }
2022 if (!lim_p && lim == 0) {
2023 mrb_int len;
2024 while ((len = RARRAY_LEN(result)) > 0 &&
2025 (tmp = RARRAY_PTR(result)[len-1], RSTRING_LEN(tmp) == 0))
2026 mrb_ary_pop(mrb, result);
2027 }
2028
2029 return result;
2030}
2031
2032MRB_API mrb_value
2033mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
2034{
2035 const char *p;
2036 char sign = 1;
2037 int c, uscore;
2038 uint64_t n = 0;
2039 mrb_int val;
2040
2041#define conv_digit(c) \
2042 (ISDIGIT(c) ? ((c) - '0') : \
2043 ISLOWER(c) ? ((c) - 'a' + 10) : \
2044 ISUPPER(c) ? ((c) - 'A' + 10) : \
2045 -1)
2046
2047 if (!str) {
2048 if (badcheck) goto bad;
2049 return mrb_fixnum_value(0);
2050 }
2051 while (ISSPACE(*str)) str++;
2052
2053 if (str[0] == '+') {
2054 str++;
2055 }
2056 else if (str[0] == '-') {
2057 str++;
2058 sign = 0;
2059 }
2060 if (str[0] == '+' || str[0] == '-') {
2061 if (badcheck) goto bad;
2062 return mrb_fixnum_value(0);
2063 }
2064 if (base <= 0) {
2065 if (str[0] == '0') {
2066 switch (str[1]) {
2067 case 'x': case 'X':
2068 base = 16;
2069 break;
2070 case 'b': case 'B':
2071 base = 2;
2072 break;
2073 case 'o': case 'O':
2074 base = 8;
2075 break;
2076 case 'd': case 'D':
2077 base = 10;
2078 break;
2079 default:
2080 base = 8;
2081 }
2082 }
2083 else if (base < -1) {
2084 base = -base;
2085 }
2086 else {
2087 base = 10;
2088 }
2089 }
2090 switch (base) {
2091 case 2:
2092 if (str[0] == '0' && (str[1] == 'b'||str[1] == 'B')) {
2093 str += 2;
2094 }
2095 break;
2096 case 3:
2097 break;
2098 case 8:
2099 if (str[0] == '0' && (str[1] == 'o'||str[1] == 'O')) {
2100 str += 2;
2101 }
2102 case 4: case 5: case 6: case 7:
2103 break;
2104 case 10:
2105 if (str[0] == '0' && (str[1] == 'd'||str[1] == 'D')) {
2106 str += 2;
2107 }
2108 case 9: case 11: case 12: case 13: case 14: case 15:
2109 break;
2110 case 16:
2111 if (str[0] == '0' && (str[1] == 'x'||str[1] == 'X')) {
2112 str += 2;
2113 }
2114 break;
2115 default:
2116 if (base < 2 || 36 < base) {
2117 mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %S", mrb_fixnum_value(base));
2118 }
2119 break;
2120 } /* end of switch (base) { */
2121 if (*str == '0') { /* squeeze preceding 0s */
2122 uscore = 0;
2123 while ((c = *++str) == '0' || c == '_') {
2124 if (c == '_') {
2125 if (++uscore >= 2)
2126 break;
2127 }
2128 else
2129 uscore = 0;
2130 }
2131 if (!(c = *str) || ISSPACE(c)) --str;
2132 }
2133 c = *str;
2134 c = conv_digit(c);
2135 if (c < 0 || c >= base) {
2136 if (badcheck) goto bad;
2137 return mrb_fixnum_value(0);
2138 }
2139
2140 uscore = 0;
2141 for (p=str;*p;p++) {
2142 if (*p == '_') {
2143 if (uscore == 0) {
2144 uscore++;
2145 continue;
2146 }
2147 if (badcheck) goto bad;
2148 break;
2149 }
2150 uscore = 0;
2151 c = conv_digit(*p);
2152 if (c < 0 || c >= base) {
2153 if (badcheck) goto bad;
2154 break;
2155 }
2156 n *= base;
2157 n += c;
2158 if (n > MRB_INT_MAX) {
2159 mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer", mrb_str_new_cstr(mrb, str));
2160 }
2161 }
2162 val = n;
2163 if (badcheck) {
2164 if (p == str) goto bad; /* no number */
2165 while (*p && ISSPACE(*p)) p++;
2166 if (*p) goto bad; /* trailing garbage */
2167 }
2168
2169 return mrb_fixnum_value(sign ? val : -val);
2170bad:
2171 mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for number(%S)", mrb_str_new_cstr(mrb, str));
2172 /* not reached */
2173 return mrb_fixnum_value(0);
2174}
2175
2176MRB_API const char*
2177mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr)
2178{
2179 mrb_value str = mrb_str_to_str(mrb, *ptr);
2180 struct RString *ps = mrb_str_ptr(str);
2181 mrb_int len = mrb_str_strlen(mrb, ps);
2182 char *p = RSTR_PTR(ps);
2183
2184 if (!p || p[len] != '\0') {
2185 mrb_str_modify(mrb, ps);
2186 return RSTR_PTR(ps);
2187 }
2188 return p;
2189}
2190
2191MRB_API mrb_value
2192mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck)
2193{
2194 const char *s;
2195 mrb_int len;
2196
2197 if (badcheck) {
2198 /* Raises if the string contains a null character (the badcheck) */
2199 s = mrb_string_value_cstr(mrb, &str);
2200 }
2201 else {
2202 s = mrb_string_value_ptr(mrb, str);
2203 }
2204 if (s) {
2205 len = RSTRING_LEN(str);
2206 if (s[len]) { /* no sentinel somehow */
2207 struct RString *temp_str = str_new(mrb, s, len);
2208 s = RSTR_PTR(temp_str);
2209 }
2210 }
2211 return mrb_cstr_to_inum(mrb, s, base, badcheck);
2212}
2213
2214/* 15.2.10.5.38 */
2215/*
2216 * call-seq:
2217 * str.to_i(base=10) => integer
2218 *
2219 * Returns the result of interpreting leading characters in <i>str</i> as an
2220 * integer base <i>base</i> (between 2 and 36). Extraneous characters past the
2221 * end of a valid number are ignored. If there is not a valid number at the
2222 * start of <i>str</i>, <code>0</code> is returned. This method never raises an
2223 * exception.
2224 *
2225 * "12345".to_i #=> 12345
2226 * "99 red balloons".to_i #=> 99
2227 * "0a".to_i #=> 0
2228 * "0a".to_i(16) #=> 10
2229 * "hello".to_i #=> 0
2230 * "1100101".to_i(2) #=> 101
2231 * "1100101".to_i(8) #=> 294977
2232 * "1100101".to_i(10) #=> 1100101
2233 * "1100101".to_i(16) #=> 17826049
2234 */
2235static mrb_value
2236mrb_str_to_i(mrb_state *mrb, mrb_value self)
2237{
2238 mrb_int base = 10;
2239
2240 mrb_get_args(mrb, "|i", &base);
2241 if (base < 0) {
2242 mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %S", mrb_fixnum_value(base));
2243 }
2244 return mrb_str_to_inum(mrb, self, base, FALSE);
2245}
2246
2247MRB_API double
2248mrb_cstr_to_dbl(mrb_state *mrb, const char * p, mrb_bool badcheck)
2249{
2250 char *end;
2251 char buf[DBL_DIG * 4 + 10];
2252 double d;
2253
2254 enum {max_width = 20};
2255
2256 if (!p) return 0.0;
2257 while (ISSPACE(*p)) p++;
2258
2259 if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
2260 return 0.0;
2261 }
2262 d = strtod(p, &end);
2263 if (p == end) {
2264 if (badcheck) {
2265bad:
2266 mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for float(%S)", mrb_str_new_cstr(mrb, p));
2267 /* not reached */
2268 }
2269 return d;
2270 }
2271 if (*end) {
2272 char *n = buf;
2273 char *e = buf + sizeof(buf) - 1;
2274 char prev = 0;
2275
2276 while (p < end && n < e) prev = *n++ = *p++;
2277 while (*p) {
2278 if (*p == '_') {
2279 /* remove underscores between digits */
2280 if (badcheck) {
2281 if (n == buf || !ISDIGIT(prev)) goto bad;
2282 ++p;
2283 if (!ISDIGIT(*p)) goto bad;
2284 }
2285 else {
2286 while (*++p == '_');
2287 continue;
2288 }
2289 }
2290 prev = *p++;
2291 if (n < e) *n++ = prev;
2292 }
2293 *n = '\0';
2294 p = buf;
2295
2296 if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
2297 return 0.0;
2298 }
2299
2300 d = strtod(p, &end);
2301 if (badcheck) {
2302 if (!end || p == end) goto bad;
2303 while (*end && ISSPACE(*end)) end++;
2304 if (*end) goto bad;
2305 }
2306 }
2307 return d;
2308}
2309
2310MRB_API double
2311mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck)
2312{
2313 char *s;
2314 mrb_int len;
2315
2316 str = mrb_str_to_str(mrb, str);
2317 s = RSTRING_PTR(str);
2318 len = RSTRING_LEN(str);
2319 if (s) {
2320 if (badcheck && memchr(s, '\0', len)) {
2321 mrb_raise(mrb, E_ARGUMENT_ERROR, "string for Float contains null byte");
2322 }
2323 if (s[len]) { /* no sentinel somehow */
2324 struct RString *temp_str = str_new(mrb, s, len);
2325 s = RSTR_PTR(temp_str);
2326 }
2327 }
2328 return mrb_cstr_to_dbl(mrb, s, badcheck);
2329}
2330
2331/* 15.2.10.5.39 */
2332/*
2333 * call-seq:
2334 * str.to_f => float
2335 *
2336 * Returns the result of interpreting leading characters in <i>str</i> as a
2337 * floating point number. Extraneous characters past the end of a valid number
2338 * are ignored. If there is not a valid number at the start of <i>str</i>,
2339 * <code>0.0</code> is returned. This method never raises an exception.
2340 *
2341 * "123.45e1".to_f #=> 1234.5
2342 * "45.67 degrees".to_f #=> 45.67
2343 * "thx1138".to_f #=> 0.0
2344 */
2345static mrb_value
2346mrb_str_to_f(mrb_state *mrb, mrb_value self)
2347{
2348 return mrb_float_value(mrb, mrb_str_to_dbl(mrb, self, FALSE));
2349}
2350
2351/* 15.2.10.5.40 */
2352/*
2353 * call-seq:
2354 * str.to_s => str
2355 * str.to_str => str
2356 *
2357 * Returns the receiver.
2358 */
2359static mrb_value
2360mrb_str_to_s(mrb_state *mrb, mrb_value self)
2361{
2362 if (mrb_obj_class(mrb, self) != mrb->string_class) {
2363 return mrb_str_dup(mrb, self);
2364 }
2365 return self;
2366}
2367
2368/* 15.2.10.5.43 */
2369/*
2370 * call-seq:
2371 * str.upcase! => str or nil
2372 *
2373 * Upcases the contents of <i>str</i>, returning <code>nil</code> if no changes
2374 * were made.
2375 */
2376static mrb_value
2377mrb_str_upcase_bang(mrb_state *mrb, mrb_value str)
2378{
2379 struct RString *s = mrb_str_ptr(str);
2380 char *p, *pend;
2381 mrb_bool modify = FALSE;
2382
2383 mrb_str_modify(mrb, s);
2384 p = RSTRING_PTR(str);
2385 pend = RSTRING_END(str);
2386 while (p < pend) {
2387 if (ISLOWER(*p)) {
2388 *p = TOUPPER(*p);
2389 modify = TRUE;
2390 }
2391 p++;
2392 }
2393
2394 if (modify) return str;
2395 return mrb_nil_value();
2396}
2397
2398/* 15.2.10.5.42 */
2399/*
2400 * call-seq:
2401 * str.upcase => new_str
2402 *
2403 * Returns a copy of <i>str</i> with all lowercase letters replaced with their
2404 * uppercase counterparts. The operation is locale insensitive---only
2405 * characters 'a' to 'z' are affected.
2406 *
2407 * "hEllO".upcase #=> "HELLO"
2408 */
2409static mrb_value
2410mrb_str_upcase(mrb_state *mrb, mrb_value self)
2411{
2412 mrb_value str;
2413
2414 str = mrb_str_dup(mrb, self);
2415 mrb_str_upcase_bang(mrb, str);
2416 return str;
2417}
2418
2419#define IS_EVSTR(p,e) ((p) < (e) && (*(p) == '$' || *(p) == '@' || *(p) == '{'))
2420
2421/*
2422 * call-seq:
2423 * str.dump -> new_str
2424 *
2425 * Produces a version of <i>str</i> with all nonprinting characters replaced by
2426 * <code>\nnn</code> notation and all special characters escaped.
2427 */
2428mrb_value
2429mrb_str_dump(mrb_state *mrb, mrb_value str)
2430{
2431 mrb_int len;
2432 const char *p, *pend;
2433 char *q;
2434 struct RString *result;
2435
2436 len = 2; /* "" */
2437 p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str);
2438 while (p < pend) {
2439 unsigned char c = *p++;
2440 switch (c) {
2441 case '"': case '\\':
2442 case '\n': case '\r':
2443 case '\t': case '\f':
2444 case '\013': case '\010': case '\007': case '\033':
2445 len += 2;
2446 break;
2447
2448 case '#':
2449 len += IS_EVSTR(p, pend) ? 2 : 1;
2450 break;
2451
2452 default:
2453 if (ISPRINT(c)) {
2454 len++;
2455 }
2456 else {
2457 len += 4; /* \NNN */
2458 }
2459 break;
2460 }
2461 }
2462
2463 result = str_new(mrb, 0, len);
2464 str_with_class(mrb, result, str);
2465 p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str);
2466 q = RSTR_PTR(result);
2467 *q++ = '"';
2468 while (p < pend) {
2469 unsigned char c = *p++;
2470
2471 switch (c) {
2472 case '"':
2473 case '\\':
2474 *q++ = '\\';
2475 *q++ = c;
2476 break;
2477
2478 case '\n':
2479 *q++ = '\\';
2480 *q++ = 'n';
2481 break;
2482
2483 case '\r':
2484 *q++ = '\\';
2485 *q++ = 'r';
2486 break;
2487
2488 case '\t':
2489 *q++ = '\\';
2490 *q++ = 't';
2491 break;
2492
2493 case '\f':
2494 *q++ = '\\';
2495 *q++ = 'f';
2496 break;
2497
2498 case '\013':
2499 *q++ = '\\';
2500 *q++ = 'v';
2501 break;
2502
2503 case '\010':
2504 *q++ = '\\';
2505 *q++ = 'b';
2506 break;
2507
2508 case '\007':
2509 *q++ = '\\';
2510 *q++ = 'a';
2511 break;
2512
2513 case '\033':
2514 *q++ = '\\';
2515 *q++ = 'e';
2516 break;
2517
2518 case '#':
2519 if (IS_EVSTR(p, pend)) *q++ = '\\';
2520 *q++ = '#';
2521 break;
2522
2523 default:
2524 if (ISPRINT(c)) {
2525 *q++ = c;
2526 }
2527 else {
2528 *q++ = '\\';
2529 q[2] = '0' + c % 8; c /= 8;
2530 q[1] = '0' + c % 8; c /= 8;
2531 q[0] = '0' + c % 8;
2532 q += 3;
2533 }
2534 }
2535 }
2536 *q = '"';
2537 return mrb_obj_value(result);
2538}
2539
2540MRB_API mrb_value
2541mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len)
2542{
2543 str_buf_cat(mrb, mrb_str_ptr(str), ptr, len);
2544 return str;
2545}
2546
2547MRB_API mrb_value
2548mrb_str_cat_cstr(mrb_state *mrb, mrb_value str, const char *ptr)
2549{
2550 return mrb_str_cat(mrb, str, ptr, strlen(ptr));
2551}
2552
2553MRB_API mrb_value
2554mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2)
2555{
2556 return mrb_str_cat(mrb, str, RSTRING_PTR(str2), RSTRING_LEN(str2));
2557}
2558
2559MRB_API mrb_value
2560mrb_str_append(mrb_state *mrb, mrb_value str1, mrb_value str2)
2561{
2562 str2 = mrb_str_to_str(mrb, str2);
2563 return mrb_str_cat_str(mrb, str1, str2);
2564}
2565
2566#define CHAR_ESC_LEN 13 /* sizeof(\x{ hex of 32bit unsigned int } \0) */
2567
2568/*
2569 * call-seq:
2570 * str.inspect -> string
2571 *
2572 * Returns a printable version of _str_, surrounded by quote marks,
2573 * with special characters escaped.
2574 *
2575 * str = "hello"
2576 * str[3] = "\b"
2577 * str.inspect #=> "\"hel\\bo\""
2578 */
2579mrb_value
2580mrb_str_inspect(mrb_state *mrb, mrb_value str)
2581{
2582 const char *p, *pend;
2583 char buf[CHAR_ESC_LEN + 1];
2584 mrb_value result = mrb_str_new_lit(mrb, "\"");
2585
2586 p = RSTRING_PTR(str); pend = RSTRING_END(str);
2587 for (;p < pend; p++) {
2588 unsigned char c, cc;
2589#ifdef MRB_UTF8_STRING
2590 mrb_int clen;
2591
2592 clen = utf8len(p, pend);
2593 if (clen > 1) {
2594 mrb_int i;
2595
2596 for (i=0; i<clen; i++) {
2597 buf[i] = p[i];
2598 }
2599 mrb_str_cat(mrb, result, buf, clen);
2600 p += clen-1;
2601 continue;
2602 }
2603#endif
2604 c = *p;
2605 if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p, pend))) {
2606 buf[0] = '\\'; buf[1] = c;
2607 mrb_str_cat(mrb, result, buf, 2);
2608 continue;
2609 }
2610 if (ISPRINT(c)) {
2611 buf[0] = c;
2612 mrb_str_cat(mrb, result, buf, 1);
2613 continue;
2614 }
2615 switch (c) {
2616 case '\n': cc = 'n'; break;
2617 case '\r': cc = 'r'; break;
2618 case '\t': cc = 't'; break;
2619 case '\f': cc = 'f'; break;
2620 case '\013': cc = 'v'; break;
2621 case '\010': cc = 'b'; break;
2622 case '\007': cc = 'a'; break;
2623 case 033: cc = 'e'; break;
2624 default: cc = 0; break;
2625 }
2626 if (cc) {
2627 buf[0] = '\\';
2628 buf[1] = (char)cc;
2629 mrb_str_cat(mrb, result, buf, 2);
2630 continue;
2631 }
2632 else {
2633 buf[0] = '\\';
2634 buf[3] = '0' + c % 8; c /= 8;
2635 buf[2] = '0' + c % 8; c /= 8;
2636 buf[1] = '0' + c % 8;
2637 mrb_str_cat(mrb, result, buf, 4);
2638 continue;
2639 }
2640 }
2641 mrb_str_cat_lit(mrb, result, "\"");
2642
2643 return result;
2644}
2645
2646/*
2647 * call-seq:
2648 * str.bytes -> array of fixnums
2649 *
2650 * Returns an array of bytes in _str_.
2651 *
2652 * str = "hello"
2653 * str.bytes #=> [104, 101, 108, 108, 111]
2654 */
2655static mrb_value
2656mrb_str_bytes(mrb_state *mrb, mrb_value str)
2657{
2658 struct RString *s = mrb_str_ptr(str);
2659 mrb_value a = mrb_ary_new_capa(mrb, RSTR_LEN(s));
2660 unsigned char *p = (unsigned char *)(RSTR_PTR(s)), *pend = p + RSTR_LEN(s);
2661
2662 while (p < pend) {
2663 mrb_ary_push(mrb, a, mrb_fixnum_value(p[0]));
2664 p++;
2665 }
2666 return a;
2667}
2668
2669/* ---------------------------*/
2670void
2671mrb_init_string(mrb_state *mrb)
2672{
2673 struct RClass *s;
2674
2675 mrb_static_assert(RSTRING_EMBED_LEN_MAX < (1 << 5), "pointer size too big for embedded string");
2676
2677 mrb->string_class = s = mrb_define_class(mrb, "String", mrb->object_class); /* 15.2.10 */
2678 MRB_SET_INSTANCE_TT(s, MRB_TT_STRING);
2679
2680 mrb_define_method(mrb, s, "bytesize", mrb_str_bytesize, MRB_ARGS_NONE());
2681
2682 mrb_define_method(mrb, s, "<=>", mrb_str_cmp_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.1 */
2683 mrb_define_method(mrb, s, "==", mrb_str_equal_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.2 */
2684 mrb_define_method(mrb, s, "+", mrb_str_plus_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.4 */
2685 mrb_define_method(mrb, s, "*", mrb_str_times, MRB_ARGS_REQ(1)); /* 15.2.10.5.5 */
2686 mrb_define_method(mrb, s, "[]", mrb_str_aref_m, MRB_ARGS_ANY()); /* 15.2.10.5.6 */
2687 mrb_define_method(mrb, s, "capitalize", mrb_str_capitalize, MRB_ARGS_NONE()); /* 15.2.10.5.7 */
2688 mrb_define_method(mrb, s, "capitalize!", mrb_str_capitalize_bang, MRB_ARGS_NONE()); /* 15.2.10.5.8 */
2689 mrb_define_method(mrb, s, "chomp", mrb_str_chomp, MRB_ARGS_ANY()); /* 15.2.10.5.9 */
2690 mrb_define_method(mrb, s, "chomp!", mrb_str_chomp_bang, MRB_ARGS_ANY()); /* 15.2.10.5.10 */
2691 mrb_define_method(mrb, s, "chop", mrb_str_chop, MRB_ARGS_REQ(1)); /* 15.2.10.5.11 */
2692 mrb_define_method(mrb, s, "chop!", mrb_str_chop_bang, MRB_ARGS_REQ(1)); /* 15.2.10.5.12 */
2693 mrb_define_method(mrb, s, "downcase", mrb_str_downcase, MRB_ARGS_NONE()); /* 15.2.10.5.13 */
2694 mrb_define_method(mrb, s, "downcase!", mrb_str_downcase_bang, MRB_ARGS_NONE()); /* 15.2.10.5.14 */
2695 mrb_define_method(mrb, s, "empty?", mrb_str_empty_p, MRB_ARGS_NONE()); /* 15.2.10.5.16 */
2696 mrb_define_method(mrb, s, "eql?", mrb_str_eql, MRB_ARGS_REQ(1)); /* 15.2.10.5.17 */
2697
2698 mrb_define_method(mrb, s, "hash", mrb_str_hash_m, MRB_ARGS_NONE()); /* 15.2.10.5.20 */
2699 mrb_define_method(mrb, s, "include?", mrb_str_include, MRB_ARGS_REQ(1)); /* 15.2.10.5.21 */
2700 mrb_define_method(mrb, s, "index", mrb_str_index, MRB_ARGS_ANY()); /* 15.2.10.5.22 */
2701 mrb_define_method(mrb, s, "initialize", mrb_str_init, MRB_ARGS_REQ(1)); /* 15.2.10.5.23 */
2702 mrb_define_method(mrb, s, "initialize_copy", mrb_str_replace, MRB_ARGS_REQ(1)); /* 15.2.10.5.24 */
2703 mrb_define_method(mrb, s, "intern", mrb_str_intern, MRB_ARGS_NONE()); /* 15.2.10.5.25 */
2704 mrb_define_method(mrb, s, "length", mrb_str_size, MRB_ARGS_NONE()); /* 15.2.10.5.26 */
2705 mrb_define_method(mrb, s, "replace", mrb_str_replace, MRB_ARGS_REQ(1)); /* 15.2.10.5.28 */
2706 mrb_define_method(mrb, s, "reverse", mrb_str_reverse, MRB_ARGS_NONE()); /* 15.2.10.5.29 */
2707 mrb_define_method(mrb, s, "reverse!", mrb_str_reverse_bang, MRB_ARGS_NONE()); /* 15.2.10.5.30 */
2708 mrb_define_method(mrb, s, "rindex", mrb_str_rindex, MRB_ARGS_ANY()); /* 15.2.10.5.31 */
2709 mrb_define_method(mrb, s, "size", mrb_str_size, MRB_ARGS_NONE()); /* 15.2.10.5.33 */
2710 mrb_define_method(mrb, s, "slice", mrb_str_aref_m, MRB_ARGS_ANY()); /* 15.2.10.5.34 */
2711 mrb_define_method(mrb, s, "split", mrb_str_split_m, MRB_ARGS_ANY()); /* 15.2.10.5.35 */
2712
2713 mrb_define_method(mrb, s, "to_f", mrb_str_to_f, MRB_ARGS_NONE()); /* 15.2.10.5.38 */
2714 mrb_define_method(mrb, s, "to_i", mrb_str_to_i, MRB_ARGS_ANY()); /* 15.2.10.5.39 */
2715 mrb_define_method(mrb, s, "to_s", mrb_str_to_s, MRB_ARGS_NONE()); /* 15.2.10.5.40 */
2716 mrb_define_method(mrb, s, "to_str", mrb_str_to_s, MRB_ARGS_NONE());
2717 mrb_define_method(mrb, s, "to_sym", mrb_str_intern, MRB_ARGS_NONE()); /* 15.2.10.5.41 */
2718 mrb_define_method(mrb, s, "upcase", mrb_str_upcase, MRB_ARGS_NONE()); /* 15.2.10.5.42 */
2719 mrb_define_method(mrb, s, "upcase!", mrb_str_upcase_bang, MRB_ARGS_NONE()); /* 15.2.10.5.43 */
2720 mrb_define_method(mrb, s, "inspect", mrb_str_inspect, MRB_ARGS_NONE()); /* 15.2.10.5.46(x) */
2721 mrb_define_method(mrb, s, "bytes", mrb_str_bytes, MRB_ARGS_NONE());
2722
2723 mrb_define_method(mrb, s, "freeze", mrb_str_freeze, MRB_ARGS_NONE());
2724}
Note: See TracBrowser for help on using the repository browser.