mruby/c VM Source Code master (2026/08/06)
Loading...
Searching...
No Matches
c_string.c
Go to the documentation of this file.
1
14
15/***** Feature test switches ************************************************/
16/***** System headers *******************************************************/
17//@cond
18#include "vm_config.h"
19#include <stdlib.h>
20#include <string.h>
21#include <limits.h>
22#include <assert.h>
23//@endcond
24
25/***** Local headers ********************************************************/
26#include "mrubyc.h"
27#include "_autogen_unicode_case.h"
28
29/***** Constat values *******************************************************/
30/***** Macros ***************************************************************/
31/***** Typedefs *************************************************************/
32/***** Function prototypes **************************************************/
33/***** Local variables ******************************************************/
34/***** Global variables *****************************************************/
35/***** Signal catching functions ********************************************/
36/***** Local functions ******************************************************/
37#if MRBC_USE_STRING
38//================================================================
44static int is_space( int ch )
45{
46 static const char ws[] = " \t\r\n\f\v"; // '\0' on tail
47
48 for( int i = 0; i < sizeof(ws); i++ ) {
49 if( ch == ws[i] ) return 1;
50 }
51 return 0;
52}
53
54
55/***** Global functions *****************************************************/
56//================================================================
64mrbc_value mrbc_string_new(mrbc_vm *vm, const void *src, int len)
65{
66 uint8_t *buf = mrbc_alloc(vm, len+1);
67
68 // Copy a source string.
69 if( src == NULL ) {
70 buf[0] = '\0';
71 } else {
72 memcpy( buf, src, len );
73 buf[len] = '\0';
74 }
75
76 return mrbc_string_new_alloc(vm, buf, len);
77}
78
79
80//================================================================
89{
90 mrbc_string *str = mrbc_alloc(vm, sizeof(mrbc_string));
91
92 *str = (mrbc_string){
94 .size = len,
95 .data = buf,
96 };
97
98 return mrbc_immediate_value(MRBC_TT_STRING, .string = str);
99}
100
101
102//================================================================
108{
110 mrbc_raw_free(str->string);
111}
112
113
114
115//================================================================
119{
120 // shrink suitable size. realloc() may move the block.
121 str->string->data = mrbc_raw_realloc(str->string->data, 1);
122 str->string->data[0] = '\0';
123 str->string->size = 0;
124}
125
126
127#if defined(MRBC_ALLOC_VMID)
128//================================================================
131void mrbc_string_clear_vm_id(mrbc_value *str)
132{
133 mrbc_set_vm_id( str->string, 0 );
134 mrbc_set_vm_id( str->string->data, 0 );
135}
136#endif
137
138
139//================================================================
147{
148 mrbc_string *h1 = s1->string;
149 mrbc_value value = mrbc_string_new(vm, NULL, h1->size);
150
151 memcpy( value.string->data, h1->data, h1->size + 1 );
152
153 return value;
154}
155
156
157//================================================================
166{
167 mrbc_string *h1 = s1->string;
168 mrbc_string *h2 = s2->string;
169 mrbc_value value = mrbc_string_new(vm, NULL, h1->size + h2->size);
170
171 memcpy( value.string->data, h1->data, h1->size );
172 memcpy( value.string->data + h1->size, h2->data, h2->size + 1 );
173
174 return value;
175}
176
177
178//================================================================
186{
187 int len1 = s1->string->size;
188 int len2 = (mrbc_type(*s2) == MRBC_TT_STRING) ? s2->string->size : 1;
189 uint8_t *str = mrbc_raw_realloc(s1->string->data, len1+len2+1);
190
191 if( mrbc_type(*s2) == MRBC_TT_STRING ) {
192 memcpy(str + len1, s2->string->data, len2 + 1);
193 } else if( mrbc_type(*s2) == MRBC_TT_INTEGER ) {
194 str[len1] = s2->i;
195 str[len1+1] = '\0';
196 }
197
198 s1->string->size = len1 + len2;
199 s1->string->data = str;
200
201 return 0;
202}
203
204
205//================================================================
213int mrbc_string_append_cbuf(mrbc_value *s1, const void *s2, int len2)
214{
215 int len1 = s1->string->size;
216 uint8_t *str = mrbc_raw_realloc(s1->string->data, len1+len2+1);
217
218 if( s2 ) {
219 memcpy(str + len1, s2, len2);
220 str[len1 + len2] = 0;
221 } else {
222 memset(str + len1, 0, len2 + 1);
223 }
224
225 s1->string->size = len1 + len2;
226 s1->string->data = str;
227
228 return 0;
229}
230
231
232//================================================================
240int mrbc_string_index(const mrbc_value *src, const mrbc_value *pattern, int offset)
241{
242 char *p1 = mrbc_string_cstr(src) + offset;
243 char *p2 = mrbc_string_cstr(pattern);
244 int try_cnt = mrbc_string_size(src) - mrbc_string_size(pattern) - offset;
245
246 while( try_cnt >= 0 ) {
247 if( memcmp( p1, p2, mrbc_string_size(pattern) ) == 0 ) {
248 return p1 - mrbc_string_cstr(src); // matched.
249 }
250 try_cnt--;
251 p1++;
252 }
253
254 return -1;
255}
256
257
258//================================================================
265int mrbc_string_strip(mrbc_value *src, int mode)
266{
267 char *p1 = mrbc_string_cstr(src);
268 char *p2 = p1 + mrbc_string_size(src) - 1;
269
270 // left-side
271 if( mode & 0x01 ) {
272 while( p1 <= p2 ) {
273 if( *p1 == '\0' ) break;
274 if( !is_space(*p1) ) break;
275 p1++;
276 }
277 }
278 // right-side
279 if( mode & 0x02 ) {
280 while( p1 <= p2 ) {
281 if( !is_space(*p2) ) break;
282 p2--;
283 }
284 }
285
286 int new_size = p2 - p1 + 1;
287 if( mrbc_string_size(src) == new_size ) return 0;
288
289 char *buf = mrbc_string_cstr(src);
290 if( p1 != buf ) memmove( buf, p1, new_size );
291 buf[new_size] = '\0';
292
293 // shrink suitable size. realloc() may move the block.
294 src->string->data = mrbc_raw_realloc(buf, new_size+1);
295 src->string->size = new_size;
296
297 return 1;
298}
299
300
301//================================================================
308{
309 char *p1 = mrbc_string_cstr(src);
310 char *p2 = p1 + mrbc_string_size(src) - 1;
311
312 if( *p2 == '\n' ) {
313 p2--;
314 }
315 if( *p2 == '\r' ) {
316 p2--;
317 }
318
319 int new_size = p2 - p1 + 1;
320 if( mrbc_string_size(src) == new_size ) return 0;
321
322 char *buf = mrbc_string_cstr(src);
323 buf[new_size] = '\0';
324 src->string->size = new_size;
325
326 return 1;
327}
328
329
330#if !MRBC_USE_STRING_UTF8 || !MRBC_USE_UNICODE_CASE
331//================================================================
338{
339 int len = str->string->size;
340 int count = 0;
341 uint8_t *data = str->string->data;
342 while (len != 0) {
343 len--;
344 if ('a' <= data[len] && data[len] <= 'z') {
345 data[len] = data[len] - ('a' - 'A');
346 count++;
347 }
348 }
349 return count;
350}
351
352
353//================================================================
360{
361 int len = str->string->size;
362 int count = 0;
363 uint8_t *data = str->string->data;
364 while (len != 0) {
365 len--;
366 if ('A' <= data[len] && data[len] <= 'Z') {
367 data[len] = data[len] + ('a' - 'A');
368 count++;
369 }
370 }
371 return count;
372}
373#endif /* !MRBC_USE_STRING_UTF8 || !MRBC_USE_UNICODE_CASE */
374
375
376#if MRBC_USE_STRING_UTF8
377//================================================================
383int mrbc_string_utf8_size(const char *str)
384{
385 unsigned char c = (unsigned char)*str;
386
387 if( (c & 0x80) == 0x00 ) return 1; // ASCII: 0xxxxxxx
388 if( (c & 0xE0) == 0xC0 ) return 2; // 2-byte: 110xxxxx
389 if( (c & 0xF0) == 0xE0 ) return 3; // 3-byte: 1110xxxx
390 if( (c & 0xF8) == 0xF0 ) return 4; // 4-byte: 11110xxx
391 return 0; // continuation or invalid
392}
393
394
395//================================================================
407static int utf8_validated_char_len(const char *str, const char *end)
408{
409 int char_len = mrbc_string_utf8_size(str);
410
411 if( char_len == 0 ) return 1; // isolated continuation byte
412 if( str + char_len > end ) return 1; // incomplete sequence at end
413
414 for( int j = 1; j < char_len; j++ ) {
415 if( ((unsigned char)str[j] & 0xC0) != 0x80 ) return 1; // invalid continuation
416 }
417 return char_len;
418}
419
420
421//================================================================
432int mrbc_string_char_size(const char *str, int len)
433{
434 int count = 0;
435 const char *end = str + len;
436
437 while( str < end ) {
438 int char_len = utf8_validated_char_len(str, end);
439 count++;
440 str += char_len;
441 }
442 return count;
443}
444
445
446//================================================================
454int mrbc_string_chars2bytes(mrbc_value *src, int off, int idx)
455{
456 const char *str = mrbc_string_cstr(src) + off;
457 const char *end = mrbc_string_cstr(src) + src->string->size;
458 int bytes = 0;
459
460 for( int i = 0; i < idx && str < end; i++ ) {
461 int char_len = utf8_validated_char_len(str, end);
462 bytes += char_len;
463 str += char_len;
464 }
465 return bytes;
466}
467
468
469//================================================================
476int mrbc_string_bytes2chars(const mrbc_value *src, int byte_index)
477{
478 const char *str = mrbc_string_cstr(src);
479 const char *target = str + byte_index;
480 int count = 0;
481
482 while( str < target ) {
483 int char_len = mrbc_string_utf8_size(str);
484 if( char_len == 0 ) char_len = 1;
485 str += char_len;
486 count++;
487 }
488 return (str == target) ? count : -1;
489}
490
491
492#if MRBC_USE_UNICODE_CASE
493//================================================================
500static int32_t unicode_decode_utf8(const uint8_t *str, int *len_out)
501{
502 uint8_t c = str[0];
503 int32_t cp;
504 int len;
505
506 if( (c & 0x80) == 0 ) {
507 cp = c;
508 len = 1;
509 } else if( (c & 0xE0) == 0xC0 ) {
510 cp = (c & 0x1F) << 6 | (str[1] & 0x3F);
511 len = 2;
512 } else if( (c & 0xF0) == 0xE0 ) {
513 cp = (c & 0x0F) << 12 | (str[1] & 0x3F) << 6 | (str[2] & 0x3F);
514 len = 3;
515 } else if( (c & 0xF8) == 0xF0 ) {
516 cp = (c & 0x07) << 18 | (str[1] & 0x3F) << 12 | (str[2] & 0x3F) << 6 | (str[3] & 0x3F);
517 len = 4;
518 } else {
519 *len_out = 1;
520 return -1;
521 }
522 *len_out = len;
523 return cp;
524}
525
526
527//================================================================
534static int unicode_encode_utf8(int32_t cp, uint8_t *buf)
535{
536 if( cp < 0x80 ) {
537 buf[0] = cp;
538 return 1;
539 } else if( cp < 0x800 ) {
540 buf[0] = 0xC0 | (cp >> 6);
541 buf[1] = 0x80 | (cp & 0x3F);
542 return 2;
543 } else if( cp < 0x10000 ) {
544 buf[0] = 0xE0 | (cp >> 12);
545 buf[1] = 0x80 | ((cp >> 6) & 0x3F);
546 buf[2] = 0x80 | (cp & 0x3F);
547 return 3;
548 } else {
549 buf[0] = 0xF0 | (cp >> 18);
550 buf[1] = 0x80 | ((cp >> 12) & 0x3F);
551 buf[2] = 0x80 | ((cp >> 6) & 0x3F);
552 buf[3] = 0x80 | (cp & 0x3F);
553 return 4;
554 }
555}
556
557
558//================================================================
566static int32_t unicode_case_lookup_range(int32_t cp, const case_range_t *ranges, int count)
567{
568 for( int i = 0; i < count; i++ ) {
569 if( ranges[i].start <= cp && cp <= ranges[i].end ) {
570 // Check if this character matches the XOR pattern
571 int32_t converted = cp ^ ranges[i].xor_val;
572 // Verify the conversion is in the expected direction
573 // For upcase: converted should be < cp (or same block)
574 // For downcase: converted should be > cp (or same block)
575 return converted;
576 }
577 }
578 return cp; // Not found in ranges
579}
580
581
582//================================================================
590static int32_t unicode_case_lookup_exception(int32_t cp, const case_exception_t *exceptions, int count)
591{
592 int low = 0, high = count - 1;
593
594 while( low <= high ) {
595 int mid = (low + high) / 2;
596 if( exceptions[mid].from == cp ) {
597 return exceptions[mid].to;
598 } else if( exceptions[mid].from < cp ) {
599 low = mid + 1;
600 } else {
601 high = mid - 1;
602 }
603 }
604 return cp; // Not found
605}
606
607
608//================================================================
614static int32_t unicode_upcase_codepoint(int32_t cp)
615{
616 if( cp < 0 || cp > 0xFFFF ) return cp; // BMP only
617
618 // Try ranges first (more common patterns)
619 int32_t result = unicode_case_lookup_range(cp, upcase_ranges, UPCASE_RANGES_COUNT);
620 if( result != cp ) return result;
621
622 // Fall back to exceptions
623 return unicode_case_lookup_exception(cp, upcase_exceptions, UPCASE_EXCEPTIONS_COUNT);
624}
625
626
627//================================================================
633static int32_t unicode_downcase_codepoint(int32_t cp)
634{
635 if( cp < 0 || cp > 0xFFFF ) return cp; // BMP only
636
637 // Try ranges first
638 int32_t result = unicode_case_lookup_range(cp, downcase_ranges, DOWNCASE_RANGES_COUNT);
639 if( result != cp ) return result;
640
641 // Fall back to exceptions
642 return unicode_case_lookup_exception(cp, downcase_exceptions, DOWNCASE_EXCEPTIONS_COUNT);
643}
644
645
646//================================================================
653{
654 int len = str->string->size;
655 uint8_t *data = str->string->data;
656 int count = 0;
657
658 // First pass: check if any conversion changes byte length
659 int new_len = 0;
660 int needs_realloc = 0;
661 for( int i = 0; i < len; ) {
662 int char_len;
663 int32_t cp = unicode_decode_utf8(data + i, &char_len);
664 int32_t upper_cp = unicode_upcase_codepoint(cp);
665
666 uint8_t buf[4];
667 int new_char_len = unicode_encode_utf8(upper_cp, buf);
668 new_len += new_char_len;
669 if( new_char_len != char_len ) needs_realloc = 1;
670 i += char_len;
671 }
672
673 if( needs_realloc ) {
674 // Need to allocate new buffer
675 uint8_t *new_data = mrbc_raw_alloc(new_len + 1);
676 if( !new_data ) return 0;
677
678 int j = 0;
679 for( int i = 0; i < len; ) {
680 int char_len;
681 int32_t cp = unicode_decode_utf8(data + i, &char_len);
682 int32_t upper_cp = unicode_upcase_codepoint(cp);
683
684 int new_char_len = unicode_encode_utf8(upper_cp, new_data + j);
685 if( upper_cp != cp ) count++;
686 j += new_char_len;
687 i += char_len;
688 }
689 new_data[new_len] = '\0';
690
691 mrbc_raw_free(data);
692 str->string->data = new_data;
693 str->string->size = new_len;
694 } else {
695 // In-place conversion
696 for( int i = 0; i < len; ) {
697 int char_len;
698 int32_t cp = unicode_decode_utf8(data + i, &char_len);
699 int32_t upper_cp = unicode_upcase_codepoint(cp);
700
701 if( upper_cp != cp ) {
702 unicode_encode_utf8(upper_cp, data + i);
703 count++;
704 }
705 i += char_len;
706 }
707 }
708
709 return count;
710}
711
712
713//================================================================
720{
721 int len = str->string->size;
722 uint8_t *data = str->string->data;
723 int count = 0;
724
725 // First pass: check if any conversion changes byte length
726 int new_len = 0;
727 int needs_realloc = 0;
728 for( int i = 0; i < len; ) {
729 int char_len;
730 int32_t cp = unicode_decode_utf8(data + i, &char_len);
731 int32_t lower_cp = unicode_downcase_codepoint(cp);
732
733 uint8_t buf[4];
734 int new_char_len = unicode_encode_utf8(lower_cp, buf);
735 new_len += new_char_len;
736 if( new_char_len != char_len ) needs_realloc = 1;
737 i += char_len;
738 }
739
740 if( needs_realloc ) {
741 // Need to allocate new buffer
742 uint8_t *new_data = mrbc_raw_alloc(new_len + 1);
743 if( !new_data ) return 0;
744
745 int j = 0;
746 for( int i = 0; i < len; ) {
747 int char_len;
748 int32_t cp = unicode_decode_utf8(data + i, &char_len);
749 int32_t lower_cp = unicode_downcase_codepoint(cp);
750
751 int new_char_len = unicode_encode_utf8(lower_cp, new_data + j);
752 if( lower_cp != cp ) count++;
753 j += new_char_len;
754 i += char_len;
755 }
756 new_data[new_len] = '\0';
757
758 mrbc_raw_free(data);
759 str->string->data = new_data;
760 str->string->size = new_len;
761 } else {
762 // In-place conversion
763 for( int i = 0; i < len; ) {
764 int char_len;
765 int32_t cp = unicode_decode_utf8(data + i, &char_len);
766 int32_t lower_cp = unicode_downcase_codepoint(cp);
767
768 if( lower_cp != cp ) {
769 unicode_encode_utf8(lower_cp, data + i);
770 count++;
771 }
772 i += char_len;
773 }
774 }
775
776 return count;
777}
778#endif /* MRBC_USE_UNICODE_CASE */
779#endif // MRBC_USE_STRING_UTF8
780
781
782//================================================================
785static void c_string_new(mrbc_vm *vm, mrbc_value v[], int argc)
786{
787 mrbc_value value;
788
789 switch( argc ) {
790 case 0:
791 value = mrbc_string_new(vm, NULL, 0);
792 break;
793
794 case 1:
795 if( mrbc_type(v[1]) != MRBC_TT_STRING ) {
796 mrbc_raisef( vm, MRBC_CLASS(TypeError), "no implicit conversion into %s", "String");
797 return;
798 }
799 value = mrbc_string_dup(vm, &v[1]);
800 break;
801
802 default:
803 mrbc_raise( vm, MRBC_CLASS(ArgumentError), "wrong number of arguments");
804 return;
805 }
806
807 SET_RETURN(value);
808}
809
810
811//================================================================
814static void c_string_add(mrbc_vm *vm, mrbc_value v[], int argc)
815{
816 if( mrbc_type(v[1]) != MRBC_TT_STRING ) {
817 mrbc_raise( vm, MRBC_CLASS(ArgumentError), 0 );
818 return;
819 }
820
821 mrbc_value value = mrbc_string_add(vm, &v[0], &v[1]);
822 SET_RETURN(value);
823}
824
825
826
827//================================================================
830static void c_string_mul(mrbc_vm *vm, mrbc_value v[], int argc)
831{
832 if( mrbc_type(v[1]) != MRBC_TT_INTEGER ) {
833 mrbc_raisef( vm, MRBC_CLASS(TypeError), "no implicit conversion into %s", "String");
834 return;
835 }
836
837 if( v[1].i < 0 ) {
838 mrbc_raise( vm, MRBC_CLASS(ArgumentError), "negative argument");
839 return;
840 }
841
842 mrbc_value value = mrbc_string_new(vm, NULL,
843 mrbc_string_size(&v[0]) * mrbc_integer(v[1]));
844 uint8_t *p = value.string->data;
845 for( int i = 0; i < v[1].i; i++ ) {
846 memcpy( p, mrbc_string_cstr(&v[0]), mrbc_string_size(&v[0]) );
847 p += mrbc_string_size(&v[0]);
848 }
849 *p = 0;
850
851 SET_RETURN(value);
852}
853
854
855
856//================================================================
859static void c_string_size(mrbc_vm *vm, mrbc_value v[], int argc)
860{
861#if MRBC_USE_STRING_UTF8
862 mrbc_int_t size = mrbc_string_char_size(mrbc_string_cstr(&v[0]), mrbc_string_size(&v[0]));
863#else
864 mrbc_int_t size = mrbc_string_size(&v[0]);
865#endif
866
867 SET_INT_RETURN( size );
868}
869
870
871//================================================================
874static void c_string_bytesize(mrbc_vm *vm, mrbc_value v[], int argc)
875{
877}
878
879
880//================================================================
883static void c_string_to_i(mrbc_vm *vm, mrbc_value v[], int argc)
884{
885 int base = 10;
886 if( argc ) {
887 base = v[1].i;
888 if( base < 2 || base > 36 ) {
889 mrbc_raisef(vm, MRBC_CLASS(ArgumentError), "invalid radix %d", base);
890 return;
891 }
892 }
893
894 mrbc_int_t i = mrbc_atoi( mrbc_string_cstr(v), base );
895
896 SET_INT_RETURN( i );
897}
898
899
900#if MRBC_USE_FLOAT
901//================================================================
904static void c_string_to_f(mrbc_vm *vm, mrbc_value v[], int argc)
905{
906#if MRBC_USE_FLOAT == 1
907 mrbc_float_t d = strtof(mrbc_string_cstr(v), NULL);
908#else
909 mrbc_float_t d = strtod(mrbc_string_cstr(v), NULL);
910#endif
911
912 SET_FLOAT_RETURN( d );
913}
914#endif
915
916
917//================================================================
920static void c_string_to_s(mrbc_vm *vm, mrbc_value v[], int argc)
921{
922 if( mrbc_type(v[0]) == MRBC_TT_CLASS ) {
923 mrbc_object_inspect(vm, v, argc);
924 return;
925 }
926}
927
928
929//================================================================
932static void c_string_append(mrbc_vm *vm, mrbc_value v[], int argc)
933{
934#if MRBC_USE_STRING_UTF8
935 // In UTF-8 mode, << accepts an integer codepoint
936 if( mrbc_type(v[1]) == MRBC_TT_INTEGER ) {
937 mrbc_int_t codepoint = mrbc_integer(v[1]);
938 char buf[5];
939 int len;
940
941 if( codepoint < 0 || codepoint > 0x10FFFF ) {
942 mrbc_raise(vm, MRBC_CLASS(RangeError), "out of char range");
943 return;
944 }
945 if( codepoint >= 0xD800 && codepoint <= 0xDFFF ) {
946 mrbc_raise(vm, MRBC_CLASS(RangeError), "invalid codepoint in UTF-8");
947 return;
948 }
949
950 len = mrbc_utf8_encode(codepoint, buf);
951 mrbc_string_append_cbuf(&v[0], buf, len);
952 return;
953 }
954#endif
955
956 mrbc_string_append( &v[0], &v[1] );
957}
958
959
960//================================================================
961/* Parse slice arguments (nth), (nth, len), or (Range) into *pos and *len.
962 Returns: 0=ok, 1=return nil, 2=error already raised.
963*/
964static int string_slice_parse(mrbc_vm *vm, mrbc_value v[], int argc,
965 int target_len, int *pos, int *len)
966{
967 if (argc == 1 && mrbc_type(v[1]) == MRBC_TT_INTEGER) {
968 *pos = mrbc_integer(v[1]);
969 if (*pos < 0) *pos += target_len;
970 if (*pos >= target_len) return 1;
971 *len = 1;
972 }
973 else if( argc == 2 && mrbc_type(v[1]) == MRBC_TT_INTEGER &&
974 mrbc_type(v[2]) == MRBC_TT_INTEGER ) {
975 *pos = mrbc_integer(v[1]);
976 if( *pos < 0 ) *pos += target_len;
977 *len = mrbc_integer(v[2]);
978 }
979 else if( argc == 1 && mrbc_type(v[1]) == MRBC_TT_RANGE ) {
980 const mrbc_value *v1 = mrbc_range_first_p(&v[1]);
981 switch( mrbc_type(*v1) ) {
982 case MRBC_TT_INTEGER:
983 *pos = mrbc_integer(*v1);
984 if( *pos < 0 ) *pos += target_len;
985 break;
986 case MRBC_TT_NIL:
987 *pos = 0;
988 break;
989 default:
990 mrbc_raise(vm, MRBC_CLASS(TypeError), 0);
991 return 2;
992 }
993
994 const mrbc_value *v2 = mrbc_range_last_p(&v[1]);
995 int pos2;
996 switch( mrbc_type(*v2) ) {
997 case MRBC_TT_INTEGER:
998 pos2 = mrbc_integer(*v2);
999 if( pos2 < 0 ) pos2 += target_len;
1000 break;
1001 case MRBC_TT_NIL:
1002 pos2 = target_len;
1003 break;
1004 default:
1005 mrbc_raise(vm, MRBC_CLASS(TypeError), 0);
1006 return 2;
1007 }
1008
1009 *len = pos2 - *pos;
1010 if( !mrbc_range_exclude_end(&v[1]) ) (*len)++;
1011 }
1012 else {
1013 mrbc_raise(vm, MRBC_CLASS(ArgumentError), 0);
1014 return 2;
1015 }
1016
1017 if( *pos < 0 || *pos > target_len ) return 1;
1018 if( *len > target_len - *pos ) *len = target_len - *pos;
1019 if( *len < 0 ) {
1020 if( mrbc_type(v[1]) == MRBC_TT_RANGE) {
1021 *len = 0;
1022 } else {
1023 return 1;
1024 }
1025 }
1026
1027 return 0;
1028}
1029
1030
1031//================================================================
1034static void c_string_slice(mrbc_vm *vm, mrbc_value v[], int argc)
1035{
1036#if MRBC_USE_STRING_UTF8
1037 int target_len = mrbc_string_char_size(mrbc_string_cstr(&v[0]), mrbc_string_size(&v[0]));
1038#else
1039 int target_len = mrbc_string_size(v);
1040#endif
1041 int pos, len;
1042
1043 switch( string_slice_parse(vm, v, argc, target_len, &pos, &len) ) {
1044 case 1: SET_NIL_RETURN(); return;
1045 case 2: return;
1046 }
1047
1048#if MRBC_USE_STRING_UTF8
1049 int byte_pos = mrbc_string_chars2bytes(&v[0], 0, pos);
1050 int byte_len = mrbc_string_chars2bytes(&v[0], byte_pos, len);
1051 mrbc_value ret = mrbc_string_new(vm, mrbc_string_cstr(v) + byte_pos, byte_len);
1052#else
1053 mrbc_value ret = mrbc_string_new(vm, mrbc_string_cstr(v) + pos, len);
1054#endif
1055
1056 SET_RETURN(ret);
1057}
1058
1059
1060//================================================================
1063static void c_string_byteslice(mrbc_vm *vm, mrbc_value v[], int argc)
1064{
1065 int target_len = mrbc_string_size(&v[0]); // always byte count
1066 int pos, len;
1067
1068 switch (string_slice_parse(vm, v, argc, target_len, &pos, &len)) {
1069 case 1: SET_NIL_RETURN(); return;
1070 case 2: return;
1071 }
1072
1073 mrbc_value ret = mrbc_string_new(vm, mrbc_string_cstr(v) + pos, len);
1074
1075 SET_RETURN(ret);
1076}
1077
1078
1079//================================================================
1082static void c_string_insert(mrbc_vm *vm, mrbc_value v[], int argc)
1083{
1084#if MRBC_USE_STRING_UTF8
1085 int target_len = mrbc_string_char_size(mrbc_string_cstr(&v[0]), mrbc_string_size(&v[0]));
1086#else
1087 int target_len = mrbc_string_size(v);
1088#endif
1089 int pos, len;
1090 mrbc_value *val;
1091
1092 // in case of self[pos] = val
1093 if( argc == 2 && mrbc_type(v[1]) == MRBC_TT_INTEGER &&
1094 mrbc_type(v[2]) == MRBC_TT_STRING ) {
1095 pos = mrbc_integer(v[1]);
1096 len = 1;
1097 val = &v[2];
1098 }
1099
1100 // in case of self[pos, len] = val
1101 else if( argc == 3 && mrbc_type(v[1]) == MRBC_TT_INTEGER &&
1102 mrbc_type(v[2]) == MRBC_TT_INTEGER &&
1103 mrbc_type(v[3]) == MRBC_TT_STRING ) {
1104 pos = mrbc_integer(v[1]);
1105 len = mrbc_integer(v[2]);
1106 val = &v[3];
1107 }
1108
1109 // in case of self[Range] = val
1110 else if( argc == 2 && mrbc_type(v[1]) == MRBC_TT_RANGE &&
1111 mrbc_type(v[2]) == MRBC_TT_STRING ) {
1112 const mrbc_value *v1 = mrbc_range_first_p(&v[1]);
1113 switch( mrbc_type(*v1) ) {
1114 case MRBC_TT_INTEGER:
1115 pos = mrbc_integer(*v1);
1116 if( pos < 0 ) pos += target_len;
1117 if( pos < 0 || pos > target_len ) {
1118 mrbc_raise( vm, MRBC_CLASS(RangeError), 0 );
1119 return;
1120 }
1121 break;
1122 case MRBC_TT_NIL:
1123 pos = 0;
1124 break;
1125 default:
1126 goto TYPE_ERROR;
1127 }
1128
1129 const mrbc_value *v2 = mrbc_range_last_p(&v[1]);
1130 int pos2;
1131 switch( mrbc_type(*v2) ) {
1132 case MRBC_TT_INTEGER:
1133 pos2 = mrbc_integer(*v2);
1134 if( pos2 < 0 ) pos2 += target_len;
1135 break;
1136 case MRBC_TT_NIL:
1137 pos2 = target_len;
1138 break;
1139 default:
1140 goto TYPE_ERROR;
1141 }
1142
1143 len = pos2 - pos;
1144 if( !mrbc_range_exclude_end(&v[1]) ) len++;
1145 if( len < 0 ) len = 0;
1146 val = &v[2];
1147 }
1148
1149 // other cases
1150 else {
1151 mrbc_raise( vm, MRBC_CLASS(ArgumentError), 0 );
1152 return;
1153 }
1154
1155 int len1 = target_len; // character length
1156 int len2 = mrbc_string_size(val); // byte length of replacement
1157 if( pos < 0 ) pos = len1 + pos; // adjust to positive number.
1158 if( len > len1 - pos ) len = len1 - pos;
1159 if( pos < 0 || pos > len1 || len < 0) {
1160 mrbc_raisef( vm, MRBC_CLASS(IndexError), "index %d out of string", pos );
1161 return;
1162 }
1163
1164#if MRBC_USE_STRING_UTF8
1165 // Convert character position/length to byte position/length
1166 int byte_pos = mrbc_string_chars2bytes(&v[0], 0, pos);
1167 int byte_len = mrbc_string_chars2bytes(&v[0], byte_pos, len);
1168 int byte_len1 = mrbc_string_size(&v[0]); // original byte length
1169
1170 int byte_len3 = byte_len1 + len2 - byte_len; // final byte length
1171 uint8_t *str = v->string->data;
1172 if( byte_len1 < byte_len3 ) {
1173 str = mrbc_realloc(vm, str, byte_len3+1); // expand
1174 }
1175
1176 memmove( str + byte_pos + len2, str + byte_pos + byte_len, byte_len1 - byte_pos - byte_len + 1 );
1177 memcpy( str + byte_pos, mrbc_string_cstr(val), len2 );
1178
1179 if( byte_len1 > byte_len3 ) {
1180 str = mrbc_realloc(vm, str, byte_len3+1); // shrink
1181 }
1182
1183 v->string->size = byte_len3;
1184 v->string->data = str;
1185#else
1186 int len3 = len1 + len2 - len; // final length.
1187 uint8_t *str = v->string->data;
1188 if( len1 < len3 ) {
1189 str = mrbc_realloc(vm, str, len3+1); // expand
1190 }
1191
1192 memmove( str + pos + len2, str + pos + len, len1 - pos - len + 1 );
1193 memcpy( str + pos, mrbc_string_cstr(val), len2 );
1194
1195 if( len1 > len3 ) {
1196 str = mrbc_realloc(vm, str, len3+1); // shrink
1197 }
1198
1199 v->string->size = len1 + len2 - len;
1200 v->string->data = str;
1201#endif
1202
1203 // return val
1204 mrbc_decref(&v[0]);
1205 v[0] = *val;
1207 return;
1208
1209
1210 TYPE_ERROR:
1211 mrbc_raise( vm, MRBC_CLASS(TypeError), 0 );
1212 return;
1213}
1214
1215
1216//================================================================
1219static void c_string_chomp(mrbc_vm *vm, mrbc_value v[], int argc)
1220{
1221 mrbc_value ret = mrbc_string_dup(vm, &v[0]);
1222
1223 mrbc_string_chomp(&ret);
1224
1225 SET_RETURN(ret);
1226}
1227
1228
1229//================================================================
1232static void c_string_clear(mrbc_vm *vm, mrbc_value v[], int argc)
1233{
1234 mrbc_string_clear(&v[0]);
1235}
1236
1237
1238//================================================================
1241static void c_string_chomp_self(mrbc_vm *vm, mrbc_value v[], int argc)
1242{
1243 if( mrbc_string_chomp(&v[0]) == 0 ) {
1245 }
1246}
1247
1248
1249//================================================================
1252static void c_string_dup(mrbc_vm *vm, mrbc_value v[], int argc)
1253{
1254 mrbc_value ret = mrbc_string_dup(vm, &v[0]);
1255
1256 SET_RETURN(ret);
1257}
1258
1259
1260//================================================================
1263static void c_string_empty(mrbc_vm *vm, mrbc_value v[], int argc)
1264{
1266}
1267
1268
1269//================================================================
1272static void c_string_getbyte(mrbc_vm *vm, mrbc_value v[], int argc)
1273{
1274 int len = mrbc_string_size(&v[0]);
1275 mrbc_int_t idx = mrbc_integer(v[1]);
1276
1277 if( idx >= 0 ) {
1278 if( idx >= len ) idx = -1;
1279 } else {
1280 idx += len;
1281 }
1282 if( idx >= 0 ) {
1283 SET_INT_RETURN( ((uint8_t *)mrbc_string_cstr(&v[0]))[idx] );
1284 } else {
1286 }
1287}
1288
1289
1290//================================================================
1293static void c_string_setbyte(mrbc_vm *vm, mrbc_value v[], int argc)
1294{
1295 if( argc != 2 ) {
1296 mrbc_raise(vm, MRBC_CLASS(ArgumentError), "wrong number of arguments");
1297 return;
1298 }
1299
1300 int len = mrbc_string_size(&v[0]);
1301 mrbc_int_t idx = mrbc_integer(v[1]);
1302 mrbc_int_t dat = mrbc_integer(v[2]);
1303
1304 if( idx < 0 ) {
1305 idx += len;
1306 }
1307 if( idx < 0 || idx >= len ) {
1308 mrbc_raisef( vm, MRBC_CLASS(IndexError), "index %d out of string", idx );
1309 return;
1310 }
1311
1312 mrbc_string_cstr(&v[0])[idx] = dat;
1313
1314 SET_INT_RETURN( dat );
1315}
1316
1317
1318//================================================================
1321static void c_string_index(mrbc_vm *vm, mrbc_value v[], int argc)
1322{
1323 int index;
1324 int offset;
1325
1326 if( argc == 1 ) {
1327 offset = 0;
1328
1329 } else if( argc == 2 && mrbc_type(v[2]) == MRBC_TT_INTEGER ) {
1330 offset = v[2].i;
1331#if MRBC_USE_STRING_UTF8
1332 int char_len = mrbc_string_char_size(mrbc_string_cstr(&v[0]), mrbc_string_size(&v[0]));
1333 if( offset < 0 ) offset += char_len;
1334 if( offset < 0 ) goto NIL_RETURN;
1335 // Convert character offset to byte offset
1336 offset = mrbc_string_chars2bytes(&v[0], 0, offset);
1337#else
1338 if( offset < 0 ) offset += mrbc_string_size(&v[0]);
1339 if( offset < 0 ) goto NIL_RETURN;
1340#endif
1341
1342 } else {
1343 mrbc_raise( vm, MRBC_CLASS(ArgumentError), 0 );
1344 return;
1345 }
1346
1347 index = mrbc_string_index(&v[0], &v[1], offset);
1348 if( index < 0 ) goto NIL_RETURN;
1349
1350#if MRBC_USE_STRING_UTF8
1351 // Convert byte index to character index
1352 index = mrbc_string_bytes2chars(&v[0], index);
1353 if( index < 0 ) goto NIL_RETURN;
1354#endif
1355
1356 SET_INT_RETURN(index);
1357 return;
1358
1359 NIL_RETURN:
1361}
1362
1363
1364//================================================================
1367static void c_string_inspect(mrbc_vm *vm, mrbc_value v[], int argc)
1368{
1369 if( mrbc_type(v[0]) == MRBC_TT_CLASS ) {
1370 mrbc_object_inspect(vm, v, argc);
1371 return;
1372 }
1373
1374 mrbc_value ret = mrbc_string_new_cstr(vm, "\"");
1375 const char *s = mrbc_string_cstr(v);
1376 int size = mrbc_string_size(v);
1377
1378#if MRBC_USE_STRING_UTF8
1379 for( int i = 0; i < size; ) {
1380 unsigned char uch = (unsigned char)s[i];
1381 if( uch < 0x80 ) {
1382 // ASCII byte: use existing escape logic
1383 char buf[10];
1384 mrbc_char_to_s( buf, s + i );
1385 mrbc_string_append_cstr(&ret, buf);
1386 i++;
1387 } else {
1388 int char_len = utf8_validated_char_len(s + i, s + size);
1389 int raw_len = mrbc_string_utf8_size(s + i);
1390 if( raw_len >= 2 && char_len == raw_len ) {
1391 // Valid multi-byte UTF-8 character: output as-is
1392 mrbc_string_append_cbuf(&ret, s + i, char_len);
1393 i += char_len;
1394 } else {
1395 // Invalid/isolated byte: escape as \xHH
1396 char buf[5];
1397 buf[0] = '\\';
1398 buf[1] = 'x';
1399 buf[2] = "0123456789ABCDEF"[uch >> 4];
1400 buf[3] = "0123456789ABCDEF"[uch & 0x0f];
1401 buf[4] = 0;
1402 mrbc_string_append_cstr(&ret, buf);
1403 i++;
1404 }
1405 }
1406 }
1407#else
1408 for( int i = 0; i < size; i++ ) {
1409 char buf[10];
1410 mrbc_char_to_s( buf, s + i );
1411 mrbc_string_append_cstr(&ret, buf);
1412 }
1413#endif
1414
1415 mrbc_string_append_cstr(&ret, "\"");
1416
1417 SET_RETURN( ret );
1418}
1419
1420
1421//================================================================
1424static void c_string_ord(mrbc_vm *vm, mrbc_value v[], int argc)
1425{
1426 if( mrbc_string_size(v) == 0 ) {
1427 mrbc_raise(vm, MRBC_CLASS(ArgumentError), "empty string");
1428 return;
1429 }
1430
1431#if MRBC_USE_STRING_UTF8
1432 const uint8_t *s = (const uint8_t *)mrbc_string_cstr(v);
1433 const char *end = mrbc_string_cstr(v) + mrbc_string_size(v);
1434 int char_len = utf8_validated_char_len((const char *)s, end);
1435 mrbc_int_t codepoint;
1436
1437 if( char_len == 1 ) {
1438 codepoint = s[0];
1439 } else if( char_len == 2 ) {
1440 codepoint = ((s[0] & 0x1F) << 6) | (s[1] & 0x3F);
1441 } else if( char_len == 3 ) {
1442 codepoint = ((s[0] & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
1443 } else if( char_len == 4 ) {
1444 codepoint = ((s[0] & 0x07) << 18) | ((s[1] & 0x3F) << 12) |
1445 ((s[2] & 0x3F) << 6) | (s[3] & 0x3F);
1446 } else {
1447 codepoint = s[0]; // fallback for invalid UTF-8
1448 }
1449
1450 SET_INT_RETURN( codepoint );
1451#else
1452 int i = ((uint8_t *)mrbc_string_cstr(v))[0];
1453
1454 SET_INT_RETURN( i );
1455#endif
1456}
1457
1458
1459//================================================================
1462static void c_string_slice_self(mrbc_vm *vm, mrbc_value v[], int argc)
1463{
1464#if MRBC_USE_STRING_UTF8
1465 int target_len = mrbc_string_char_size(mrbc_string_cstr(&v[0]), mrbc_string_size(&v[0]));
1466#else
1467 int target_len = mrbc_string_size(v);
1468#endif
1469 int pos = mrbc_integer(v[1]);
1470 int len;
1471
1472 // in case of slice!(nth) -> String | nil
1473 if( argc == 1 && mrbc_type(v[1]) == MRBC_TT_INTEGER ) {
1474 len = 1;
1475
1476 // in case of slice!(nth, len) -> String | nil
1477 } else if( argc == 2 && mrbc_type(v[1]) == MRBC_TT_INTEGER &&
1478 mrbc_type(v[2]) == MRBC_TT_INTEGER ) {
1479 len = mrbc_integer(v[2]);
1480
1481 // other case
1482 } else {
1483 mrbc_raise( vm, MRBC_CLASS(ArgumentError), 0 );
1484 return;
1485 }
1486
1487 if( pos < 0 ) pos += target_len;
1488 if( pos < 0 ) goto RETURN_NIL;
1489 if( len > (target_len - pos) ) len = target_len - pos;
1490 if( len < 0 ) goto RETURN_NIL;
1491 if( argc == 1 && len <= 0 ) goto RETURN_NIL;
1492
1493#if MRBC_USE_STRING_UTF8
1494 // Convert character position/length to byte position/length
1495 int byte_pos = mrbc_string_chars2bytes(&v[0], 0, pos);
1496 int byte_len = mrbc_string_chars2bytes(&v[0], byte_pos, len);
1497 int byte_size = mrbc_string_size(&v[0]);
1498
1499 mrbc_value ret = mrbc_string_new(vm, mrbc_string_cstr(v) + byte_pos, byte_len);
1500
1501 if( byte_len > 0 ) {
1502 memmove( mrbc_string_cstr(v) + byte_pos, mrbc_string_cstr(v) + byte_pos + byte_len,
1503 byte_size - byte_pos - byte_len + 1 );
1504 v->string->size = byte_size - byte_len;
1505 // shrink suitable size. realloc() may move the block.
1507 }
1508#else
1509 mrbc_value ret = mrbc_string_new(vm, mrbc_string_cstr(v) + pos, len);
1510
1511 if( len > 0 ) {
1512 memmove( mrbc_string_cstr(v) + pos, mrbc_string_cstr(v) + pos + len,
1513 mrbc_string_size(v) - pos - len + 1 );
1514 v->string->size = mrbc_string_size(v) - len;
1515 // shrink suitable size. realloc() may move the block.
1517 }
1518#endif
1519
1520 SET_RETURN(ret);
1521 return; // normal return
1522
1523 RETURN_NIL:
1525}
1526
1527
1528//================================================================
1531static void c_string_split(mrbc_vm *vm, mrbc_value v[], int argc)
1532{
1533 mrbc_value ret = mrbc_array_new(vm, 0);
1534 if( mrbc_string_size(&v[0]) == 0 ) goto DONE;
1535
1536 // check limit parameter.
1537 int limit = 0;
1538 if( argc >= 2 ) {
1539 if( mrbc_type(v[2]) != MRBC_TT_INTEGER ) {
1540 mrbc_raise( vm, MRBC_CLASS(ArgumentError), 0 );
1541 return;
1542 }
1543 limit = v[2].i;
1544 if( limit == 1 ) {
1545 mrbc_array_push( &ret, &v[0] );
1546 mrbc_incref( &v[0] );
1547 goto DONE;
1548 }
1549 }
1550
1551 // check separator parameter.
1552 mrbc_value sep = (argc == 0) ? mrbc_string_new_cstr(vm, " ") : v[1];
1553 switch( mrbc_type(sep) ) {
1554 case MRBC_TT_NIL:
1555 sep = mrbc_string_new_cstr(vm, " ");
1556 break;
1557
1558 case MRBC_TT_STRING:
1559 break;
1560
1561 default:
1562 mrbc_raise( vm, MRBC_CLASS(TypeError), 0 );
1563 return;
1564 }
1565
1566 int flag_strip = (mrbc_string_cstr(&sep)[0] == ' ') &&
1567 (mrbc_string_size(&sep) == 1);
1568 int offset = 0;
1569 int sep_len = mrbc_string_size(&sep);
1570 if( sep_len == 0 ) sep_len++;
1571
1572 while( 1 ) {
1573 int pos, len = 0;
1574
1575 if( flag_strip ) {
1576 for( ; offset < mrbc_string_size(&v[0]); offset++ ) {
1577 if( !is_space( mrbc_string_cstr(&v[0])[offset] )) break;
1578 }
1579 if( offset > mrbc_string_size(&v[0])) break;
1580 }
1581
1582 // check limit
1583 if( limit > 0 && mrbc_array_size(&ret)+1 >= limit ) {
1584 pos = -1;
1585 goto SPLIT_ITEM;
1586 }
1587
1588 // split by space character.
1589 if( flag_strip ) {
1590 pos = offset;
1591 for( ; pos < mrbc_string_size(&v[0]); pos++ ) {
1592 if( is_space( mrbc_string_cstr(&v[0])[pos] )) break;
1593 }
1594 len = pos - offset;
1595 goto SPLIT_ITEM;
1596 }
1597
1598 // split by each character.
1599 if( mrbc_string_size(&sep) == 0 ) {
1600#if MRBC_USE_STRING_UTF8
1601 // Get UTF-8 character length at current offset
1602 int char_len = mrbc_string_utf8_size(mrbc_string_cstr(&v[0]) + offset);
1603 if( char_len == 0 ) char_len = 1; // skip invalid byte
1604 pos = (offset + char_len < mrbc_string_size(&v[0])) ? offset : -1;
1605 len = char_len;
1606 sep_len = char_len; // advance by character length
1607#else
1608 pos = (offset < mrbc_string_size(&v[0])-1) ? offset : -1;
1609 len = 1;
1610#endif
1611 goto SPLIT_ITEM;
1612 }
1613
1614 // split by specified character.
1615 pos = mrbc_string_index( &v[0], &sep, offset );
1616 len = pos - offset;
1617
1618
1619 SPLIT_ITEM:
1620 if( pos < 0 ) len = mrbc_string_size(&v[0]) - offset;
1621
1622 mrbc_value v1 = mrbc_string_new(vm, mrbc_string_cstr(&v[0]) + offset, len);
1623 mrbc_array_push( &ret, &v1 );
1624
1625 if( pos < 0 ) break;
1626 offset = pos + sep_len;
1627 }
1628
1629 // remove trailing empty item
1630 if( limit == 0 ) {
1631 while( 1 ) {
1632 int idx = mrbc_array_size(&ret) - 1;
1633 if( idx < 0 ) break;
1634
1635 mrbc_value v1 = mrbc_array_get( &ret, idx );
1636 if( mrbc_string_size(&v1) != 0 ) break;
1637
1638 mrbc_array_remove(&ret, idx);
1639 mrbc_string_delete( &v1 );
1640 }
1641 }
1642
1643 if( argc == 0 || mrbc_type(v[1]) == MRBC_TT_NIL ) {
1644 mrbc_string_delete(&sep);
1645 }
1646
1647 DONE:
1648 SET_RETURN( ret );
1649}
1650
1651
1652//================================================================
1655static void c_string_lstrip(mrbc_vm *vm, mrbc_value v[], int argc)
1656{
1657 mrbc_value ret = mrbc_string_dup(vm, &v[0]);
1658
1659 mrbc_string_strip(&ret, 0x01); // 1: left side only
1660
1661 SET_RETURN(ret);
1662}
1663
1664
1665//================================================================
1668static void c_string_lstrip_self(mrbc_vm *vm, mrbc_value v[], int argc)
1669{
1670 if( mrbc_string_strip(&v[0], 0x01) == 0 ) { // 1: left side only
1672 }
1673}
1674
1675
1676//================================================================
1679static void c_string_rstrip(mrbc_vm *vm, mrbc_value v[], int argc)
1680{
1681 mrbc_value ret = mrbc_string_dup(vm, &v[0]);
1682
1683 mrbc_string_strip(&ret, 0x02); // 2: right side only
1684
1685 SET_RETURN(ret);
1686}
1687
1688
1689//================================================================
1692static void c_string_rstrip_self(mrbc_vm *vm, mrbc_value v[], int argc)
1693{
1694 if( mrbc_string_strip(&v[0], 0x02) == 0 ) { // 2: right side only
1696 }
1697}
1698
1699
1700//================================================================
1703static void c_string_strip(mrbc_vm *vm, mrbc_value v[], int argc)
1704{
1705 mrbc_value ret = mrbc_string_dup(vm, &v[0]);
1706
1707 mrbc_string_strip(&ret, 0x03); // 3: left and right
1708
1709 SET_RETURN(ret);
1710}
1711
1712
1713//================================================================
1716static void c_string_strip_self(mrbc_vm *vm, mrbc_value v[], int argc)
1717{
1718 if( mrbc_string_strip(&v[0], 0x03) == 0 ) { // 3: left and right
1720 }
1721}
1722
1723
1724//================================================================
1727static void c_string_to_sym(mrbc_vm *vm, mrbc_value v[], int argc)
1728{
1730
1731 SET_RETURN(ret);
1732}
1733
1734
1735//================================================================
1745
1746#if MRBC_USE_STRING_UTF8
1747//================================================================
1748// UTF-8 version of tr - uses codepoints instead of bytes
1749//================================================================
1750
1754static int32_t tr_utf8_decode(const char **str)
1755{
1756 const unsigned char *s = (const unsigned char *)*str;
1757 int32_t codepoint;
1758 int len;
1759
1760 if( s[0] < 0x80 ) {
1761 codepoint = s[0];
1762 len = 1;
1763 } else if( (s[0] & 0xE0) == 0xC0 ) {
1764 codepoint = ((s[0] & 0x1F) << 6) | (s[1] & 0x3F);
1765 len = 2;
1766 } else if( (s[0] & 0xF0) == 0xE0 ) {
1767 codepoint = ((s[0] & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
1768 len = 3;
1769 } else if( (s[0] & 0xF8) == 0xF0 ) {
1770 codepoint = ((s[0] & 0x07) << 18) | ((s[1] & 0x3F) << 12) | ((s[2] & 0x3F) << 6) | (s[3] & 0x3F);
1771 len = 4;
1772 } else {
1773 // Invalid UTF-8, treat as single byte
1774 codepoint = s[0];
1775 len = 1;
1776 }
1777
1778 *str += len;
1779 return codepoint;
1780}
1781
1782// UTF-8 pattern stores codepoints
1783struct tr_pattern_utf8 {
1784 uint8_t type; // 1:in-order, 2:range
1785 uint8_t flag_reverse;
1786 int16_t n; // number of codepoints
1787 struct tr_pattern_utf8 *next;
1788 int32_t codepoints[]; // flexible array of codepoints
1789};
1790
1791static void tr_free_pattern_utf8(struct tr_pattern_utf8 *pat)
1792{
1793 while( pat ) {
1794 struct tr_pattern_utf8 *p = pat->next;
1795 mrbc_raw_free(pat);
1796 pat = p;
1797 }
1798}
1799
1800static struct tr_pattern_utf8 *tr_parse_pattern_utf8(mrbc_vm *vm, const mrbc_value *v_pattern, int flag_reverse_enable)
1801{
1802 const char *pattern = mrbc_string_cstr(v_pattern);
1803 const char *pattern_end = pattern + mrbc_string_size(v_pattern);
1804 int flag_reverse = 0;
1805 struct tr_pattern_utf8 *ret = NULL;
1806 struct tr_pattern_utf8 *last = NULL;
1807
1808 // Check for ^ at start (only treat as negation if followed by at least one char)
1809 if( flag_reverse_enable && pattern < pattern_end && *pattern == '^' && (pattern + 1) < pattern_end ) {
1810 flag_reverse = 1;
1811 pattern++;
1812 }
1813
1814 while( pattern < pattern_end ) {
1815 const char *start = pattern;
1816 int32_t first_cp = tr_utf8_decode(&pattern);
1817
1818 // Check if this is a range pattern (cp1 - cp2)
1819 if( pattern < pattern_end && *pattern == '-' && (pattern + 1) < pattern_end ) {
1820 pattern++; // skip '-'
1821 int32_t second_cp = tr_utf8_decode(&pattern);
1822
1823 // Create range pattern
1824 struct tr_pattern_utf8 *pat1 = mrbc_alloc(vm, sizeof(struct tr_pattern_utf8) + 2 * sizeof(int32_t));
1825
1826 pat1->type = 2; // range
1827 pat1->flag_reverse = flag_reverse;
1828 pat1->n = second_cp - first_cp + 1;
1829 pat1->next = NULL;
1830 pat1->codepoints[0] = first_cp;
1831 pat1->codepoints[1] = second_cp;
1832
1833 // Add to list
1834 if( ret == NULL ) {
1835 ret = last = pat1;
1836 } else {
1837 last->next = pat1;
1838 last = pat1;
1839 }
1840 } else {
1841 // In-order pattern - collect consecutive non-range characters
1842 // First, count how many codepoints until we hit a range or end
1843 const char *scan = pattern;
1844 int count = 1; // we already have first_cp
1845
1846 while( scan < pattern_end ) {
1847 const char *next = scan;
1848 tr_utf8_decode(&next);
1849 // Check if next char starts a range
1850 if( next < pattern_end && *next == '-' && (next + 1) < pattern_end ) {
1851 break; // stop before this char, it's part of a range
1852 }
1853 tr_utf8_decode(&scan);
1854 count++;
1855 }
1856
1857 // Create in-order pattern
1858 struct tr_pattern_utf8 *pat1 = mrbc_alloc(vm, sizeof(struct tr_pattern_utf8) + count * sizeof(int32_t));
1859
1860 pat1->type = 1; // in-order
1861 pat1->flag_reverse = flag_reverse;
1862 pat1->n = count;
1863 pat1->next = NULL;
1864
1865 // Fill in codepoints
1866 const char *p = start;
1867 for( int i = 0; i < count; i++ ) {
1868 pat1->codepoints[i] = tr_utf8_decode(&p);
1869 }
1870 pattern = p;
1871
1872 // Add to list
1873 if( ret == NULL ) {
1874 ret = last = pat1;
1875 } else {
1876 last->next = pat1;
1877 last = pat1;
1878 }
1879 }
1880 }
1881
1882 return ret;
1883}
1884
1885static int tr_find_codepoint(const struct tr_pattern_utf8 *pat, int32_t cp)
1886{
1887 int ret = -1;
1888 int n_sum = 0;
1889 int flag_reverse = pat ? pat->flag_reverse : 0;
1890
1891 while( pat != NULL ) {
1892 if( pat->type == 1 ) { // in-order
1893 for( int i = 0; i < pat->n; i++ ) {
1894 if( pat->codepoints[i] == cp ) ret = n_sum + i;
1895 }
1896 } else { // range
1897 if( pat->codepoints[0] <= cp && cp <= pat->codepoints[1] ) {
1898 ret = n_sum + (cp - pat->codepoints[0]);
1899 }
1900 }
1901 n_sum += pat->n;
1902 pat = pat->next;
1903 }
1904
1905 if( flag_reverse ) {
1906 return (ret < 0) ? INT_MAX : -1;
1907 }
1908 return ret;
1909}
1910
1911static int32_t tr_get_codepoint(const struct tr_pattern_utf8 *pat, int n_th)
1912{
1913 int n_sum = 0;
1914 while( pat != NULL ) {
1915 if( n_th < (n_sum + pat->n) ) {
1916 int i = n_th - n_sum;
1917 return (pat->type == 1) ? pat->codepoints[i] : pat->codepoints[0] + i;
1918 }
1919 if( pat->next == NULL ) {
1920 // Use last character for overflow
1921 return (pat->type == 1) ? pat->codepoints[pat->n - 1] : pat->codepoints[1];
1922 }
1923 n_sum += pat->n;
1924 pat = pat->next;
1925 }
1926 return -1;
1927}
1928
1929static int tr_main_utf8(mrbc_vm *vm, mrbc_value v[], int argc)
1930{
1931 if( !(argc == 2 && mrbc_type(v[1]) == MRBC_TT_STRING &&
1932 mrbc_type(v[2]) == MRBC_TT_STRING) ) {
1933 mrbc_raise(vm, MRBC_CLASS(ArgumentError), 0);
1934 return -1;
1935 }
1936
1937 struct tr_pattern_utf8 *pat = tr_parse_pattern_utf8(vm, &v[1], 1);
1938 if( pat == NULL ) return 0;
1939
1940 struct tr_pattern_utf8 *rep = tr_parse_pattern_utf8(vm, &v[2], 0);
1941
1942 // Build result string using chars approach
1943 mrbc_value result = mrbc_string_new(vm, NULL, 0);
1944 if( result.string == NULL ) {
1945 tr_free_pattern_utf8(pat);
1946 tr_free_pattern_utf8(rep);
1947 return -1;
1948 }
1949
1950 int flag_changed = 0;
1951 const char *s = mrbc_string_cstr(&v[0]);
1952 const char *end = s + mrbc_string_size(&v[0]);
1953
1954 while( s < end ) {
1955 const char *char_start = s;
1956 int32_t cp = tr_utf8_decode(&s);
1957 int char_len = s - char_start;
1958
1959 int n = tr_find_codepoint(pat, cp);
1960 if( n < 0 ) {
1961 // No match, copy original character
1962 mrbc_string_append_cbuf(&result, char_start, char_len);
1963 } else {
1964 flag_changed = 1;
1965 if( rep == NULL ) {
1966 // Delete character (don't append anything)
1967 } else {
1968 // Replace with corresponding character from rep
1969 int32_t new_cp = tr_get_codepoint(rep, n);
1970 char buf[4];
1971 int new_len = mrbc_utf8_encode(new_cp, buf);
1972 mrbc_string_append_cbuf(&result, buf, new_len);
1973 }
1974 }
1975 }
1976
1977 tr_free_pattern_utf8(pat);
1978 tr_free_pattern_utf8(rep);
1979
1980 // Replace original string content with result
1981 mrbc_string *orig = v[0].string;
1982 mrbc_string *res = result.string;
1983
1984 // Swap the data
1985 if( mrbc_string_size(&v[0]) != mrbc_string_size(&result) ) {
1986 // Need to reallocate
1987 orig->data = mrbc_realloc(vm, orig->data, res->size + 1);
1988 }
1989 memcpy(orig->data, res->data, res->size + 1);
1990 orig->size = res->size;
1991
1992 mrbc_decref(&result);
1993 return flag_changed;
1994}
1995#endif // MRBC_USE_STRING_UTF8
1996
1997#if !MRBC_USE_STRING_UTF8
1998//================================================================
1999// Byte-based version of tr (original implementation, used when UTF-8 disabled)
2000//================================================================
2002 uint8_t type; // 1:in-order, 2:range
2004 int16_t n;
2006 char ch[];
2007};
2008
2009static void tr_free_pattern( struct tr_pattern *pat )
2010{
2011 while( pat ) {
2012 struct tr_pattern *p = pat->next;
2013 mrbc_raw_free( pat );
2014 pat = p;
2015 }
2016}
2017
2018static struct tr_pattern * tr_parse_pattern( mrbc_vm *vm, const mrbc_value *v_pattern, int flag_reverse_enable )
2019{
2020 const char *pattern = mrbc_string_cstr( v_pattern );
2021 int pattern_length = mrbc_string_size( v_pattern );
2022 int flag_reverse = 0;
2023 struct tr_pattern *ret = NULL;
2024
2025 int i = 0;
2026 if( flag_reverse_enable && pattern_length >= 2 && pattern[i] == '^' ) {
2027 flag_reverse = 1;
2028 i++;
2029 }
2030
2031 struct tr_pattern *pat1;
2032 while( i < pattern_length ) {
2033 // is range pattern ?
2034 if( (i+2) < pattern_length && pattern[i+1] == '-' ) {
2035 pat1 = mrbc_alloc( vm, sizeof(struct tr_pattern) + 2 );
2036 pat1->type = 2;
2037 pat1->flag_reverse = flag_reverse;
2038 pat1->n = pattern[i+2] - pattern[i] + 1;
2039 pat1->next = NULL;
2040 pat1->ch[0] = pattern[i];
2041 pat1->ch[1] = pattern[i+2];
2042 i += 3;
2043
2044 } else {
2045 // in order pattern.
2046 int start_pos = i++;
2047 while( i < pattern_length ) {
2048 if( (i+2) < pattern_length && pattern[i+1] == '-' ) break;
2049 i++;
2050 }
2051
2052 int len = i - start_pos;
2053 pat1 = mrbc_alloc( vm, sizeof(struct tr_pattern) + len );
2054 pat1->type = 1;
2055 pat1->flag_reverse = flag_reverse;
2056 pat1->n = len;
2057 pat1->next = NULL;
2058 memcpy( pat1->ch, &pattern[start_pos], len );
2059 }
2060
2061 // connect linked list.
2062 if( ret == NULL ) {
2063 ret = pat1;
2064 } else {
2065 struct tr_pattern *p = ret;
2066 while( p->next != NULL ) { p = p->next; }
2067 p->next = pat1;
2068 }
2069 }
2070
2071 return ret;
2072}
2073
2074static int tr_find_character( const struct tr_pattern *pat, int ch )
2075{
2076 int ret = -1;
2077 int n_sum = 0;
2078 int flag_reverse = pat ? pat->flag_reverse : 0;
2079
2080 while( pat != NULL ) {
2081 if( pat->type == 1 ) { // in-order
2082 for( int i = 0; i < pat->n; i++ ) {
2083 if( pat->ch[i] == ch ) ret = n_sum + i;
2084 }
2085 } else { // pat->type == 2 range
2086 if( pat->ch[0] <= ch && ch <= pat->ch[1] ) ret = n_sum + ch - pat->ch[0];
2087 }
2088 n_sum += pat->n;
2089 pat = pat->next;
2090 }
2091
2092 if( flag_reverse ) {
2093 return (ret < 0) ? INT_MAX : -1;
2094 }
2095 return ret;
2096}
2097
2098static int tr_get_character( const struct tr_pattern *pat, int n_th )
2099{
2100 int n_sum = 0;
2101 while( pat != NULL ) {
2102 if( n_th < (n_sum + pat->n) ) {
2103 int i = (n_th - n_sum);
2104 return (pat->type == 1) ? pat->ch[i] :pat->ch[0] + i;
2105 }
2106 if( pat->next == NULL ) {
2107 return (pat->type == 1) ? pat->ch[pat->n - 1] : pat->ch[1];
2108 }
2109 n_sum += pat->n;
2110 pat = pat->next;
2111 }
2112
2113 return -1;
2114}
2115
2116static int tr_main( mrbc_vm *vm, mrbc_value v[], int argc )
2117{
2118 if( !(argc == 2 && mrbc_type(v[1]) == MRBC_TT_STRING &&
2119 mrbc_type(v[2]) == MRBC_TT_STRING)) {
2120 mrbc_raise( vm, MRBC_CLASS(ArgumentError), 0 );
2121 return -1;
2122 }
2123
2124 struct tr_pattern *pat = tr_parse_pattern( vm, &v[1], 1 );
2125 if( pat == NULL ) return 0;
2126
2127 struct tr_pattern *rep = tr_parse_pattern( vm, &v[2], 0 );
2128
2129 int flag_changed = 0;
2130 char *s = mrbc_string_cstr( &v[0] );
2131 int len = mrbc_string_size( &v[0] );
2132
2133 for( int i = 0; i < len; i++ ) {
2134 int n = tr_find_character( pat, s[i] );
2135 if( n < 0 ) continue;
2136
2137 flag_changed = 1;
2138 if( rep == NULL ) {
2139 memmove( s + i, s + i + 1, len - i );
2140 len--;
2141 i--;
2142 } else {
2143 s[i] = tr_get_character( rep, n );
2144 }
2145 }
2146
2147 tr_free_pattern( pat );
2148 tr_free_pattern( rep );
2149
2150 v[0].string->size = len;
2151 v[0].string->data[len] = 0;
2152
2153 return flag_changed;
2154}
2155#endif // !MRBC_USE_STRING_UTF8
2156
2157static void c_string_tr(mrbc_vm *vm, mrbc_value v[], int argc)
2158{
2159 mrbc_value ret = mrbc_string_dup( vm, &v[0] );
2160 SET_RETURN( ret );
2161#if MRBC_USE_STRING_UTF8
2162 tr_main_utf8(vm, v, argc);
2163#else
2164 tr_main(vm, v, argc);
2165#endif
2166}
2167
2168
2169//================================================================
2172static void c_string_tr_self(mrbc_vm *vm, mrbc_value v[], int argc)
2173{
2174#if MRBC_USE_STRING_UTF8
2175 int flag_changed = tr_main_utf8(vm, v, argc);
2176#else
2177 int flag_changed = tr_main(vm, v, argc);
2178#endif
2179
2180 if( !flag_changed ) {
2182 }
2183}
2184
2185
2186//================================================================
2189static void c_string_start_with(mrbc_vm *vm, mrbc_value v[], int argc)
2190{
2191 if( !(argc == 1 && mrbc_type(v[1]) == MRBC_TT_STRING)) {
2192 mrbc_raise( vm, MRBC_CLASS(ArgumentError), 0 );
2193 return;
2194 }
2195
2196 int ret;
2197 if( mrbc_string_size(&v[0]) < mrbc_string_size(&v[1]) ) {
2198 ret = 0;
2199 } else {
2200 ret = (memcmp( mrbc_string_cstr(&v[0]), mrbc_string_cstr(&v[1]),
2201 mrbc_string_size(&v[1]) ) == 0);
2202 }
2203
2204 SET_BOOL_RETURN(ret);
2205}
2206
2207
2208//================================================================
2211static void c_string_end_with(mrbc_vm *vm, mrbc_value v[], int argc)
2212{
2213 if( !(argc == 1 && mrbc_type(v[1]) == MRBC_TT_STRING)) {
2214 mrbc_raise( vm, MRBC_CLASS(ArgumentError), 0 );
2215 return;
2216 }
2217
2218 int ret;
2219 int offset = mrbc_string_size(&v[0]) - mrbc_string_size(&v[1]);
2220 if( offset < 0 ) {
2221 ret = 0;
2222 } else {
2223 ret = (memcmp( mrbc_string_cstr(&v[0]) + offset, mrbc_string_cstr(&v[1]),
2224 mrbc_string_size(&v[1]) ) == 0);
2225 }
2226
2227 SET_BOOL_RETURN(ret);
2228}
2229
2230
2231//================================================================
2234static void c_string_include(mrbc_vm *vm, mrbc_value v[], int argc)
2235{
2236 if( !(argc == 1 && mrbc_type(v[1]) == MRBC_TT_STRING)) {
2237 mrbc_raise( vm, MRBC_CLASS(ArgumentError), 0 );
2238 return;
2239 }
2240
2241 int ret = mrbc_string_index( &v[0], &v[1], 0 );
2242 SET_BOOL_RETURN(ret >= 0);
2243}
2244
2245
2246//================================================================
2249static void c_string_bytes(mrbc_vm *vm, mrbc_value v[], int argc)
2250{
2251 /*
2252 * Note: This String#bytes doesn't support taking a block parameter.
2253 * Use String#each_byte instead.
2254 */
2255 int len = mrbc_string_size(&v[0]);
2256 mrbc_value ret = mrbc_array_new(vm, len);
2257
2258 for( int i = 0; i < len; i++ ) {
2259 mrbc_array_set(&ret, i, &mrbc_integer_value(v[0].string->data[i]));
2260 }
2261 SET_RETURN(ret);
2262}
2263
2264
2265//================================================================
2268static void c_string_upcase(mrbc_vm *vm, mrbc_value v[], int argc)
2269{
2270 mrbc_value ret = mrbc_string_dup(vm, &v[0]);
2271 mrbc_string_upcase(&ret);
2272 SET_RETURN(ret);
2273}
2274
2275//================================================================
2278static void c_string_upcase_self(mrbc_vm *vm, mrbc_value v[], int argc)
2279{
2280 if (mrbc_string_upcase(&v[0]) == 0) {
2282 }
2283}
2284
2285
2286//================================================================
2289static void c_string_downcase(mrbc_vm *vm, mrbc_value v[], int argc)
2290{
2291 mrbc_value ret = mrbc_string_dup(vm, &v[0]);
2293 SET_RETURN(ret);
2294}
2295
2296
2297//================================================================
2300static void c_string_downcase_self(mrbc_vm *vm, mrbc_value v[], int argc)
2301{
2302 if (mrbc_string_downcase(&v[0]) == 0) {
2304 }
2305}
2306
2307
2308#if MRBC_USE_STRING_UTF8
2309//================================================================
2312static void c_string_encoding(mrbc_vm *vm, mrbc_value v[], int argc)
2313{
2314 mrbc_value ret = mrbc_string_new_cstr(vm, "UTF-8");
2315 SET_RETURN(ret);
2316}
2317
2318
2319//================================================================
2322static void c_string_valid_encoding(mrbc_vm *vm, mrbc_value v[], int argc)
2323{
2324 const uint8_t *s = (const uint8_t *)mrbc_string_cstr(&v[0]);
2325 int len = mrbc_string_size(&v[0]);
2326 int i = 0;
2327
2328 while( i < len ) {
2329 int char_len = mrbc_string_utf8_size((const char *)&s[i]);
2330 if( char_len == 0 ) {
2331 // Invalid leading byte or continuation byte at start
2332 SET_BOOL_RETURN(0);
2333 return;
2334 }
2335 if( i + char_len > len ) {
2336 // Truncated sequence
2337 SET_BOOL_RETURN(0);
2338 return;
2339 }
2340 // Verify continuation bytes
2341 for( int j = 1; j < char_len; j++ ) {
2342 if( (s[i + j] & 0xC0) != 0x80 ) {
2343 SET_BOOL_RETURN(0);
2344 return;
2345 }
2346 }
2347 i += char_len;
2348 }
2349
2350 SET_BOOL_RETURN(1);
2351}
2352
2353
2354//================================================================
2357static void c_string_ascii_only(mrbc_vm *vm, mrbc_value v[], int argc)
2358{
2359 const uint8_t *s = (const uint8_t *)mrbc_string_cstr(&v[0]);
2360 int len = mrbc_string_size(&v[0]);
2361
2362 for( int i = 0; i < len; i++ ) {
2363 if( s[i] > 0x7F ) {
2364 SET_BOOL_RETURN(0);
2365 return;
2366 }
2367 }
2368
2369 SET_BOOL_RETURN(1);
2370}
2371
2372
2373//================================================================
2376static void c_string_chars(mrbc_vm *vm, mrbc_value v[], int argc)
2377{
2378 const uint8_t *s = (const uint8_t *)mrbc_string_cstr(&v[0]);
2379 int len = mrbc_string_size(&v[0]);
2380 int char_count = mrbc_string_char_size((const char *)s, len);
2381 mrbc_value ret = mrbc_array_new(vm, char_count);
2382
2383 int i = 0;
2384 int idx = 0;
2385 while( i < len ) {
2386 int char_len = mrbc_string_utf8_size((const char *)&s[i]);
2387 if( char_len == 0 ) char_len = 1; // skip invalid byte
2388 if( i + char_len > len ) char_len = len - i;
2389
2390 mrbc_value ch = mrbc_string_new(vm, &s[i], char_len);
2391 mrbc_array_set(&ret, idx++, &ch);
2392 i += char_len;
2393 }
2394
2395 SET_RETURN(ret);
2396}
2397
2398
2399//================================================================
2402static void c_string_reverse(mrbc_vm *vm, mrbc_value v[], int argc)
2403{
2404 const uint8_t *s = (const uint8_t *)mrbc_string_cstr(&v[0]);
2405 int len = mrbc_string_size(&v[0]);
2406
2407 // Allocate result buffer (same byte length as original)
2408 mrbc_value ret = mrbc_string_new(vm, NULL, len);
2409 if( ret.string == NULL ) return;
2410 uint8_t *dst = ret.string->data;
2411
2412 // Find all character boundaries first
2413 int char_count = mrbc_string_char_size((const char *)s, len);
2414 int *offsets = mrbc_raw_alloc(sizeof(int) * (char_count + 1));
2415 if( offsets == NULL ) {
2416 mrbc_decref(&ret);
2417 return;
2418 }
2419
2420 int i = 0;
2421 int idx = 0;
2422 while( i < len ) {
2423 offsets[idx++] = i;
2424 int char_len = mrbc_string_utf8_size((const char *)&s[i]);
2425 if( char_len == 0 ) char_len = 1;
2426 i += char_len;
2427 }
2428 offsets[idx] = len;
2429
2430 // Copy characters in reverse order
2431 int dst_pos = 0;
2432 for( i = char_count - 1; i >= 0; i-- ) {
2433 int char_start = offsets[i];
2434 int char_len = offsets[i + 1] - char_start;
2435 memcpy(dst + dst_pos, s + char_start, char_len);
2436 dst_pos += char_len;
2437 }
2438 dst[len] = '\0';
2439
2440 mrbc_raw_free(offsets);
2441 SET_RETURN(ret);
2442}
2443
2444
2445//================================================================
2448static void c_string_reverse_self(mrbc_vm *vm, mrbc_value v[], int argc)
2449{
2450 const uint8_t *s = (const uint8_t *)mrbc_string_cstr(&v[0]);
2451 int len = mrbc_string_size(&v[0]);
2452
2453 // Find all character boundaries
2454 int char_count = mrbc_string_char_size((const char *)s, len);
2455 int *offsets = mrbc_raw_alloc(sizeof(int) * (char_count + 1));
2456 if( offsets == NULL ) return;
2457
2458 int i = 0;
2459 int idx = 0;
2460 while( i < len ) {
2461 offsets[idx++] = i;
2462 int char_len = mrbc_string_utf8_size((const char *)&s[i]);
2463 if( char_len == 0 ) char_len = 1;
2464 i += char_len;
2465 }
2466 offsets[idx] = len;
2467
2468 // Create temp buffer and copy reversed
2469 uint8_t *tmp = mrbc_raw_alloc(len);
2470 if( tmp == NULL ) {
2471 mrbc_raw_free(offsets);
2472 return;
2473 }
2474
2475 int dst_pos = 0;
2476 for( i = char_count - 1; i >= 0; i-- ) {
2477 int char_start = offsets[i];
2478 int char_len = offsets[i + 1] - char_start;
2479 memcpy(tmp + dst_pos, s + char_start, char_len);
2480 dst_pos += char_len;
2481 }
2482
2483 // Copy back to original
2484 memcpy(v[0].string->data, tmp, len);
2485
2486 mrbc_raw_free(tmp);
2487 mrbc_raw_free(offsets);
2488}
2489#endif // MRBC_USE_STRING_UTF8
2490
2491
2492/* MRBC_AUTOGEN_METHOD_TABLE
2493
2494 CLASS("String")
2495 FILE("_autogen_class_string.h")
2496
2497 METHOD( "new", c_string_new )
2498 METHOD( "+", c_string_add )
2499 METHOD( "*", c_string_mul )
2500 METHOD( "size", c_string_size )
2501 METHOD( "length", c_string_size )
2502 METHOD( "bytesize", c_string_bytesize )
2503 METHOD( "to_i", c_string_to_i )
2504 METHOD( "to_s", c_string_to_s )
2505 METHOD( "<<", c_string_append )
2506 METHOD( "[]", c_string_slice )
2507 METHOD( "[]=", c_string_insert )
2508 METHOD( "b", c_ineffect )
2509 METHOD( "clear", c_string_clear )
2510 METHOD( "chomp", c_string_chomp )
2511 METHOD( "chomp!", c_string_chomp_self )
2512 METHOD( "dup", c_string_dup )
2513 METHOD( "empty?", c_string_empty )
2514 METHOD( "getbyte", c_string_getbyte )
2515 METHOD( "setbyte", c_string_setbyte )
2516 METHOD( "index", c_string_index )
2517 METHOD( "inspect", c_string_inspect )
2518 METHOD( "ord", c_string_ord )
2519 METHOD( "byteslice", c_string_byteslice )
2520 METHOD( "slice", c_string_slice )
2521 METHOD( "slice!", c_string_slice_self )
2522 METHOD( "split", c_string_split )
2523 METHOD( "lstrip", c_string_lstrip )
2524 METHOD( "lstrip!", c_string_lstrip_self )
2525 METHOD( "rstrip", c_string_rstrip )
2526 METHOD( "rstrip!", c_string_rstrip_self )
2527 METHOD( "strip", c_string_strip )
2528 METHOD( "strip!", c_string_strip_self )
2529 METHOD( "to_sym", c_string_to_sym )
2530 METHOD( "intern", c_string_to_sym )
2531 METHOD( "tr", c_string_tr )
2532 METHOD( "tr!", c_string_tr_self )
2533 METHOD( "start_with?", c_string_start_with )
2534 METHOD( "end_with?", c_string_end_with )
2535 METHOD( "include?", c_string_include )
2536 METHOD( "bytes", c_string_bytes )
2537 METHOD( "upcase", c_string_upcase )
2538 METHOD( "upcase!", c_string_upcase_self )
2539 METHOD( "downcase", c_string_downcase )
2540 METHOD( "downcase!", c_string_downcase_self )
2541
2542#if MRBC_USE_FLOAT
2543 METHOD( "to_f", c_string_to_f )
2544#endif
2545
2546#if MRBC_USE_STRING_UTF8
2547 METHOD( "encoding", c_string_encoding )
2548 METHOD( "valid_encoding?", c_string_valid_encoding )
2549 METHOD( "ascii_only?", c_string_ascii_only )
2550 METHOD( "chars", c_string_chars )
2551 METHOD( "reverse", c_string_reverse )
2552 METHOD( "reverse!", c_string_reverse_self )
2553#endif
2554*/
2555#include "_autogen_class_string.h"
2556
2557
2558#endif // MRBC_USE_STRING
void * mrbc_raw_alloc(unsigned int size)
Definition alloc.c:514
void * mrbc_raw_realloc(void *ptr, unsigned int size)
Definition alloc.c:806
void mrbc_raw_free(void *ptr)
Definition alloc.c:707
#define mrbc_immediate_value(...)
Definition boxing_no.h:75
#define mrbc_nil_value()
Definition boxing_no.h:70
static void mrbc_set_tt(mrbc_value *p, mrbc_vtype type)
Definition boxing_no.h:125
#define mrbc_type(o)
Definition boxing_no.h:57
struct RObject mrbc_value
Value object. Default version.
#define mrbc_integer(o)
Definition boxing_no.h:58
#define mrbc_integer_value(n)
Definition boxing_no.h:66
int mrbc_array_push(mrbc_value *ary, mrbc_value *set_val)
Definition c_array.c:243
int mrbc_array_set(mrbc_value *ary, int idx, mrbc_value *set_val)
Definition c_array.c:169
mrbc_value mrbc_array_get(const mrbc_value *ary, int idx)
Definition c_array.c:207
mrbc_value mrbc_array_new(mrbc_vm *vm, int size)
Definition c_array.c:83
mrbc_value mrbc_array_remove(mrbc_value *ary, int idx)
Definition c_array.c:386
static int mrbc_array_size(const mrbc_value *ary)
Definition c_array.h:84
void mrbc_object_inspect(mrbc_vm *vm, mrbc_value v[], int argc)
Definition c_object.c:91
static mrbc_value * mrbc_range_last_p(const mrbc_value *v)
Definition c_range.h:90
static int mrbc_range_exclude_end(const mrbc_value *v)
Definition c_range.h:98
static mrbc_value * mrbc_range_first_p(const mrbc_value *v)
Definition c_range.h:74
int mrbc_string_index(const mrbc_value *src, const mrbc_value *pattern, int offset)
Definition c_string.c:240
static int string_slice_parse(mrbc_vm *vm, mrbc_value v[], int argc, int target_len, int *pos, int *len)
Definition c_string.c:964
void mrbc_string_delete(mrbc_value *str)
Definition c_string.c:107
static void tr_free_pattern(struct tr_pattern *pat)
Definition c_string.c:2009
static int tr_get_character(const struct tr_pattern *pat, int n_th)
Definition c_string.c:2098
static int tr_main(mrbc_vm *vm, mrbc_value v[], int argc)
Definition c_string.c:2116
static struct tr_pattern * tr_parse_pattern(mrbc_vm *vm, const mrbc_value *v_pattern, int flag_reverse_enable)
Definition c_string.c:2018
static int tr_find_character(const struct tr_pattern *pat, int ch)
Definition c_string.c:2074
mrbc_value mrbc_string_new(mrbc_vm *vm, const void *src, int len)
Definition c_string.c:64
mrbc_value mrbc_string_new_alloc(mrbc_vm *vm, void *buf, int len)
Definition c_string.c:88
static int is_space(int ch)
Definition c_string.c:44
void mrbc_string_clear(mrbc_value *str)
Definition c_string.c:118
mrbc_value mrbc_string_dup(mrbc_vm *vm, mrbc_value *s1)
Definition c_string.c:146
mrbc_value mrbc_string_add(mrbc_vm *vm, const mrbc_value *s1, const mrbc_value *s2)
Definition c_string.c:165
int mrbc_string_chomp(mrbc_value *src)
Definition c_string.c:307
int mrbc_string_append_cbuf(mrbc_value *s1, const void *s2, int len2)
Definition c_string.c:213
int mrbc_string_upcase(mrbc_value *str)
Definition c_string.c:337
int mrbc_string_downcase(mrbc_value *str)
Definition c_string.c:359
int mrbc_string_append(mrbc_value *s1, const mrbc_value *s2)
Definition c_string.c:185
int mrbc_string_strip(mrbc_value *src, int mode)
Definition c_string.c:265
struct RString mrbc_string
String object.
static char * mrbc_string_cstr(const mrbc_value *v)
Definition c_string.h:122
static int mrbc_string_append_cstr(mrbc_value *s1, const char *s2)
Definition c_string.h:134
static mrbc_value mrbc_string_new_cstr(mrbc_vm *vm, const char *src)
Definition c_string.h:91
static int mrbc_string_size(const mrbc_value *str)
Definition c_string.h:114
#define MRBC_CLASS(cls)
Definition class.h:55
void mrbc_char_to_s(char *buf, const void *src)
Definition console.c:359
void mrbc_raise(struct VM *vm, struct RClass *exc_cls, const char *msg)
Definition error.c:145
void mrbc_raisef(struct VM *vm, struct RClass *exc_cls, const char *fstr,...)
Definition error.c:168
Include at once the necessary header files.
struct RString * string
Definition boxing_no.h:32
mrbc_int_t i
Definition boxing_no.h:22
uint8_t * data
pointer to allocated buffer.
Definition c_string.h:53
MRBC_STRING_SIZE_T size
string length.
Definition c_string.h:52
int16_t n
Definition c_string.c:2004
uint8_t flag_reverse
Definition c_string.c:2003
char ch[]
Definition c_string.c:2006
struct tr_pattern * next
Definition c_string.c:2005
uint8_t type
Definition c_string.c:2002
mrbc_value mrbc_symbol_new(struct VM *vm, const char *str)
Definition symbol.c:335
mrbc_int_t mrbc_atoi(const char *s, int base)
Definition value.c:188
float mrbc_float_t
Definition value.h:52
int32_t mrbc_int_t
Definition value.h:47
static void mrbc_decref(mrbc_value *v)
Definition value.h:572
#define MRBC_INIT_OBJECT_HEADER_DI(t)
Definition value.h:147
#define SET_BOOL_RETURN(n)
Definition value.h:238
#define SET_INT_RETURN(n)
Definition value.h:243
#define SET_NIL_RETURN()
Definition value.h:226
static void mrbc_incref(mrbc_value *v)
Definition value.h:557
@ MRBC_TT_STRING
String.
Definition value.h:98
@ MRBC_TT_INTEGER
Integer.
Definition value.h:86
@ MRBC_TT_EMPTY
Definition value.h:80
@ MRBC_TT_RANGE
Range.
Definition value.h:99
@ MRBC_TT_NIL
NilClass.
Definition value.h:81
@ MRBC_TT_CLASS
Class.
Definition value.h:90
#define SET_RETURN(n)
Definition value.h:221
#define SET_FLOAT_RETURN(n)
Definition value.h:248
struct VM mrbc_vm
Virtual Machine.
Global configuration of mruby/c VM's.