mruby/c VM Source Code release 4.0.0
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 mrbc_raw_realloc(str->string->data, 1);
121 str->string->data[0] = '\0';
122 str->string->size = 0;
123}
124
125
126#if defined(MRBC_ALLOC_VMID)
127//================================================================
130void mrbc_string_clear_vm_id(mrbc_value *str)
131{
132 mrbc_set_vm_id( str->string, 0 );
133 mrbc_set_vm_id( str->string->data, 0 );
134}
135#endif
136
137
138//================================================================
146{
147 mrbc_string *h1 = s1->string;
148 mrbc_value value = mrbc_string_new(vm, NULL, h1->size);
149
150 memcpy( value.string->data, h1->data, h1->size + 1 );
151
152 return value;
153}
154
155
156//================================================================
165{
166 mrbc_string *h1 = s1->string;
167 mrbc_string *h2 = s2->string;
168 mrbc_value value = mrbc_string_new(vm, NULL, h1->size + h2->size);
169
170 memcpy( value.string->data, h1->data, h1->size );
171 memcpy( value.string->data + h1->size, h2->data, h2->size + 1 );
172
173 return value;
174}
175
176
177//================================================================
185{
186 int len1 = s1->string->size;
187 int len2 = (mrbc_type(*s2) == MRBC_TT_STRING) ? s2->string->size : 1;
188 uint8_t *str = mrbc_raw_realloc(s1->string->data, len1+len2+1);
189
190 if( mrbc_type(*s2) == MRBC_TT_STRING ) {
191 memcpy(str + len1, s2->string->data, len2 + 1);
192 } else if( mrbc_type(*s2) == MRBC_TT_INTEGER ) {
193 str[len1] = s2->i;
194 str[len1+1] = '\0';
195 }
196
197 s1->string->size = len1 + len2;
198 s1->string->data = str;
199
200 return 0;
201}
202
203
204//================================================================
212int mrbc_string_append_cbuf(mrbc_value *s1, const void *s2, int len2)
213{
214 int len1 = s1->string->size;
215 uint8_t *str = mrbc_raw_realloc(s1->string->data, len1+len2+1);
216
217 if( s2 ) {
218 memcpy(str + len1, s2, len2);
219 str[len1 + len2] = 0;
220 } else {
221 memset(str + len1, 0, len2 + 1);
222 }
223
224 s1->string->size = len1 + len2;
225 s1->string->data = str;
226
227 return 0;
228}
229
230
231//================================================================
239int mrbc_string_index(const mrbc_value *src, const mrbc_value *pattern, int offset)
240{
241 char *p1 = mrbc_string_cstr(src) + offset;
242 char *p2 = mrbc_string_cstr(pattern);
243 int try_cnt = mrbc_string_size(src) - mrbc_string_size(pattern) - offset;
244
245 while( try_cnt >= 0 ) {
246 if( memcmp( p1, p2, mrbc_string_size(pattern) ) == 0 ) {
247 return p1 - mrbc_string_cstr(src); // matched.
248 }
249 try_cnt--;
250 p1++;
251 }
252
253 return -1;
254}
255
256
257//================================================================
264int mrbc_string_strip(mrbc_value *src, int mode)
265{
266 char *p1 = mrbc_string_cstr(src);
267 char *p2 = p1 + mrbc_string_size(src) - 1;
268
269 // left-side
270 if( mode & 0x01 ) {
271 while( p1 <= p2 ) {
272 if( *p1 == '\0' ) break;
273 if( !is_space(*p1) ) break;
274 p1++;
275 }
276 }
277 // right-side
278 if( mode & 0x02 ) {
279 while( p1 <= p2 ) {
280 if( !is_space(*p2) ) break;
281 p2--;
282 }
283 }
284
285 int new_size = p2 - p1 + 1;
286 if( mrbc_string_size(src) == new_size ) return 0;
287
288 char *buf = mrbc_string_cstr(src);
289 if( p1 != buf ) memmove( buf, p1, new_size );
290 buf[new_size] = '\0';
291 mrbc_raw_realloc(buf, new_size+1); // shrink suitable size.
292 src->string->size = new_size;
293
294 return 1;
295}
296
297
298//================================================================
305{
306 char *p1 = mrbc_string_cstr(src);
307 char *p2 = p1 + mrbc_string_size(src) - 1;
308
309 if( *p2 == '\n' ) {
310 p2--;
311 }
312 if( *p2 == '\r' ) {
313 p2--;
314 }
315
316 int new_size = p2 - p1 + 1;
317 if( mrbc_string_size(src) == new_size ) return 0;
318
319 char *buf = mrbc_string_cstr(src);
320 buf[new_size] = '\0';
321 src->string->size = new_size;
322
323 return 1;
324}
325
326
327#if !MRBC_USE_STRING_UTF8 || !MRBC_USE_UNICODE_CASE
328//================================================================
335{
336 int len = str->string->size;
337 int count = 0;
338 uint8_t *data = str->string->data;
339 while (len != 0) {
340 len--;
341 if ('a' <= data[len] && data[len] <= 'z') {
342 data[len] = data[len] - ('a' - 'A');
343 count++;
344 }
345 }
346 return count;
347}
348
349
350//================================================================
357{
358 int len = str->string->size;
359 int count = 0;
360 uint8_t *data = str->string->data;
361 while (len != 0) {
362 len--;
363 if ('A' <= data[len] && data[len] <= 'Z') {
364 data[len] = data[len] + ('a' - 'A');
365 count++;
366 }
367 }
368 return count;
369}
370#endif /* !MRBC_USE_STRING_UTF8 || !MRBC_USE_UNICODE_CASE */
371
372
373#if MRBC_USE_STRING_UTF8
374//================================================================
380int mrbc_string_utf8_size(const char *str)
381{
382 unsigned char c = (unsigned char)*str;
383
384 if( (c & 0x80) == 0x00 ) return 1; // ASCII: 0xxxxxxx
385 if( (c & 0xE0) == 0xC0 ) return 2; // 2-byte: 110xxxxx
386 if( (c & 0xF0) == 0xE0 ) return 3; // 3-byte: 1110xxxx
387 if( (c & 0xF8) == 0xF0 ) return 4; // 4-byte: 11110xxx
388 return 0; // continuation or invalid
389}
390
391
392//================================================================
404static int utf8_validated_char_len(const char *str, const char *end)
405{
406 int char_len = mrbc_string_utf8_size(str);
407
408 if( char_len == 0 ) return 1; // isolated continuation byte
409 if( str + char_len > end ) return 1; // incomplete sequence at end
410
411 for( int j = 1; j < char_len; j++ ) {
412 if( ((unsigned char)str[j] & 0xC0) != 0x80 ) return 1; // invalid continuation
413 }
414 return char_len;
415}
416
417
418//================================================================
429int mrbc_string_char_size(const char *str, int len)
430{
431 int count = 0;
432 const char *end = str + len;
433
434 while( str < end ) {
435 int char_len = utf8_validated_char_len(str, end);
436 count++;
437 str += char_len;
438 }
439 return count;
440}
441
442
443//================================================================
451int mrbc_string_chars2bytes(mrbc_value *src, int off, int idx)
452{
453 const char *str = mrbc_string_cstr(src) + off;
454 const char *end = mrbc_string_cstr(src) + src->string->size;
455 int bytes = 0;
456
457 for( int i = 0; i < idx && str < end; i++ ) {
458 int char_len = utf8_validated_char_len(str, end);
459 bytes += char_len;
460 str += char_len;
461 }
462 return bytes;
463}
464
465
466//================================================================
473int mrbc_string_bytes2chars(const mrbc_value *src, int byte_index)
474{
475 const char *str = mrbc_string_cstr(src);
476 const char *target = str + byte_index;
477 int count = 0;
478
479 while( str < target ) {
480 int char_len = mrbc_string_utf8_size(str);
481 if( char_len == 0 ) char_len = 1;
482 str += char_len;
483 count++;
484 }
485 return (str == target) ? count : -1;
486}
487
488
489#if MRBC_USE_UNICODE_CASE
490//================================================================
497static int32_t unicode_decode_utf8(const uint8_t *str, int *len_out)
498{
499 uint8_t c = str[0];
500 int32_t cp;
501 int len;
502
503 if( (c & 0x80) == 0 ) {
504 cp = c;
505 len = 1;
506 } else if( (c & 0xE0) == 0xC0 ) {
507 cp = (c & 0x1F) << 6 | (str[1] & 0x3F);
508 len = 2;
509 } else if( (c & 0xF0) == 0xE0 ) {
510 cp = (c & 0x0F) << 12 | (str[1] & 0x3F) << 6 | (str[2] & 0x3F);
511 len = 3;
512 } else if( (c & 0xF8) == 0xF0 ) {
513 cp = (c & 0x07) << 18 | (str[1] & 0x3F) << 12 | (str[2] & 0x3F) << 6 | (str[3] & 0x3F);
514 len = 4;
515 } else {
516 *len_out = 1;
517 return -1;
518 }
519 *len_out = len;
520 return cp;
521}
522
523
524//================================================================
531static int unicode_encode_utf8(int32_t cp, uint8_t *buf)
532{
533 if( cp < 0x80 ) {
534 buf[0] = cp;
535 return 1;
536 } else if( cp < 0x800 ) {
537 buf[0] = 0xC0 | (cp >> 6);
538 buf[1] = 0x80 | (cp & 0x3F);
539 return 2;
540 } else if( cp < 0x10000 ) {
541 buf[0] = 0xE0 | (cp >> 12);
542 buf[1] = 0x80 | ((cp >> 6) & 0x3F);
543 buf[2] = 0x80 | (cp & 0x3F);
544 return 3;
545 } else {
546 buf[0] = 0xF0 | (cp >> 18);
547 buf[1] = 0x80 | ((cp >> 12) & 0x3F);
548 buf[2] = 0x80 | ((cp >> 6) & 0x3F);
549 buf[3] = 0x80 | (cp & 0x3F);
550 return 4;
551 }
552}
553
554
555//================================================================
563static int32_t unicode_case_lookup_range(int32_t cp, const case_range_t *ranges, int count)
564{
565 for( int i = 0; i < count; i++ ) {
566 if( ranges[i].start <= cp && cp <= ranges[i].end ) {
567 // Check if this character matches the XOR pattern
568 int32_t converted = cp ^ ranges[i].xor_val;
569 // Verify the conversion is in the expected direction
570 // For upcase: converted should be < cp (or same block)
571 // For downcase: converted should be > cp (or same block)
572 return converted;
573 }
574 }
575 return cp; // Not found in ranges
576}
577
578
579//================================================================
587static int32_t unicode_case_lookup_exception(int32_t cp, const case_exception_t *exceptions, int count)
588{
589 int low = 0, high = count - 1;
590
591 while( low <= high ) {
592 int mid = (low + high) / 2;
593 if( exceptions[mid].from == cp ) {
594 return exceptions[mid].to;
595 } else if( exceptions[mid].from < cp ) {
596 low = mid + 1;
597 } else {
598 high = mid - 1;
599 }
600 }
601 return cp; // Not found
602}
603
604
605//================================================================
611static int32_t unicode_upcase_codepoint(int32_t cp)
612{
613 if( cp < 0 || cp > 0xFFFF ) return cp; // BMP only
614
615 // Try ranges first (more common patterns)
616 int32_t result = unicode_case_lookup_range(cp, upcase_ranges, UPCASE_RANGES_COUNT);
617 if( result != cp ) return result;
618
619 // Fall back to exceptions
620 return unicode_case_lookup_exception(cp, upcase_exceptions, UPCASE_EXCEPTIONS_COUNT);
621}
622
623
624//================================================================
630static int32_t unicode_downcase_codepoint(int32_t cp)
631{
632 if( cp < 0 || cp > 0xFFFF ) return cp; // BMP only
633
634 // Try ranges first
635 int32_t result = unicode_case_lookup_range(cp, downcase_ranges, DOWNCASE_RANGES_COUNT);
636 if( result != cp ) return result;
637
638 // Fall back to exceptions
639 return unicode_case_lookup_exception(cp, downcase_exceptions, DOWNCASE_EXCEPTIONS_COUNT);
640}
641
642
643//================================================================
650{
651 int len = str->string->size;
652 uint8_t *data = str->string->data;
653 int count = 0;
654
655 // First pass: check if any conversion changes byte length
656 int new_len = 0;
657 int needs_realloc = 0;
658 for( int i = 0; i < len; ) {
659 int char_len;
660 int32_t cp = unicode_decode_utf8(data + i, &char_len);
661 int32_t upper_cp = unicode_upcase_codepoint(cp);
662
663 uint8_t buf[4];
664 int new_char_len = unicode_encode_utf8(upper_cp, buf);
665 new_len += new_char_len;
666 if( new_char_len != char_len ) needs_realloc = 1;
667 i += char_len;
668 }
669
670 if( needs_realloc ) {
671 // Need to allocate new buffer
672 uint8_t *new_data = mrbc_raw_alloc(new_len + 1);
673 if( !new_data ) return 0;
674
675 int j = 0;
676 for( int i = 0; i < len; ) {
677 int char_len;
678 int32_t cp = unicode_decode_utf8(data + i, &char_len);
679 int32_t upper_cp = unicode_upcase_codepoint(cp);
680
681 int new_char_len = unicode_encode_utf8(upper_cp, new_data + j);
682 if( upper_cp != cp ) count++;
683 j += new_char_len;
684 i += char_len;
685 }
686 new_data[new_len] = '\0';
687
688 mrbc_raw_free(data);
689 str->string->data = new_data;
690 str->string->size = new_len;
691 } else {
692 // In-place conversion
693 for( int i = 0; i < len; ) {
694 int char_len;
695 int32_t cp = unicode_decode_utf8(data + i, &char_len);
696 int32_t upper_cp = unicode_upcase_codepoint(cp);
697
698 if( upper_cp != cp ) {
699 unicode_encode_utf8(upper_cp, data + i);
700 count++;
701 }
702 i += char_len;
703 }
704 }
705
706 return count;
707}
708
709
710//================================================================
717{
718 int len = str->string->size;
719 uint8_t *data = str->string->data;
720 int count = 0;
721
722 // First pass: check if any conversion changes byte length
723 int new_len = 0;
724 int needs_realloc = 0;
725 for( int i = 0; i < len; ) {
726 int char_len;
727 int32_t cp = unicode_decode_utf8(data + i, &char_len);
728 int32_t lower_cp = unicode_downcase_codepoint(cp);
729
730 uint8_t buf[4];
731 int new_char_len = unicode_encode_utf8(lower_cp, buf);
732 new_len += new_char_len;
733 if( new_char_len != char_len ) needs_realloc = 1;
734 i += char_len;
735 }
736
737 if( needs_realloc ) {
738 // Need to allocate new buffer
739 uint8_t *new_data = mrbc_raw_alloc(new_len + 1);
740 if( !new_data ) return 0;
741
742 int j = 0;
743 for( int i = 0; i < len; ) {
744 int char_len;
745 int32_t cp = unicode_decode_utf8(data + i, &char_len);
746 int32_t lower_cp = unicode_downcase_codepoint(cp);
747
748 int new_char_len = unicode_encode_utf8(lower_cp, new_data + j);
749 if( lower_cp != cp ) count++;
750 j += new_char_len;
751 i += char_len;
752 }
753 new_data[new_len] = '\0';
754
755 mrbc_raw_free(data);
756 str->string->data = new_data;
757 str->string->size = new_len;
758 } else {
759 // In-place conversion
760 for( int i = 0; i < len; ) {
761 int char_len;
762 int32_t cp = unicode_decode_utf8(data + i, &char_len);
763 int32_t lower_cp = unicode_downcase_codepoint(cp);
764
765 if( lower_cp != cp ) {
766 unicode_encode_utf8(lower_cp, data + i);
767 count++;
768 }
769 i += char_len;
770 }
771 }
772
773 return count;
774}
775#endif /* MRBC_USE_UNICODE_CASE */
776#endif // MRBC_USE_STRING_UTF8
777
778
779//================================================================
782static void c_string_new(mrbc_vm *vm, mrbc_value v[], int argc)
783{
784 mrbc_value value;
785
786 switch( argc ) {
787 case 0:
788 value = mrbc_string_new(vm, NULL, 0);
789 break;
790
791 case 1:
792 if( mrbc_type(v[1]) != MRBC_TT_STRING ) {
793 mrbc_raisef( vm, MRBC_CLASS(TypeError), "no implicit conversion into %s", "String");
794 return;
795 }
796 value = mrbc_string_dup(vm, &v[1]);
797 break;
798
799 default:
800 mrbc_raise( vm, MRBC_CLASS(ArgumentError), "wrong number of arguments");
801 return;
802 }
803
804 SET_RETURN(value);
805}
806
807
808//================================================================
811static void c_string_add(mrbc_vm *vm, mrbc_value v[], int argc)
812{
813 if( mrbc_type(v[1]) != MRBC_TT_STRING ) {
814 mrbc_raise( vm, MRBC_CLASS(ArgumentError), 0 );
815 return;
816 }
817
818 mrbc_value value = mrbc_string_add(vm, &v[0], &v[1]);
819 SET_RETURN(value);
820}
821
822
823
824//================================================================
827static void c_string_mul(mrbc_vm *vm, mrbc_value v[], int argc)
828{
829 if( mrbc_type(v[1]) != MRBC_TT_INTEGER ) {
830 mrbc_raisef( vm, MRBC_CLASS(TypeError), "no implicit conversion into %s", "String");
831 return;
832 }
833
834 if( v[1].i < 0 ) {
835 mrbc_raise( vm, MRBC_CLASS(ArgumentError), "negative argument");
836 return;
837 }
838
839 mrbc_value value = mrbc_string_new(vm, NULL,
840 mrbc_string_size(&v[0]) * mrbc_integer(v[1]));
841 uint8_t *p = value.string->data;
842 for( int i = 0; i < v[1].i; i++ ) {
843 memcpy( p, mrbc_string_cstr(&v[0]), mrbc_string_size(&v[0]) );
844 p += mrbc_string_size(&v[0]);
845 }
846 *p = 0;
847
848 SET_RETURN(value);
849}
850
851
852
853//================================================================
856static void c_string_size(mrbc_vm *vm, mrbc_value v[], int argc)
857{
858#if MRBC_USE_STRING_UTF8
859 mrbc_int_t size = mrbc_string_char_size(mrbc_string_cstr(&v[0]), mrbc_string_size(&v[0]));
860#else
861 mrbc_int_t size = mrbc_string_size(&v[0]);
862#endif
863
864 SET_INT_RETURN( size );
865}
866
867
868//================================================================
871static void c_string_bytesize(mrbc_vm *vm, mrbc_value v[], int argc)
872{
874}
875
876
877//================================================================
880static void c_string_to_i(mrbc_vm *vm, mrbc_value v[], int argc)
881{
882 int base = 10;
883 if( argc ) {
884 base = v[1].i;
885 if( base < 2 || base > 36 ) {
886 mrbc_raisef(vm, MRBC_CLASS(ArgumentError), "invalid radix %d", base);
887 return;
888 }
889 }
890
891 mrbc_int_t i = mrbc_atoi( mrbc_string_cstr(v), base );
892
893 SET_INT_RETURN( i );
894}
895
896
897#if MRBC_USE_FLOAT
898//================================================================
901static void c_string_to_f(mrbc_vm *vm, mrbc_value v[], int argc)
902{
903#if MRBC_USE_FLOAT == 1
904 mrbc_float_t d = strtof(mrbc_string_cstr(v), NULL);
905#else
906 mrbc_float_t d = strtod(mrbc_string_cstr(v), NULL);
907#endif
908
909 SET_FLOAT_RETURN( d );
910}
911#endif
912
913
914//================================================================
917static void c_string_to_s(mrbc_vm *vm, mrbc_value v[], int argc)
918{
919 if( mrbc_type(v[0]) == MRBC_TT_CLASS ) {
920 mrbc_object_inspect(vm, v, argc);
921 return;
922 }
923}
924
925
926//================================================================
929static void c_string_append(mrbc_vm *vm, mrbc_value v[], int argc)
930{
931#if MRBC_USE_STRING_UTF8
932 // In UTF-8 mode, << accepts an integer codepoint
933 if( mrbc_type(v[1]) == MRBC_TT_INTEGER ) {
934 mrbc_int_t codepoint = mrbc_integer(v[1]);
935 char buf[5];
936 int len;
937
938 if( codepoint < 0 || codepoint > 0x10FFFF ) {
939 mrbc_raise(vm, MRBC_CLASS(RangeError), "out of char range");
940 return;
941 }
942 if( codepoint >= 0xD800 && codepoint <= 0xDFFF ) {
943 mrbc_raise(vm, MRBC_CLASS(RangeError), "invalid codepoint in UTF-8");
944 return;
945 }
946
947 len = mrbc_utf8_encode(codepoint, buf);
948 mrbc_string_append_cbuf(&v[0], buf, len);
949 return;
950 }
951#endif
952
953 mrbc_string_append( &v[0], &v[1] );
954}
955
956
957//================================================================
958/* Parse slice arguments (nth), (nth, len), or (Range) into *pos and *len.
959 Returns: 0=ok, 1=return nil, 2=error already raised.
960*/
961static int string_slice_parse(mrbc_vm *vm, mrbc_value v[], int argc,
962 int target_len, int *pos, int *len)
963{
964 if (argc == 1 && mrbc_type(v[1]) == MRBC_TT_INTEGER) {
965 *pos = mrbc_integer(v[1]);
966 if (*pos < 0) *pos += target_len;
967 if (*pos >= target_len) return 1;
968 *len = 1;
969 }
970 else if( argc == 2 && mrbc_type(v[1]) == MRBC_TT_INTEGER &&
971 mrbc_type(v[2]) == MRBC_TT_INTEGER ) {
972 *pos = mrbc_integer(v[1]);
973 if( *pos < 0 ) *pos += target_len;
974 *len = mrbc_integer(v[2]);
975 }
976 else if( argc == 1 && mrbc_type(v[1]) == MRBC_TT_RANGE ) {
977 const mrbc_value *v1 = mrbc_range_first_p(&v[1]);
978 switch( mrbc_type(*v1) ) {
979 case MRBC_TT_INTEGER:
980 *pos = mrbc_integer(*v1);
981 if( *pos < 0 ) *pos += target_len;
982 break;
983 case MRBC_TT_NIL:
984 *pos = 0;
985 break;
986 default:
987 mrbc_raise(vm, MRBC_CLASS(TypeError), 0);
988 return 2;
989 }
990
991 const mrbc_value *v2 = mrbc_range_last_p(&v[1]);
992 int pos2;
993 switch( mrbc_type(*v2) ) {
994 case MRBC_TT_INTEGER:
995 pos2 = mrbc_integer(*v2);
996 if( pos2 < 0 ) pos2 += target_len;
997 break;
998 case MRBC_TT_NIL:
999 pos2 = target_len;
1000 break;
1001 default:
1002 mrbc_raise(vm, MRBC_CLASS(TypeError), 0);
1003 return 2;
1004 }
1005
1006 *len = pos2 - *pos;
1007 if( !mrbc_range_exclude_end(&v[1]) ) (*len)++;
1008 }
1009 else {
1010 mrbc_raise(vm, MRBC_CLASS(ArgumentError), 0);
1011 return 2;
1012 }
1013
1014 if( *pos < 0 || *pos > target_len ) return 1;
1015 if( *len > target_len - *pos ) *len = target_len - *pos;
1016 if( *len < 0 ) {
1017 if( mrbc_type(v[1]) == MRBC_TT_RANGE) {
1018 *len = 0;
1019 } else {
1020 return 1;
1021 }
1022 }
1023
1024 return 0;
1025}
1026
1027
1028//================================================================
1031static void c_string_slice(mrbc_vm *vm, mrbc_value v[], int argc)
1032{
1033#if MRBC_USE_STRING_UTF8
1034 int target_len = mrbc_string_char_size(mrbc_string_cstr(&v[0]), mrbc_string_size(&v[0]));
1035#else
1036 int target_len = mrbc_string_size(v);
1037#endif
1038 int pos, len;
1039
1040 switch( string_slice_parse(vm, v, argc, target_len, &pos, &len) ) {
1041 case 1: SET_NIL_RETURN(); return;
1042 case 2: return;
1043 }
1044
1045#if MRBC_USE_STRING_UTF8
1046 int byte_pos = mrbc_string_chars2bytes(&v[0], 0, pos);
1047 int byte_len = mrbc_string_chars2bytes(&v[0], byte_pos, len);
1048 mrbc_value ret = mrbc_string_new(vm, mrbc_string_cstr(v) + byte_pos, byte_len);
1049#else
1050 mrbc_value ret = mrbc_string_new(vm, mrbc_string_cstr(v) + pos, len);
1051#endif
1052
1053 SET_RETURN(ret);
1054}
1055
1056
1057//================================================================
1060static void c_string_byteslice(mrbc_vm *vm, mrbc_value v[], int argc)
1061{
1062 int target_len = mrbc_string_size(&v[0]); // always byte count
1063 int pos, len;
1064
1065 switch (string_slice_parse(vm, v, argc, target_len, &pos, &len)) {
1066 case 1: SET_NIL_RETURN(); return;
1067 case 2: return;
1068 }
1069
1070 mrbc_value ret = mrbc_string_new(vm, mrbc_string_cstr(v) + pos, len);
1071
1072 SET_RETURN(ret);
1073}
1074
1075
1076//================================================================
1079static void c_string_insert(mrbc_vm *vm, mrbc_value v[], int argc)
1080{
1081#if MRBC_USE_STRING_UTF8
1082 int target_len = mrbc_string_char_size(mrbc_string_cstr(&v[0]), mrbc_string_size(&v[0]));
1083#else
1084 int target_len = mrbc_string_size(v);
1085#endif
1086 int pos, len;
1087 mrbc_value *val;
1088
1089 // in case of self[pos] = val
1090 if( argc == 2 && mrbc_type(v[1]) == MRBC_TT_INTEGER &&
1091 mrbc_type(v[2]) == MRBC_TT_STRING ) {
1092 pos = mrbc_integer(v[1]);
1093 len = 1;
1094 val = &v[2];
1095 }
1096
1097 // in case of self[pos, len] = val
1098 else if( argc == 3 && mrbc_type(v[1]) == MRBC_TT_INTEGER &&
1099 mrbc_type(v[2]) == MRBC_TT_INTEGER &&
1100 mrbc_type(v[3]) == MRBC_TT_STRING ) {
1101 pos = mrbc_integer(v[1]);
1102 len = mrbc_integer(v[2]);
1103 val = &v[3];
1104 }
1105
1106 // in case of self[Range] = val
1107 else if( argc == 2 && mrbc_type(v[1]) == MRBC_TT_RANGE &&
1108 mrbc_type(v[2]) == MRBC_TT_STRING ) {
1109 const mrbc_value *v1 = mrbc_range_first_p(&v[1]);
1110 switch( mrbc_type(*v1) ) {
1111 case MRBC_TT_INTEGER:
1112 pos = mrbc_integer(*v1);
1113 if( pos < 0 ) pos += target_len;
1114 if( pos < 0 || pos > target_len ) {
1115 mrbc_raise( vm, MRBC_CLASS(RangeError), 0 );
1116 return;
1117 }
1118 break;
1119 case MRBC_TT_NIL:
1120 pos = 0;
1121 break;
1122 default:
1123 goto TYPE_ERROR;
1124 }
1125
1126 const mrbc_value *v2 = mrbc_range_last_p(&v[1]);
1127 int pos2;
1128 switch( mrbc_type(*v2) ) {
1129 case MRBC_TT_INTEGER:
1130 pos2 = mrbc_integer(*v2);
1131 if( pos2 < 0 ) pos2 += target_len;
1132 break;
1133 case MRBC_TT_NIL:
1134 pos2 = target_len;
1135 break;
1136 default:
1137 goto TYPE_ERROR;
1138 }
1139
1140 len = pos2 - pos;
1141 if( !mrbc_range_exclude_end(&v[1]) ) len++;
1142 if( len < 0 ) len = 0;
1143 val = &v[2];
1144 }
1145
1146 // other cases
1147 else {
1148 mrbc_raise( vm, MRBC_CLASS(ArgumentError), 0 );
1149 return;
1150 }
1151
1152 int len1 = target_len; // character length
1153 int len2 = mrbc_string_size(val); // byte length of replacement
1154 if( pos < 0 ) pos = len1 + pos; // adjust to positive number.
1155 if( len > len1 - pos ) len = len1 - pos;
1156 if( pos < 0 || pos > len1 || len < 0) {
1157 mrbc_raisef( vm, MRBC_CLASS(IndexError), "index %d out of string", pos );
1158 return;
1159 }
1160
1161#if MRBC_USE_STRING_UTF8
1162 // Convert character position/length to byte position/length
1163 int byte_pos = mrbc_string_chars2bytes(&v[0], 0, pos);
1164 int byte_len = mrbc_string_chars2bytes(&v[0], byte_pos, len);
1165 int byte_len1 = mrbc_string_size(&v[0]); // original byte length
1166
1167 int byte_len3 = byte_len1 + len2 - byte_len; // final byte length
1168 uint8_t *str = v->string->data;
1169 if( byte_len1 < byte_len3 ) {
1170 str = mrbc_realloc(vm, str, byte_len3+1); // expand
1171 }
1172
1173 memmove( str + byte_pos + len2, str + byte_pos + byte_len, byte_len1 - byte_pos - byte_len + 1 );
1174 memcpy( str + byte_pos, mrbc_string_cstr(val), len2 );
1175
1176 if( byte_len1 > byte_len3 ) {
1177 str = mrbc_realloc(vm, str, byte_len3+1); // shrink
1178 }
1179
1180 v->string->size = byte_len3;
1181 v->string->data = str;
1182#else
1183 int len3 = len1 + len2 - len; // final length.
1184 uint8_t *str = v->string->data;
1185 if( len1 < len3 ) {
1186 str = mrbc_realloc(vm, str, len3+1); // expand
1187 }
1188
1189 memmove( str + pos + len2, str + pos + len, len1 - pos - len + 1 );
1190 memcpy( str + pos, mrbc_string_cstr(val), len2 );
1191
1192 if( len1 > len3 ) {
1193 str = mrbc_realloc(vm, str, len3+1); // shrink
1194 }
1195
1196 v->string->size = len1 + len2 - len;
1197 v->string->data = str;
1198#endif
1199
1200 // return val
1201 mrbc_decref(&v[0]);
1202 v[0] = *val;
1204 return;
1205
1206
1207 TYPE_ERROR:
1208 mrbc_raise( vm, MRBC_CLASS(TypeError), 0 );
1209 return;
1210}
1211
1212
1213//================================================================
1216static void c_string_chomp(mrbc_vm *vm, mrbc_value v[], int argc)
1217{
1218 mrbc_value ret = mrbc_string_dup(vm, &v[0]);
1219
1220 mrbc_string_chomp(&ret);
1221
1222 SET_RETURN(ret);
1223}
1224
1225
1226//================================================================
1229static void c_string_clear(mrbc_vm *vm, mrbc_value v[], int argc)
1230{
1231 mrbc_string_clear(&v[0]);
1232}
1233
1234
1235//================================================================
1238static void c_string_chomp_self(mrbc_vm *vm, mrbc_value v[], int argc)
1239{
1240 if( mrbc_string_chomp(&v[0]) == 0 ) {
1242 }
1243}
1244
1245
1246//================================================================
1249static void c_string_dup(mrbc_vm *vm, mrbc_value v[], int argc)
1250{
1251 mrbc_value ret = mrbc_string_dup(vm, &v[0]);
1252
1253 SET_RETURN(ret);
1254}
1255
1256
1257//================================================================
1260static void c_string_empty(mrbc_vm *vm, mrbc_value v[], int argc)
1261{
1263}
1264
1265
1266//================================================================
1269static void c_string_getbyte(mrbc_vm *vm, mrbc_value v[], int argc)
1270{
1271 int len = mrbc_string_size(&v[0]);
1272 mrbc_int_t idx = mrbc_integer(v[1]);
1273
1274 if( idx >= 0 ) {
1275 if( idx >= len ) idx = -1;
1276 } else {
1277 idx += len;
1278 }
1279 if( idx >= 0 ) {
1280 SET_INT_RETURN( ((uint8_t *)mrbc_string_cstr(&v[0]))[idx] );
1281 } else {
1283 }
1284}
1285
1286
1287//================================================================
1290static void c_string_setbyte(mrbc_vm *vm, mrbc_value v[], int argc)
1291{
1292 if( argc != 2 ) {
1293 mrbc_raise(vm, MRBC_CLASS(ArgumentError), "wrong number of arguments");
1294 return;
1295 }
1296
1297 int len = mrbc_string_size(&v[0]);
1298 mrbc_int_t idx = mrbc_integer(v[1]);
1299 mrbc_int_t dat = mrbc_integer(v[2]);
1300
1301 if( idx < 0 ) {
1302 idx += len;
1303 }
1304 if( idx < 0 || idx >= len ) {
1305 mrbc_raisef( vm, MRBC_CLASS(IndexError), "index %d out of string", idx );
1306 return;
1307 }
1308
1309 mrbc_string_cstr(&v[0])[idx] = dat;
1310
1311 SET_INT_RETURN( dat );
1312}
1313
1314
1315//================================================================
1318static void c_string_index(mrbc_vm *vm, mrbc_value v[], int argc)
1319{
1320 int index;
1321 int offset;
1322
1323 if( argc == 1 ) {
1324 offset = 0;
1325
1326 } else if( argc == 2 && mrbc_type(v[2]) == MRBC_TT_INTEGER ) {
1327 offset = v[2].i;
1328#if MRBC_USE_STRING_UTF8
1329 int char_len = mrbc_string_char_size(mrbc_string_cstr(&v[0]), mrbc_string_size(&v[0]));
1330 if( offset < 0 ) offset += char_len;
1331 if( offset < 0 ) goto NIL_RETURN;
1332 // Convert character offset to byte offset
1333 offset = mrbc_string_chars2bytes(&v[0], 0, offset);
1334#else
1335 if( offset < 0 ) offset += mrbc_string_size(&v[0]);
1336 if( offset < 0 ) goto NIL_RETURN;
1337#endif
1338
1339 } else {
1340 mrbc_raise( vm, MRBC_CLASS(ArgumentError), 0 );
1341 return;
1342 }
1343
1344 index = mrbc_string_index(&v[0], &v[1], offset);
1345 if( index < 0 ) goto NIL_RETURN;
1346
1347#if MRBC_USE_STRING_UTF8
1348 // Convert byte index to character index
1349 index = mrbc_string_bytes2chars(&v[0], index);
1350 if( index < 0 ) goto NIL_RETURN;
1351#endif
1352
1353 SET_INT_RETURN(index);
1354 return;
1355
1356 NIL_RETURN:
1358}
1359
1360
1361//================================================================
1364static void c_string_inspect(mrbc_vm *vm, mrbc_value v[], int argc)
1365{
1366 if( mrbc_type(v[0]) == MRBC_TT_CLASS ) {
1367 mrbc_object_inspect(vm, v, argc);
1368 return;
1369 }
1370
1371 mrbc_value ret = mrbc_string_new_cstr(vm, "\"");
1372 const char *s = mrbc_string_cstr(v);
1373 int size = mrbc_string_size(v);
1374
1375#if MRBC_USE_STRING_UTF8
1376 for( int i = 0; i < size; ) {
1377 unsigned char uch = (unsigned char)s[i];
1378 if( uch < 0x80 ) {
1379 // ASCII byte: use existing escape logic
1380 char buf[10];
1381 mrbc_char_to_s( buf, s + i );
1382 mrbc_string_append_cstr(&ret, buf);
1383 i++;
1384 } else {
1385 int char_len = utf8_validated_char_len(s + i, s + size);
1386 int raw_len = mrbc_string_utf8_size(s + i);
1387 if( raw_len >= 2 && char_len == raw_len ) {
1388 // Valid multi-byte UTF-8 character: output as-is
1389 mrbc_string_append_cbuf(&ret, s + i, char_len);
1390 i += char_len;
1391 } else {
1392 // Invalid/isolated byte: escape as \xHH
1393 char buf[5];
1394 buf[0] = '\\';
1395 buf[1] = 'x';
1396 buf[2] = "0123456789ABCDEF"[uch >> 4];
1397 buf[3] = "0123456789ABCDEF"[uch & 0x0f];
1398 buf[4] = 0;
1399 mrbc_string_append_cstr(&ret, buf);
1400 i++;
1401 }
1402 }
1403 }
1404#else
1405 for( int i = 0; i < size; i++ ) {
1406 char buf[10];
1407 mrbc_char_to_s( buf, s + i );
1408 mrbc_string_append_cstr(&ret, buf);
1409 }
1410#endif
1411
1412 mrbc_string_append_cstr(&ret, "\"");
1413
1414 SET_RETURN( ret );
1415}
1416
1417
1418//================================================================
1421static void c_string_ord(mrbc_vm *vm, mrbc_value v[], int argc)
1422{
1423 if( mrbc_string_size(v) == 0 ) {
1424 mrbc_raise(vm, MRBC_CLASS(ArgumentError), "empty string");
1425 return;
1426 }
1427
1428#if MRBC_USE_STRING_UTF8
1429 const uint8_t *s = (const uint8_t *)mrbc_string_cstr(v);
1430 const char *end = mrbc_string_cstr(v) + mrbc_string_size(v);
1431 int char_len = utf8_validated_char_len((const char *)s, end);
1432 mrbc_int_t codepoint;
1433
1434 if( char_len == 1 ) {
1435 codepoint = s[0];
1436 } else if( char_len == 2 ) {
1437 codepoint = ((s[0] & 0x1F) << 6) | (s[1] & 0x3F);
1438 } else if( char_len == 3 ) {
1439 codepoint = ((s[0] & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
1440 } else if( char_len == 4 ) {
1441 codepoint = ((s[0] & 0x07) << 18) | ((s[1] & 0x3F) << 12) |
1442 ((s[2] & 0x3F) << 6) | (s[3] & 0x3F);
1443 } else {
1444 codepoint = s[0]; // fallback for invalid UTF-8
1445 }
1446
1447 SET_INT_RETURN( codepoint );
1448#else
1449 int i = ((uint8_t *)mrbc_string_cstr(v))[0];
1450
1451 SET_INT_RETURN( i );
1452#endif
1453}
1454
1455
1456//================================================================
1459static void c_string_slice_self(mrbc_vm *vm, mrbc_value v[], int argc)
1460{
1461#if MRBC_USE_STRING_UTF8
1462 int target_len = mrbc_string_char_size(mrbc_string_cstr(&v[0]), mrbc_string_size(&v[0]));
1463#else
1464 int target_len = mrbc_string_size(v);
1465#endif
1466 int pos = mrbc_integer(v[1]);
1467 int len;
1468
1469 // in case of slice!(nth) -> String | nil
1470 if( argc == 1 && mrbc_type(v[1]) == MRBC_TT_INTEGER ) {
1471 len = 1;
1472
1473 // in case of slice!(nth, len) -> String | nil
1474 } else if( argc == 2 && mrbc_type(v[1]) == MRBC_TT_INTEGER &&
1475 mrbc_type(v[2]) == MRBC_TT_INTEGER ) {
1476 len = mrbc_integer(v[2]);
1477
1478 // other case
1479 } else {
1480 mrbc_raise( vm, MRBC_CLASS(ArgumentError), 0 );
1481 return;
1482 }
1483
1484 if( pos < 0 ) pos += target_len;
1485 if( pos < 0 ) goto RETURN_NIL;
1486 if( len > (target_len - pos) ) len = target_len - pos;
1487 if( len < 0 ) goto RETURN_NIL;
1488 if( argc == 1 && len <= 0 ) goto RETURN_NIL;
1489
1490#if MRBC_USE_STRING_UTF8
1491 // Convert character position/length to byte position/length
1492 int byte_pos = mrbc_string_chars2bytes(&v[0], 0, pos);
1493 int byte_len = mrbc_string_chars2bytes(&v[0], byte_pos, len);
1494 int byte_size = mrbc_string_size(&v[0]);
1495
1496 mrbc_value ret = mrbc_string_new(vm, mrbc_string_cstr(v) + byte_pos, byte_len);
1497
1498 if( byte_len > 0 ) {
1499 memmove( mrbc_string_cstr(v) + byte_pos, mrbc_string_cstr(v) + byte_pos + byte_len,
1500 byte_size - byte_pos - byte_len + 1 );
1501 v->string->size = byte_size - byte_len;
1503 }
1504#else
1505 mrbc_value ret = mrbc_string_new(vm, mrbc_string_cstr(v) + pos, len);
1506
1507 if( len > 0 ) {
1508 memmove( mrbc_string_cstr(v) + pos, mrbc_string_cstr(v) + pos + len,
1509 mrbc_string_size(v) - pos - len + 1 );
1510 v->string->size = mrbc_string_size(v) - len;
1512 }
1513#endif
1514
1515 SET_RETURN(ret);
1516 return; // normal return
1517
1518 RETURN_NIL:
1520}
1521
1522
1523//================================================================
1526static void c_string_split(mrbc_vm *vm, mrbc_value v[], int argc)
1527{
1528 mrbc_value ret = mrbc_array_new(vm, 0);
1529 if( mrbc_string_size(&v[0]) == 0 ) goto DONE;
1530
1531 // check limit parameter.
1532 int limit = 0;
1533 if( argc >= 2 ) {
1534 if( mrbc_type(v[2]) != MRBC_TT_INTEGER ) {
1535 mrbc_raise( vm, MRBC_CLASS(ArgumentError), 0 );
1536 return;
1537 }
1538 limit = v[2].i;
1539 if( limit == 1 ) {
1540 mrbc_array_push( &ret, &v[0] );
1541 mrbc_incref( &v[0] );
1542 goto DONE;
1543 }
1544 }
1545
1546 // check separator parameter.
1547 mrbc_value sep = (argc == 0) ? mrbc_string_new_cstr(vm, " ") : v[1];
1548 switch( mrbc_type(sep) ) {
1549 case MRBC_TT_NIL:
1550 sep = mrbc_string_new_cstr(vm, " ");
1551 break;
1552
1553 case MRBC_TT_STRING:
1554 break;
1555
1556 default:
1557 mrbc_raise( vm, MRBC_CLASS(TypeError), 0 );
1558 return;
1559 }
1560
1561 int flag_strip = (mrbc_string_cstr(&sep)[0] == ' ') &&
1562 (mrbc_string_size(&sep) == 1);
1563 int offset = 0;
1564 int sep_len = mrbc_string_size(&sep);
1565 if( sep_len == 0 ) sep_len++;
1566
1567 while( 1 ) {
1568 int pos, len = 0;
1569
1570 if( flag_strip ) {
1571 for( ; offset < mrbc_string_size(&v[0]); offset++ ) {
1572 if( !is_space( mrbc_string_cstr(&v[0])[offset] )) break;
1573 }
1574 if( offset > mrbc_string_size(&v[0])) break;
1575 }
1576
1577 // check limit
1578 if( limit > 0 && mrbc_array_size(&ret)+1 >= limit ) {
1579 pos = -1;
1580 goto SPLIT_ITEM;
1581 }
1582
1583 // split by space character.
1584 if( flag_strip ) {
1585 pos = offset;
1586 for( ; pos < mrbc_string_size(&v[0]); pos++ ) {
1587 if( is_space( mrbc_string_cstr(&v[0])[pos] )) break;
1588 }
1589 len = pos - offset;
1590 goto SPLIT_ITEM;
1591 }
1592
1593 // split by each character.
1594 if( mrbc_string_size(&sep) == 0 ) {
1595#if MRBC_USE_STRING_UTF8
1596 // Get UTF-8 character length at current offset
1597 int char_len = mrbc_string_utf8_size(mrbc_string_cstr(&v[0]) + offset);
1598 if( char_len == 0 ) char_len = 1; // skip invalid byte
1599 pos = (offset + char_len < mrbc_string_size(&v[0])) ? offset : -1;
1600 len = char_len;
1601 sep_len = char_len; // advance by character length
1602#else
1603 pos = (offset < mrbc_string_size(&v[0])-1) ? offset : -1;
1604 len = 1;
1605#endif
1606 goto SPLIT_ITEM;
1607 }
1608
1609 // split by specified character.
1610 pos = mrbc_string_index( &v[0], &sep, offset );
1611 len = pos - offset;
1612
1613
1614 SPLIT_ITEM:
1615 if( pos < 0 ) len = mrbc_string_size(&v[0]) - offset;
1616
1617 mrbc_value v1 = mrbc_string_new(vm, mrbc_string_cstr(&v[0]) + offset, len);
1618 mrbc_array_push( &ret, &v1 );
1619
1620 if( pos < 0 ) break;
1621 offset = pos + sep_len;
1622 }
1623
1624 // remove trailing empty item
1625 if( limit == 0 ) {
1626 while( 1 ) {
1627 int idx = mrbc_array_size(&ret) - 1;
1628 if( idx < 0 ) break;
1629
1630 mrbc_value v1 = mrbc_array_get( &ret, idx );
1631 if( mrbc_string_size(&v1) != 0 ) break;
1632
1633 mrbc_array_remove(&ret, idx);
1634 mrbc_string_delete( &v1 );
1635 }
1636 }
1637
1638 if( argc == 0 || mrbc_type(v[1]) == MRBC_TT_NIL ) {
1639 mrbc_string_delete(&sep);
1640 }
1641
1642 DONE:
1643 SET_RETURN( ret );
1644}
1645
1646
1647//================================================================
1650static void c_string_lstrip(mrbc_vm *vm, mrbc_value v[], int argc)
1651{
1652 mrbc_value ret = mrbc_string_dup(vm, &v[0]);
1653
1654 mrbc_string_strip(&ret, 0x01); // 1: left side only
1655
1656 SET_RETURN(ret);
1657}
1658
1659
1660//================================================================
1663static void c_string_lstrip_self(mrbc_vm *vm, mrbc_value v[], int argc)
1664{
1665 if( mrbc_string_strip(&v[0], 0x01) == 0 ) { // 1: left side only
1667 }
1668}
1669
1670
1671//================================================================
1674static void c_string_rstrip(mrbc_vm *vm, mrbc_value v[], int argc)
1675{
1676 mrbc_value ret = mrbc_string_dup(vm, &v[0]);
1677
1678 mrbc_string_strip(&ret, 0x02); // 2: right side only
1679
1680 SET_RETURN(ret);
1681}
1682
1683
1684//================================================================
1687static void c_string_rstrip_self(mrbc_vm *vm, mrbc_value v[], int argc)
1688{
1689 if( mrbc_string_strip(&v[0], 0x02) == 0 ) { // 2: right side only
1691 }
1692}
1693
1694
1695//================================================================
1698static void c_string_strip(mrbc_vm *vm, mrbc_value v[], int argc)
1699{
1700 mrbc_value ret = mrbc_string_dup(vm, &v[0]);
1701
1702 mrbc_string_strip(&ret, 0x03); // 3: left and right
1703
1704 SET_RETURN(ret);
1705}
1706
1707
1708//================================================================
1711static void c_string_strip_self(mrbc_vm *vm, mrbc_value v[], int argc)
1712{
1713 if( mrbc_string_strip(&v[0], 0x03) == 0 ) { // 3: left and right
1715 }
1716}
1717
1718
1719//================================================================
1722static void c_string_to_sym(mrbc_vm *vm, mrbc_value v[], int argc)
1723{
1725
1726 SET_RETURN(ret);
1727}
1728
1729
1730//================================================================
1740
1741#if MRBC_USE_STRING_UTF8
1742//================================================================
1743// UTF-8 version of tr - uses codepoints instead of bytes
1744//================================================================
1745
1749static int32_t tr_utf8_decode(const char **str)
1750{
1751 const unsigned char *s = (const unsigned char *)*str;
1752 int32_t codepoint;
1753 int len;
1754
1755 if( s[0] < 0x80 ) {
1756 codepoint = s[0];
1757 len = 1;
1758 } else if( (s[0] & 0xE0) == 0xC0 ) {
1759 codepoint = ((s[0] & 0x1F) << 6) | (s[1] & 0x3F);
1760 len = 2;
1761 } else if( (s[0] & 0xF0) == 0xE0 ) {
1762 codepoint = ((s[0] & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
1763 len = 3;
1764 } else if( (s[0] & 0xF8) == 0xF0 ) {
1765 codepoint = ((s[0] & 0x07) << 18) | ((s[1] & 0x3F) << 12) | ((s[2] & 0x3F) << 6) | (s[3] & 0x3F);
1766 len = 4;
1767 } else {
1768 // Invalid UTF-8, treat as single byte
1769 codepoint = s[0];
1770 len = 1;
1771 }
1772
1773 *str += len;
1774 return codepoint;
1775}
1776
1777// UTF-8 pattern stores codepoints
1778struct tr_pattern_utf8 {
1779 uint8_t type; // 1:in-order, 2:range
1780 uint8_t flag_reverse;
1781 int16_t n; // number of codepoints
1782 struct tr_pattern_utf8 *next;
1783 int32_t codepoints[]; // flexible array of codepoints
1784};
1785
1786static void tr_free_pattern_utf8(struct tr_pattern_utf8 *pat)
1787{
1788 while( pat ) {
1789 struct tr_pattern_utf8 *p = pat->next;
1790 mrbc_raw_free(pat);
1791 pat = p;
1792 }
1793}
1794
1795static struct tr_pattern_utf8 *tr_parse_pattern_utf8(mrbc_vm *vm, const mrbc_value *v_pattern, int flag_reverse_enable)
1796{
1797 const char *pattern = mrbc_string_cstr(v_pattern);
1798 const char *pattern_end = pattern + mrbc_string_size(v_pattern);
1799 int flag_reverse = 0;
1800 struct tr_pattern_utf8 *ret = NULL;
1801 struct tr_pattern_utf8 *last = NULL;
1802
1803 // Check for ^ at start (only treat as negation if followed by at least one char)
1804 if( flag_reverse_enable && pattern < pattern_end && *pattern == '^' && (pattern + 1) < pattern_end ) {
1805 flag_reverse = 1;
1806 pattern++;
1807 }
1808
1809 while( pattern < pattern_end ) {
1810 const char *start = pattern;
1811 int32_t first_cp = tr_utf8_decode(&pattern);
1812
1813 // Check if this is a range pattern (cp1 - cp2)
1814 if( pattern < pattern_end && *pattern == '-' && (pattern + 1) < pattern_end ) {
1815 pattern++; // skip '-'
1816 int32_t second_cp = tr_utf8_decode(&pattern);
1817
1818 // Create range pattern
1819 struct tr_pattern_utf8 *pat1 = mrbc_alloc(vm, sizeof(struct tr_pattern_utf8) + 2 * sizeof(int32_t));
1820
1821 pat1->type = 2; // range
1822 pat1->flag_reverse = flag_reverse;
1823 pat1->n = second_cp - first_cp + 1;
1824 pat1->next = NULL;
1825 pat1->codepoints[0] = first_cp;
1826 pat1->codepoints[1] = second_cp;
1827
1828 // Add to list
1829 if( ret == NULL ) {
1830 ret = last = pat1;
1831 } else {
1832 last->next = pat1;
1833 last = pat1;
1834 }
1835 } else {
1836 // In-order pattern - collect consecutive non-range characters
1837 // First, count how many codepoints until we hit a range or end
1838 const char *scan = pattern;
1839 int count = 1; // we already have first_cp
1840
1841 while( scan < pattern_end ) {
1842 const char *next = scan;
1843 tr_utf8_decode(&next);
1844 // Check if next char starts a range
1845 if( next < pattern_end && *next == '-' && (next + 1) < pattern_end ) {
1846 break; // stop before this char, it's part of a range
1847 }
1848 tr_utf8_decode(&scan);
1849 count++;
1850 }
1851
1852 // Create in-order pattern
1853 struct tr_pattern_utf8 *pat1 = mrbc_alloc(vm, sizeof(struct tr_pattern_utf8) + count * sizeof(int32_t));
1854
1855 pat1->type = 1; // in-order
1856 pat1->flag_reverse = flag_reverse;
1857 pat1->n = count;
1858 pat1->next = NULL;
1859
1860 // Fill in codepoints
1861 const char *p = start;
1862 for( int i = 0; i < count; i++ ) {
1863 pat1->codepoints[i] = tr_utf8_decode(&p);
1864 }
1865 pattern = p;
1866
1867 // Add to list
1868 if( ret == NULL ) {
1869 ret = last = pat1;
1870 } else {
1871 last->next = pat1;
1872 last = pat1;
1873 }
1874 }
1875 }
1876
1877 return ret;
1878}
1879
1880static int tr_find_codepoint(const struct tr_pattern_utf8 *pat, int32_t cp)
1881{
1882 int ret = -1;
1883 int n_sum = 0;
1884 int flag_reverse = pat ? pat->flag_reverse : 0;
1885
1886 while( pat != NULL ) {
1887 if( pat->type == 1 ) { // in-order
1888 for( int i = 0; i < pat->n; i++ ) {
1889 if( pat->codepoints[i] == cp ) ret = n_sum + i;
1890 }
1891 } else { // range
1892 if( pat->codepoints[0] <= cp && cp <= pat->codepoints[1] ) {
1893 ret = n_sum + (cp - pat->codepoints[0]);
1894 }
1895 }
1896 n_sum += pat->n;
1897 pat = pat->next;
1898 }
1899
1900 if( flag_reverse ) {
1901 return (ret < 0) ? INT_MAX : -1;
1902 }
1903 return ret;
1904}
1905
1906static int32_t tr_get_codepoint(const struct tr_pattern_utf8 *pat, int n_th)
1907{
1908 int n_sum = 0;
1909 while( pat != NULL ) {
1910 if( n_th < (n_sum + pat->n) ) {
1911 int i = n_th - n_sum;
1912 return (pat->type == 1) ? pat->codepoints[i] : pat->codepoints[0] + i;
1913 }
1914 if( pat->next == NULL ) {
1915 // Use last character for overflow
1916 return (pat->type == 1) ? pat->codepoints[pat->n - 1] : pat->codepoints[1];
1917 }
1918 n_sum += pat->n;
1919 pat = pat->next;
1920 }
1921 return -1;
1922}
1923
1924static int tr_main_utf8(mrbc_vm *vm, mrbc_value v[], int argc)
1925{
1926 if( !(argc == 2 && mrbc_type(v[1]) == MRBC_TT_STRING &&
1927 mrbc_type(v[2]) == MRBC_TT_STRING) ) {
1928 mrbc_raise(vm, MRBC_CLASS(ArgumentError), 0);
1929 return -1;
1930 }
1931
1932 struct tr_pattern_utf8 *pat = tr_parse_pattern_utf8(vm, &v[1], 1);
1933 if( pat == NULL ) return 0;
1934
1935 struct tr_pattern_utf8 *rep = tr_parse_pattern_utf8(vm, &v[2], 0);
1936
1937 // Build result string using chars approach
1938 mrbc_value result = mrbc_string_new(vm, NULL, 0);
1939 if( result.string == NULL ) {
1940 tr_free_pattern_utf8(pat);
1941 tr_free_pattern_utf8(rep);
1942 return -1;
1943 }
1944
1945 int flag_changed = 0;
1946 const char *s = mrbc_string_cstr(&v[0]);
1947 const char *end = s + mrbc_string_size(&v[0]);
1948
1949 while( s < end ) {
1950 const char *char_start = s;
1951 int32_t cp = tr_utf8_decode(&s);
1952 int char_len = s - char_start;
1953
1954 int n = tr_find_codepoint(pat, cp);
1955 if( n < 0 ) {
1956 // No match, copy original character
1957 mrbc_string_append_cbuf(&result, char_start, char_len);
1958 } else {
1959 flag_changed = 1;
1960 if( rep == NULL ) {
1961 // Delete character (don't append anything)
1962 } else {
1963 // Replace with corresponding character from rep
1964 int32_t new_cp = tr_get_codepoint(rep, n);
1965 char buf[4];
1966 int new_len = mrbc_utf8_encode(new_cp, buf);
1967 mrbc_string_append_cbuf(&result, buf, new_len);
1968 }
1969 }
1970 }
1971
1972 tr_free_pattern_utf8(pat);
1973 tr_free_pattern_utf8(rep);
1974
1975 // Replace original string content with result
1976 mrbc_string *orig = v[0].string;
1977 mrbc_string *res = result.string;
1978
1979 // Swap the data
1980 if( mrbc_string_size(&v[0]) != mrbc_string_size(&result) ) {
1981 // Need to reallocate
1982 uint8_t *new_data = mrbc_realloc(vm, orig->data, res->size + 1);
1983 if( new_data == NULL ) {
1984 mrbc_decref(&result);
1985 return -1;
1986 }
1987 orig->data = new_data;
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:71
#define mrbc_nil_value()
Definition boxing_no.h:66
static void mrbc_set_tt(mrbc_value *p, mrbc_vtype type)
Definition boxing_no.h:119
#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:64
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:83
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:239
static int string_slice_parse(mrbc_vm *vm, mrbc_value v[], int argc, int target_len, int *pos, int *len)
Definition c_string.c:961
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:145
mrbc_value mrbc_string_add(mrbc_vm *vm, const mrbc_value *s1, const mrbc_value *s2)
Definition c_string.c:164
int mrbc_string_chomp(mrbc_value *src)
Definition c_string.c:304
int mrbc_string_append_cbuf(mrbc_value *s1, const void *s2, int len2)
Definition c_string.c:212
int mrbc_string_upcase(mrbc_value *str)
Definition c_string.c:334
int mrbc_string_downcase(mrbc_value *str)
Definition c_string.c:356
int mrbc_string_append(mrbc_value *s1, const mrbc_value *s2)
Definition c_string.c:184
int mrbc_string_strip(mrbc_value *src, int mode)
Definition c_string.c:264
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:355
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:340
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:561
#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:546
@ 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.