1use crate::seqstring::global_string;
14use crate::stack::{Stack, pop, push};
15use crate::value::Value;
16
17#[unsafe(no_mangle)]
26pub unsafe extern "C" fn patch_seq_string_split(stack: Stack) -> Stack {
27 use crate::value::VariantData;
28
29 assert!(!stack.is_null(), "string_split: stack is empty");
30
31 let (stack, delim_val) = unsafe { pop(stack) };
32 assert!(!stack.is_null(), "string_split: need two strings");
33 let (stack, str_val) = unsafe { pop(stack) };
34
35 match (str_val, delim_val) {
36 (Value::String(s), Value::String(d)) => {
37 let fields: Vec<Value> = s
39 .as_str()
40 .split(d.as_str())
41 .map(|part| Value::String(global_string(part.to_owned())))
42 .collect();
43
44 let variant = Value::Variant(Box::new(VariantData::new(0, fields)));
46
47 unsafe { push(stack, variant) }
48 }
49 _ => panic!("string_split: expected two strings on stack"),
50 }
51}
52
53#[unsafe(no_mangle)]
60pub unsafe extern "C" fn patch_seq_string_empty(stack: Stack) -> Stack {
61 assert!(!stack.is_null(), "string_empty: stack is empty");
62
63 let (stack, value) = unsafe { pop(stack) };
64
65 match value {
66 Value::String(s) => {
67 let is_empty = s.as_str().is_empty();
68 let result = if is_empty { 1 } else { 0 };
69 unsafe { push(stack, Value::Int(result)) }
70 }
71 _ => panic!("string_empty: expected String on stack"),
72 }
73}
74
75#[unsafe(no_mangle)]
82pub unsafe extern "C" fn patch_seq_string_contains(stack: Stack) -> Stack {
83 assert!(!stack.is_null(), "string_contains: stack is empty");
84
85 let (stack, substring_val) = unsafe { pop(stack) };
86 assert!(!stack.is_null(), "string_contains: need two strings");
87 let (stack, str_val) = unsafe { pop(stack) };
88
89 match (str_val, substring_val) {
90 (Value::String(s), Value::String(sub)) => {
91 let contains = s.as_str().contains(sub.as_str());
92 let result = if contains { 1 } else { 0 };
93 unsafe { push(stack, Value::Int(result)) }
94 }
95 _ => panic!("string_contains: expected two strings on stack"),
96 }
97}
98
99#[unsafe(no_mangle)]
106pub unsafe extern "C" fn patch_seq_string_starts_with(stack: Stack) -> Stack {
107 assert!(!stack.is_null(), "string_starts_with: stack is empty");
108
109 let (stack, prefix_val) = unsafe { pop(stack) };
110 assert!(!stack.is_null(), "string_starts_with: need two strings");
111 let (stack, str_val) = unsafe { pop(stack) };
112
113 match (str_val, prefix_val) {
114 (Value::String(s), Value::String(prefix)) => {
115 let starts = s.as_str().starts_with(prefix.as_str());
116 let result = if starts { 1 } else { 0 };
117 unsafe { push(stack, Value::Int(result)) }
118 }
119 _ => panic!("string_starts_with: expected two strings on stack"),
120 }
121}
122
123#[unsafe(no_mangle)]
130pub unsafe extern "C" fn patch_seq_string_concat(stack: Stack) -> Stack {
131 assert!(!stack.is_null(), "string_concat: stack is empty");
132
133 let (stack, str2_val) = unsafe { pop(stack) };
134 assert!(!stack.is_null(), "string_concat: need two strings");
135 let (stack, str1_val) = unsafe { pop(stack) };
136
137 match (str1_val, str2_val) {
138 (Value::String(s1), Value::String(s2)) => {
139 let result = format!("{}{}", s1.as_str(), s2.as_str());
140 unsafe { push(stack, Value::String(global_string(result))) }
141 }
142 _ => panic!("string_concat: expected two strings on stack"),
143 }
144}
145
146#[unsafe(no_mangle)]
156pub unsafe extern "C" fn patch_seq_string_length(stack: Stack) -> Stack {
157 assert!(!stack.is_null(), "string_length: stack is empty");
158
159 let (stack, str_val) = unsafe { pop(stack) };
160
161 match str_val {
162 Value::String(s) => {
163 let len = s.as_str().chars().count() as i64;
164 unsafe { push(stack, Value::Int(len)) }
165 }
166 _ => panic!("string_length: expected String on stack"),
167 }
168}
169
170#[unsafe(no_mangle)]
179pub unsafe extern "C" fn patch_seq_string_byte_length(stack: Stack) -> Stack {
180 assert!(!stack.is_null(), "string_byte_length: stack is empty");
181
182 let (stack, str_val) = unsafe { pop(stack) };
183
184 match str_val {
185 Value::String(s) => {
186 let len = s.as_str().len() as i64;
187 unsafe { push(stack, Value::Int(len)) }
188 }
189 _ => panic!("string_byte_length: expected String on stack"),
190 }
191}
192
193#[unsafe(no_mangle)]
203pub unsafe extern "C" fn patch_seq_string_char_at(stack: Stack) -> Stack {
204 assert!(!stack.is_null(), "string_char_at: stack is empty");
205
206 let (stack, index_val) = unsafe { pop(stack) };
207 assert!(!stack.is_null(), "string_char_at: need string and index");
208 let (stack, str_val) = unsafe { pop(stack) };
209
210 match (str_val, index_val) {
211 (Value::String(s), Value::Int(index)) => {
212 let result = if index < 0 {
213 -1
214 } else {
215 s.as_str()
216 .chars()
217 .nth(index as usize)
218 .map(|c| c as i64)
219 .unwrap_or(-1)
220 };
221 unsafe { push(stack, Value::Int(result)) }
222 }
223 _ => panic!("string_char_at: expected String and Int on stack"),
224 }
225}
226
227#[unsafe(no_mangle)]
243pub unsafe extern "C" fn patch_seq_string_substring(stack: Stack) -> Stack {
244 assert!(!stack.is_null(), "string_substring: stack is empty");
245
246 let (stack, len_val) = unsafe { pop(stack) };
247 assert!(
248 !stack.is_null(),
249 "string_substring: need string, start, len"
250 );
251 let (stack, start_val) = unsafe { pop(stack) };
252 assert!(
253 !stack.is_null(),
254 "string_substring: need string, start, len"
255 );
256 let (stack, str_val) = unsafe { pop(stack) };
257
258 match (str_val, start_val, len_val) {
259 (Value::String(s), Value::Int(start), Value::Int(len)) => {
260 let result = if start < 0 || len < 0 {
261 String::new()
262 } else {
263 s.as_str()
264 .chars()
265 .skip(start as usize)
266 .take(len as usize)
267 .collect()
268 };
269 unsafe { push(stack, Value::String(global_string(result))) }
270 }
271 _ => panic!("string_substring: expected String, Int, Int on stack"),
272 }
273}
274
275#[unsafe(no_mangle)]
285pub unsafe extern "C" fn patch_seq_char_to_string(stack: Stack) -> Stack {
286 assert!(!stack.is_null(), "char_to_string: stack is empty");
287
288 let (stack, code_point_val) = unsafe { pop(stack) };
289
290 match code_point_val {
291 Value::Int(code_point) => {
292 let result = if !(0..=0x10FFFF).contains(&code_point) {
293 String::new()
295 } else {
296 match char::from_u32(code_point as u32) {
297 Some(c) => c.to_string(),
298 None => String::new(), }
300 };
301 unsafe { push(stack, Value::String(global_string(result))) }
302 }
303 _ => panic!("char_to_string: expected Int on stack"),
304 }
305}
306
307#[unsafe(no_mangle)]
317pub unsafe extern "C" fn patch_seq_string_find(stack: Stack) -> Stack {
318 assert!(!stack.is_null(), "string_find: stack is empty");
319
320 let (stack, needle_val) = unsafe { pop(stack) };
321 assert!(!stack.is_null(), "string_find: need string and needle");
322 let (stack, str_val) = unsafe { pop(stack) };
323
324 match (str_val, needle_val) {
325 (Value::String(haystack), Value::String(needle)) => {
326 let haystack_str = haystack.as_str();
327 let needle_str = needle.as_str();
328
329 let result = match haystack_str.find(needle_str) {
331 Some(byte_pos) => {
332 haystack_str[..byte_pos].chars().count() as i64
334 }
335 None => -1,
336 };
337 unsafe { push(stack, Value::Int(result)) }
338 }
339 _ => panic!("string_find: expected two Strings on stack"),
340 }
341}
342
343#[unsafe(no_mangle)]
350pub unsafe extern "C" fn patch_seq_string_trim(stack: Stack) -> Stack {
351 assert!(!stack.is_null(), "string_trim: stack is empty");
352
353 let (stack, str_val) = unsafe { pop(stack) };
354
355 match str_val {
356 Value::String(s) => {
357 let trimmed = s.as_str().trim();
358 unsafe { push(stack, Value::String(global_string(trimmed.to_owned()))) }
359 }
360 _ => panic!("string_trim: expected String on stack"),
361 }
362}
363
364#[unsafe(no_mangle)]
371pub unsafe extern "C" fn patch_seq_string_to_upper(stack: Stack) -> Stack {
372 assert!(!stack.is_null(), "string_to_upper: stack is empty");
373
374 let (stack, str_val) = unsafe { pop(stack) };
375
376 match str_val {
377 Value::String(s) => {
378 let upper = s.as_str().to_uppercase();
379 unsafe { push(stack, Value::String(global_string(upper))) }
380 }
381 _ => panic!("string_to_upper: expected String on stack"),
382 }
383}
384
385#[unsafe(no_mangle)]
392pub unsafe extern "C" fn patch_seq_string_to_lower(stack: Stack) -> Stack {
393 assert!(!stack.is_null(), "string_to_lower: stack is empty");
394
395 let (stack, str_val) = unsafe { pop(stack) };
396
397 match str_val {
398 Value::String(s) => {
399 let lower = s.as_str().to_lowercase();
400 unsafe { push(stack, Value::String(global_string(lower))) }
401 }
402 _ => panic!("string_to_lower: expected String on stack"),
403 }
404}
405
406#[unsafe(no_mangle)]
413pub unsafe extern "C" fn patch_seq_string_equal(stack: Stack) -> Stack {
414 assert!(!stack.is_null(), "string_equal: stack is empty");
415
416 let (stack, str2_val) = unsafe { pop(stack) };
417 assert!(!stack.is_null(), "string_equal: need two strings");
418 let (stack, str1_val) = unsafe { pop(stack) };
419
420 match (str1_val, str2_val) {
421 (Value::String(s1), Value::String(s2)) => {
422 let equal = s1.as_str() == s2.as_str();
423 let result = if equal { 1 } else { 0 };
424 unsafe { push(stack, Value::Int(result)) }
425 }
426 _ => panic!("string_equal: expected two strings on stack"),
427 }
428}
429
430#[unsafe(no_mangle)]
447pub unsafe extern "C" fn patch_seq_json_escape(stack: Stack) -> Stack {
448 assert!(!stack.is_null(), "json_escape: stack is empty");
449
450 let (stack, value) = unsafe { pop(stack) };
451
452 match value {
453 Value::String(s) => {
454 let input = s.as_str();
455 let mut result = String::with_capacity(input.len() + 16);
456
457 for ch in input.chars() {
458 match ch {
459 '"' => result.push_str("\\\""),
460 '\\' => result.push_str("\\\\"),
461 '\n' => result.push_str("\\n"),
462 '\r' => result.push_str("\\r"),
463 '\t' => result.push_str("\\t"),
464 '\x08' => result.push_str("\\b"), '\x0C' => result.push_str("\\f"), c if c.is_control() => {
469 result.push_str(&format!("\\u{:04X}", c as u32));
470 }
471 c => result.push(c),
472 }
473 }
474
475 unsafe { push(stack, Value::String(global_string(result))) }
476 }
477 _ => panic!("json_escape: expected String on stack"),
478 }
479}
480
481#[unsafe(no_mangle)]
490pub unsafe extern "C" fn patch_seq_string_to_int(stack: Stack) -> Stack {
491 assert!(!stack.is_null(), "string->int: stack is empty");
492 let (stack, val) = unsafe { pop(stack) };
493
494 match val {
495 Value::String(s) => match s.as_str().trim().parse::<i64>() {
496 Ok(i) => {
497 let stack = unsafe { push(stack, Value::Int(i)) };
498 unsafe { push(stack, Value::Int(1)) }
499 }
500 Err(_) => {
501 let stack = unsafe { push(stack, Value::Int(0)) };
502 unsafe { push(stack, Value::Int(0)) }
503 }
504 },
505 _ => panic!("string->int: expected String on stack"),
506 }
507}
508
509#[unsafe(no_mangle)]
519pub unsafe extern "C" fn patch_seq_string_chomp(stack: Stack) -> Stack {
520 assert!(!stack.is_null(), "string_chomp: stack is empty");
521
522 let (stack, str_val) = unsafe { pop(stack) };
523
524 match str_val {
525 Value::String(s) => {
526 let mut result = s.as_str().to_owned();
527 if result.ends_with('\n') {
528 result.pop();
529 if result.ends_with('\r') {
530 result.pop();
531 }
532 }
533 unsafe { push(stack, Value::String(global_string(result))) }
534 }
535 _ => panic!("string_chomp: expected String on stack"),
536 }
537}
538
539pub use patch_seq_char_to_string as char_to_string;
541pub use patch_seq_json_escape as json_escape;
542pub use patch_seq_string_byte_length as string_byte_length;
543pub use patch_seq_string_char_at as string_char_at;
544pub use patch_seq_string_chomp as string_chomp;
545pub use patch_seq_string_concat as string_concat;
546pub use patch_seq_string_contains as string_contains;
547pub use patch_seq_string_empty as string_empty;
548pub use patch_seq_string_equal as string_equal;
549pub use patch_seq_string_find as string_find;
550pub use patch_seq_string_length as string_length;
551pub use patch_seq_string_split as string_split;
552pub use patch_seq_string_starts_with as string_starts_with;
553pub use patch_seq_string_substring as string_substring;
554pub use patch_seq_string_to_int as string_to_int;
555pub use patch_seq_string_to_lower as string_to_lower;
556pub use patch_seq_string_to_upper as string_to_upper;
557pub use patch_seq_string_trim as string_trim;
558
559#[cfg(test)]
560mod tests {
561 use super::*;
562
563 #[test]
564 fn test_string_split_simple() {
565 unsafe {
566 let stack = std::ptr::null_mut();
567 let stack = push(stack, Value::String(global_string("a b c".to_owned())));
568 let stack = push(stack, Value::String(global_string(" ".to_owned())));
569
570 let stack = string_split(stack);
571
572 let (stack, result) = pop(stack);
574 match result {
575 Value::Variant(v) => {
576 assert_eq!(v.tag, 0);
577 assert_eq!(v.fields.len(), 3);
578 assert_eq!(v.fields[0], Value::String(global_string("a".to_owned())));
579 assert_eq!(v.fields[1], Value::String(global_string("b".to_owned())));
580 assert_eq!(v.fields[2], Value::String(global_string("c".to_owned())));
581 }
582 _ => panic!("Expected Variant, got {:?}", result),
583 }
584
585 assert!(stack.is_null());
586 }
587 }
588
589 #[test]
590 fn test_string_split_empty() {
591 unsafe {
592 let stack = std::ptr::null_mut();
593 let stack = push(stack, Value::String(global_string("".to_owned())));
594 let stack = push(stack, Value::String(global_string(" ".to_owned())));
595
596 let stack = string_split(stack);
597
598 let (stack, result) = pop(stack);
600 match result {
601 Value::Variant(v) => {
602 assert_eq!(v.tag, 0);
603 assert_eq!(v.fields.len(), 1);
604 assert_eq!(v.fields[0], Value::String(global_string("".to_owned())));
605 }
606 _ => panic!("Expected Variant, got {:?}", result),
607 }
608
609 assert!(stack.is_null());
610 }
611 }
612
613 #[test]
614 fn test_string_empty_true() {
615 unsafe {
616 let stack = std::ptr::null_mut();
617 let stack = push(stack, Value::String(global_string("".to_owned())));
618
619 let stack = string_empty(stack);
620
621 let (stack, result) = pop(stack);
622 assert_eq!(result, Value::Int(1));
623 assert!(stack.is_null());
624 }
625 }
626
627 #[test]
628 fn test_string_empty_false() {
629 unsafe {
630 let stack = std::ptr::null_mut();
631 let stack = push(stack, Value::String(global_string("hello".to_owned())));
632
633 let stack = string_empty(stack);
634
635 let (stack, result) = pop(stack);
636 assert_eq!(result, Value::Int(0));
637 assert!(stack.is_null());
638 }
639 }
640
641 #[test]
642 fn test_string_contains_true() {
643 unsafe {
644 let stack = std::ptr::null_mut();
645 let stack = push(
646 stack,
647 Value::String(global_string("hello world".to_owned())),
648 );
649 let stack = push(stack, Value::String(global_string("world".to_owned())));
650
651 let stack = string_contains(stack);
652
653 let (stack, result) = pop(stack);
654 assert_eq!(result, Value::Int(1));
655 assert!(stack.is_null());
656 }
657 }
658
659 #[test]
660 fn test_string_contains_false() {
661 unsafe {
662 let stack = std::ptr::null_mut();
663 let stack = push(
664 stack,
665 Value::String(global_string("hello world".to_owned())),
666 );
667 let stack = push(stack, Value::String(global_string("foo".to_owned())));
668
669 let stack = string_contains(stack);
670
671 let (stack, result) = pop(stack);
672 assert_eq!(result, Value::Int(0));
673 assert!(stack.is_null());
674 }
675 }
676
677 #[test]
678 fn test_string_starts_with_true() {
679 unsafe {
680 let stack = std::ptr::null_mut();
681 let stack = push(
682 stack,
683 Value::String(global_string("hello world".to_owned())),
684 );
685 let stack = push(stack, Value::String(global_string("hello".to_owned())));
686
687 let stack = string_starts_with(stack);
688
689 let (stack, result) = pop(stack);
690 assert_eq!(result, Value::Int(1));
691 assert!(stack.is_null());
692 }
693 }
694
695 #[test]
696 fn test_string_starts_with_false() {
697 unsafe {
698 let stack = std::ptr::null_mut();
699 let stack = push(
700 stack,
701 Value::String(global_string("hello world".to_owned())),
702 );
703 let stack = push(stack, Value::String(global_string("world".to_owned())));
704
705 let stack = string_starts_with(stack);
706
707 let (stack, result) = pop(stack);
708 assert_eq!(result, Value::Int(0));
709 assert!(stack.is_null());
710 }
711 }
712
713 #[test]
714 fn test_http_request_line_parsing() {
715 unsafe {
717 let stack = std::ptr::null_mut();
718 let stack = push(
719 stack,
720 Value::String(global_string("GET /api/users HTTP/1.1".to_owned())),
721 );
722 let stack = push(stack, Value::String(global_string(" ".to_owned())));
723
724 let stack = string_split(stack);
725
726 let (stack, result) = pop(stack);
728 match result {
729 Value::Variant(v) => {
730 assert_eq!(v.tag, 0);
731 assert_eq!(v.fields.len(), 3);
732 assert_eq!(v.fields[0], Value::String(global_string("GET".to_owned())));
733 assert_eq!(
734 v.fields[1],
735 Value::String(global_string("/api/users".to_owned()))
736 );
737 assert_eq!(
738 v.fields[2],
739 Value::String(global_string("HTTP/1.1".to_owned()))
740 );
741 }
742 _ => panic!("Expected Variant, got {:?}", result),
743 }
744
745 assert!(stack.is_null());
746 }
747 }
748
749 #[test]
750 fn test_path_routing() {
751 unsafe {
753 let stack = std::ptr::null_mut();
754 let stack = push(stack, Value::String(global_string("/api/users".to_owned())));
755 let stack = push(stack, Value::String(global_string("/api/".to_owned())));
756
757 let stack = string_starts_with(stack);
758
759 let (stack, result) = pop(stack);
760 assert_eq!(result, Value::Int(1));
761 assert!(stack.is_null());
762 }
763 }
764
765 #[test]
766 fn test_string_concat() {
767 unsafe {
768 let stack = std::ptr::null_mut();
769 let stack = push(stack, Value::String(global_string("Hello, ".to_owned())));
770 let stack = push(stack, Value::String(global_string("World!".to_owned())));
771
772 let stack = string_concat(stack);
773
774 let (stack, result) = pop(stack);
775 assert_eq!(
776 result,
777 Value::String(global_string("Hello, World!".to_owned()))
778 );
779 assert!(stack.is_null());
780 }
781 }
782
783 #[test]
784 fn test_string_length() {
785 unsafe {
786 let stack = std::ptr::null_mut();
787 let stack = push(stack, Value::String(global_string("Hello".to_owned())));
788
789 let stack = string_length(stack);
790
791 let (stack, result) = pop(stack);
792 assert_eq!(result, Value::Int(5));
793 assert!(stack.is_null());
794 }
795 }
796
797 #[test]
798 fn test_string_length_empty() {
799 unsafe {
800 let stack = std::ptr::null_mut();
801 let stack = push(stack, Value::String(global_string("".to_owned())));
802
803 let stack = string_length(stack);
804
805 let (stack, result) = pop(stack);
806 assert_eq!(result, Value::Int(0));
807 assert!(stack.is_null());
808 }
809 }
810
811 #[test]
812 fn test_string_trim() {
813 unsafe {
814 let stack = std::ptr::null_mut();
815 let stack = push(
816 stack,
817 Value::String(global_string(" Hello, World! ".to_owned())),
818 );
819
820 let stack = string_trim(stack);
821
822 let (stack, result) = pop(stack);
823 assert_eq!(
824 result,
825 Value::String(global_string("Hello, World!".to_owned()))
826 );
827 assert!(stack.is_null());
828 }
829 }
830
831 #[test]
832 fn test_string_to_upper() {
833 unsafe {
834 let stack = std::ptr::null_mut();
835 let stack = push(
836 stack,
837 Value::String(global_string("Hello, World!".to_owned())),
838 );
839
840 let stack = string_to_upper(stack);
841
842 let (stack, result) = pop(stack);
843 assert_eq!(
844 result,
845 Value::String(global_string("HELLO, WORLD!".to_owned()))
846 );
847 assert!(stack.is_null());
848 }
849 }
850
851 #[test]
852 fn test_string_to_lower() {
853 unsafe {
854 let stack = std::ptr::null_mut();
855 let stack = push(
856 stack,
857 Value::String(global_string("Hello, World!".to_owned())),
858 );
859
860 let stack = string_to_lower(stack);
861
862 let (stack, result) = pop(stack);
863 assert_eq!(
864 result,
865 Value::String(global_string("hello, world!".to_owned()))
866 );
867 assert!(stack.is_null());
868 }
869 }
870
871 #[test]
872 fn test_http_header_content_length() {
873 unsafe {
875 let stack = std::ptr::null_mut();
876 let stack = push(
877 stack,
878 Value::String(global_string("Content-Length: ".to_owned())),
879 );
880 let stack = push(stack, Value::String(global_string("42".to_owned())));
881
882 let stack = string_concat(stack);
883
884 let (stack, result) = pop(stack);
885 assert_eq!(
886 result,
887 Value::String(global_string("Content-Length: 42".to_owned()))
888 );
889 assert!(stack.is_null());
890 }
891 }
892
893 #[test]
894 fn test_string_equal_true() {
895 unsafe {
896 let stack = std::ptr::null_mut();
897 let stack = push(stack, Value::String(global_string("hello".to_owned())));
898 let stack = push(stack, Value::String(global_string("hello".to_owned())));
899
900 let stack = string_equal(stack);
901
902 let (stack, result) = pop(stack);
903 assert_eq!(result, Value::Int(1));
904 assert!(stack.is_null());
905 }
906 }
907
908 #[test]
909 fn test_string_equal_false() {
910 unsafe {
911 let stack = std::ptr::null_mut();
912 let stack = push(stack, Value::String(global_string("hello".to_owned())));
913 let stack = push(stack, Value::String(global_string("world".to_owned())));
914
915 let stack = string_equal(stack);
916
917 let (stack, result) = pop(stack);
918 assert_eq!(result, Value::Int(0));
919 assert!(stack.is_null());
920 }
921 }
922
923 #[test]
924 fn test_string_equal_empty_strings() {
925 unsafe {
926 let stack = std::ptr::null_mut();
927 let stack = push(stack, Value::String(global_string("".to_owned())));
928 let stack = push(stack, Value::String(global_string("".to_owned())));
929
930 let stack = string_equal(stack);
931
932 let (stack, result) = pop(stack);
933 assert_eq!(result, Value::Int(1));
934 assert!(stack.is_null());
935 }
936 }
937
938 #[test]
941 fn test_string_length_utf8() {
942 unsafe {
944 let stack = std::ptr::null_mut();
945 let stack = push(stack, Value::String(global_string("héllo".to_owned())));
946
947 let stack = string_length(stack);
948
949 let (stack, result) = pop(stack);
950 assert_eq!(result, Value::Int(5)); assert!(stack.is_null());
952 }
953 }
954
955 #[test]
956 fn test_string_length_emoji() {
957 unsafe {
959 let stack = std::ptr::null_mut();
960 let stack = push(stack, Value::String(global_string("hi🎉".to_owned())));
961
962 let stack = string_length(stack);
963
964 let (stack, result) = pop(stack);
965 assert_eq!(result, Value::Int(3)); assert!(stack.is_null());
967 }
968 }
969
970 #[test]
971 fn test_string_byte_length_ascii() {
972 unsafe {
973 let stack = std::ptr::null_mut();
974 let stack = push(stack, Value::String(global_string("hello".to_owned())));
975
976 let stack = string_byte_length(stack);
977
978 let (stack, result) = pop(stack);
979 assert_eq!(result, Value::Int(5)); assert!(stack.is_null());
981 }
982 }
983
984 #[test]
985 fn test_string_byte_length_utf8() {
986 unsafe {
988 let stack = std::ptr::null_mut();
989 let stack = push(stack, Value::String(global_string("héllo".to_owned())));
990
991 let stack = string_byte_length(stack);
992
993 let (stack, result) = pop(stack);
994 assert_eq!(result, Value::Int(6)); assert!(stack.is_null());
996 }
997 }
998
999 #[test]
1000 fn test_string_char_at_ascii() {
1001 unsafe {
1002 let stack = std::ptr::null_mut();
1003 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1004 let stack = push(stack, Value::Int(0));
1005
1006 let stack = string_char_at(stack);
1007
1008 let (stack, result) = pop(stack);
1009 assert_eq!(result, Value::Int(104)); assert!(stack.is_null());
1011 }
1012 }
1013
1014 #[test]
1015 fn test_string_char_at_utf8() {
1016 unsafe {
1018 let stack = std::ptr::null_mut();
1019 let stack = push(stack, Value::String(global_string("héllo".to_owned())));
1020 let stack = push(stack, Value::Int(1));
1021
1022 let stack = string_char_at(stack);
1023
1024 let (stack, result) = pop(stack);
1025 assert_eq!(result, Value::Int(233)); assert!(stack.is_null());
1027 }
1028 }
1029
1030 #[test]
1031 fn test_string_char_at_out_of_bounds() {
1032 unsafe {
1033 let stack = std::ptr::null_mut();
1034 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1035 let stack = push(stack, Value::Int(10)); let stack = string_char_at(stack);
1038
1039 let (stack, result) = pop(stack);
1040 assert_eq!(result, Value::Int(-1));
1041 assert!(stack.is_null());
1042 }
1043 }
1044
1045 #[test]
1046 fn test_string_char_at_negative() {
1047 unsafe {
1048 let stack = std::ptr::null_mut();
1049 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1050 let stack = push(stack, Value::Int(-1));
1051
1052 let stack = string_char_at(stack);
1053
1054 let (stack, result) = pop(stack);
1055 assert_eq!(result, Value::Int(-1));
1056 assert!(stack.is_null());
1057 }
1058 }
1059
1060 #[test]
1061 fn test_string_substring_simple() {
1062 unsafe {
1063 let stack = std::ptr::null_mut();
1064 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1065 let stack = push(stack, Value::Int(1)); let stack = push(stack, Value::Int(3)); let stack = string_substring(stack);
1069
1070 let (stack, result) = pop(stack);
1071 assert_eq!(result, Value::String(global_string("ell".to_owned())));
1072 assert!(stack.is_null());
1073 }
1074 }
1075
1076 #[test]
1077 fn test_string_substring_utf8() {
1078 unsafe {
1080 let stack = std::ptr::null_mut();
1081 let stack = push(stack, Value::String(global_string("héllo".to_owned())));
1082 let stack = push(stack, Value::Int(1)); let stack = push(stack, Value::Int(3)); let stack = string_substring(stack);
1086
1087 let (stack, result) = pop(stack);
1088 assert_eq!(result, Value::String(global_string("éll".to_owned())));
1089 assert!(stack.is_null());
1090 }
1091 }
1092
1093 #[test]
1094 fn test_string_substring_clamp() {
1095 unsafe {
1097 let stack = std::ptr::null_mut();
1098 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1099 let stack = push(stack, Value::Int(2)); let stack = push(stack, Value::Int(100)); let stack = string_substring(stack);
1103
1104 let (stack, result) = pop(stack);
1105 assert_eq!(result, Value::String(global_string("llo".to_owned())));
1106 assert!(stack.is_null());
1107 }
1108 }
1109
1110 #[test]
1111 fn test_string_substring_beyond_end() {
1112 unsafe {
1114 let stack = std::ptr::null_mut();
1115 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1116 let stack = push(stack, Value::Int(10)); let stack = push(stack, Value::Int(3)); let stack = string_substring(stack);
1120
1121 let (stack, result) = pop(stack);
1122 assert_eq!(result, Value::String(global_string("".to_owned())));
1123 assert!(stack.is_null());
1124 }
1125 }
1126
1127 #[test]
1128 fn test_char_to_string_ascii() {
1129 unsafe {
1130 let stack = std::ptr::null_mut();
1131 let stack = push(stack, Value::Int(65)); let stack = char_to_string(stack);
1134
1135 let (stack, result) = pop(stack);
1136 assert_eq!(result, Value::String(global_string("A".to_owned())));
1137 assert!(stack.is_null());
1138 }
1139 }
1140
1141 #[test]
1142 fn test_char_to_string_utf8() {
1143 unsafe {
1144 let stack = std::ptr::null_mut();
1145 let stack = push(stack, Value::Int(233)); let stack = char_to_string(stack);
1148
1149 let (stack, result) = pop(stack);
1150 assert_eq!(result, Value::String(global_string("é".to_owned())));
1151 assert!(stack.is_null());
1152 }
1153 }
1154
1155 #[test]
1156 fn test_char_to_string_newline() {
1157 unsafe {
1158 let stack = std::ptr::null_mut();
1159 let stack = push(stack, Value::Int(10)); let stack = char_to_string(stack);
1162
1163 let (stack, result) = pop(stack);
1164 assert_eq!(result, Value::String(global_string("\n".to_owned())));
1165 assert!(stack.is_null());
1166 }
1167 }
1168
1169 #[test]
1170 fn test_char_to_string_invalid() {
1171 unsafe {
1172 let stack = std::ptr::null_mut();
1173 let stack = push(stack, Value::Int(-1)); let stack = char_to_string(stack);
1176
1177 let (stack, result) = pop(stack);
1178 assert_eq!(result, Value::String(global_string("".to_owned())));
1179 assert!(stack.is_null());
1180 }
1181 }
1182
1183 #[test]
1184 fn test_string_find_found() {
1185 unsafe {
1186 let stack = std::ptr::null_mut();
1187 let stack = push(
1188 stack,
1189 Value::String(global_string("hello world".to_owned())),
1190 );
1191 let stack = push(stack, Value::String(global_string("world".to_owned())));
1192
1193 let stack = string_find(stack);
1194
1195 let (stack, result) = pop(stack);
1196 assert_eq!(result, Value::Int(6)); assert!(stack.is_null());
1198 }
1199 }
1200
1201 #[test]
1202 fn test_string_find_not_found() {
1203 unsafe {
1204 let stack = std::ptr::null_mut();
1205 let stack = push(
1206 stack,
1207 Value::String(global_string("hello world".to_owned())),
1208 );
1209 let stack = push(stack, Value::String(global_string("xyz".to_owned())));
1210
1211 let stack = string_find(stack);
1212
1213 let (stack, result) = pop(stack);
1214 assert_eq!(result, Value::Int(-1));
1215 assert!(stack.is_null());
1216 }
1217 }
1218
1219 #[test]
1220 fn test_string_find_first_match() {
1221 unsafe {
1223 let stack = std::ptr::null_mut();
1224 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1225 let stack = push(stack, Value::String(global_string("l".to_owned())));
1226
1227 let stack = string_find(stack);
1228
1229 let (stack, result) = pop(stack);
1230 assert_eq!(result, Value::Int(2)); assert!(stack.is_null());
1232 }
1233 }
1234
1235 #[test]
1236 fn test_string_find_utf8() {
1237 unsafe {
1239 let stack = std::ptr::null_mut();
1240 let stack = push(
1241 stack,
1242 Value::String(global_string("héllo wörld".to_owned())),
1243 );
1244 let stack = push(stack, Value::String(global_string("wörld".to_owned())));
1245
1246 let stack = string_find(stack);
1247
1248 let (stack, result) = pop(stack);
1249 assert_eq!(result, Value::Int(6)); assert!(stack.is_null());
1251 }
1252 }
1253
1254 #[test]
1257 fn test_json_escape_quotes() {
1258 unsafe {
1259 let stack = std::ptr::null_mut();
1260 let stack = push(
1261 stack,
1262 Value::String(global_string("hello \"world\"".to_owned())),
1263 );
1264
1265 let stack = json_escape(stack);
1266
1267 let (stack, result) = pop(stack);
1268 assert_eq!(
1269 result,
1270 Value::String(global_string("hello \\\"world\\\"".to_owned()))
1271 );
1272 assert!(stack.is_null());
1273 }
1274 }
1275
1276 #[test]
1277 fn test_json_escape_backslash() {
1278 unsafe {
1279 let stack = std::ptr::null_mut();
1280 let stack = push(
1281 stack,
1282 Value::String(global_string("path\\to\\file".to_owned())),
1283 );
1284
1285 let stack = json_escape(stack);
1286
1287 let (stack, result) = pop(stack);
1288 assert_eq!(
1289 result,
1290 Value::String(global_string("path\\\\to\\\\file".to_owned()))
1291 );
1292 assert!(stack.is_null());
1293 }
1294 }
1295
1296 #[test]
1297 fn test_json_escape_newline_tab() {
1298 unsafe {
1299 let stack = std::ptr::null_mut();
1300 let stack = push(
1301 stack,
1302 Value::String(global_string("line1\nline2\ttabbed".to_owned())),
1303 );
1304
1305 let stack = json_escape(stack);
1306
1307 let (stack, result) = pop(stack);
1308 assert_eq!(
1309 result,
1310 Value::String(global_string("line1\\nline2\\ttabbed".to_owned()))
1311 );
1312 assert!(stack.is_null());
1313 }
1314 }
1315
1316 #[test]
1317 fn test_json_escape_carriage_return() {
1318 unsafe {
1319 let stack = std::ptr::null_mut();
1320 let stack = push(
1321 stack,
1322 Value::String(global_string("line1\r\nline2".to_owned())),
1323 );
1324
1325 let stack = json_escape(stack);
1326
1327 let (stack, result) = pop(stack);
1328 assert_eq!(
1329 result,
1330 Value::String(global_string("line1\\r\\nline2".to_owned()))
1331 );
1332 assert!(stack.is_null());
1333 }
1334 }
1335
1336 #[test]
1337 fn test_json_escape_control_chars() {
1338 unsafe {
1339 let stack = std::ptr::null_mut();
1340 let stack = push(
1342 stack,
1343 Value::String(global_string("a\x08b\x0Cc".to_owned())),
1344 );
1345
1346 let stack = json_escape(stack);
1347
1348 let (stack, result) = pop(stack);
1349 assert_eq!(result, Value::String(global_string("a\\bb\\fc".to_owned())));
1350 assert!(stack.is_null());
1351 }
1352 }
1353
1354 #[test]
1355 fn test_json_escape_unicode_control() {
1356 unsafe {
1357 let stack = std::ptr::null_mut();
1358 let stack = push(stack, Value::String(global_string("a\x00b".to_owned())));
1360
1361 let stack = json_escape(stack);
1362
1363 let (stack, result) = pop(stack);
1364 assert_eq!(result, Value::String(global_string("a\\u0000b".to_owned())));
1365 assert!(stack.is_null());
1366 }
1367 }
1368
1369 #[test]
1370 fn test_json_escape_mixed_special_chars() {
1371 unsafe {
1373 let stack = std::ptr::null_mut();
1374 let stack = push(
1375 stack,
1376 Value::String(global_string("Line 1\nLine \"2\"\ttab\r\n".to_owned())),
1377 );
1378
1379 let stack = json_escape(stack);
1380
1381 let (stack, result) = pop(stack);
1382 assert_eq!(
1383 result,
1384 Value::String(global_string(
1385 "Line 1\\nLine \\\"2\\\"\\ttab\\r\\n".to_owned()
1386 ))
1387 );
1388 assert!(stack.is_null());
1389 }
1390 }
1391
1392 #[test]
1393 fn test_json_escape_no_change() {
1394 unsafe {
1396 let stack = std::ptr::null_mut();
1397 let stack = push(
1398 stack,
1399 Value::String(global_string("Hello, World!".to_owned())),
1400 );
1401
1402 let stack = json_escape(stack);
1403
1404 let (stack, result) = pop(stack);
1405 assert_eq!(
1406 result,
1407 Value::String(global_string("Hello, World!".to_owned()))
1408 );
1409 assert!(stack.is_null());
1410 }
1411 }
1412
1413 #[test]
1414 fn test_json_escape_empty_string() {
1415 unsafe {
1416 let stack = std::ptr::null_mut();
1417 let stack = push(stack, Value::String(global_string("".to_owned())));
1418
1419 let stack = json_escape(stack);
1420
1421 let (stack, result) = pop(stack);
1422 assert_eq!(result, Value::String(global_string("".to_owned())));
1423 assert!(stack.is_null());
1424 }
1425 }
1426
1427 #[test]
1430 fn test_string_to_int_success() {
1431 unsafe {
1432 let stack = std::ptr::null_mut();
1433 let stack = push(stack, Value::String(global_string("42".to_owned())));
1434
1435 let stack = string_to_int(stack);
1436
1437 let (stack, success) = pop(stack);
1438 let (stack, value) = pop(stack);
1439 assert_eq!(success, Value::Int(1));
1440 assert_eq!(value, Value::Int(42));
1441 assert!(stack.is_null());
1442 }
1443 }
1444
1445 #[test]
1446 fn test_string_to_int_negative() {
1447 unsafe {
1448 let stack = std::ptr::null_mut();
1449 let stack = push(stack, Value::String(global_string("-99".to_owned())));
1450
1451 let stack = string_to_int(stack);
1452
1453 let (stack, success) = pop(stack);
1454 let (stack, value) = pop(stack);
1455 assert_eq!(success, Value::Int(1));
1456 assert_eq!(value, Value::Int(-99));
1457 assert!(stack.is_null());
1458 }
1459 }
1460
1461 #[test]
1462 fn test_string_to_int_with_whitespace() {
1463 unsafe {
1464 let stack = std::ptr::null_mut();
1465 let stack = push(stack, Value::String(global_string(" 123 ".to_owned())));
1466
1467 let stack = string_to_int(stack);
1468
1469 let (stack, success) = pop(stack);
1470 let (stack, value) = pop(stack);
1471 assert_eq!(success, Value::Int(1));
1472 assert_eq!(value, Value::Int(123));
1473 assert!(stack.is_null());
1474 }
1475 }
1476
1477 #[test]
1478 fn test_string_to_int_failure() {
1479 unsafe {
1480 let stack = std::ptr::null_mut();
1481 let stack = push(
1482 stack,
1483 Value::String(global_string("not a number".to_owned())),
1484 );
1485
1486 let stack = string_to_int(stack);
1487
1488 let (stack, success) = pop(stack);
1489 let (stack, value) = pop(stack);
1490 assert_eq!(success, Value::Int(0));
1491 assert_eq!(value, Value::Int(0));
1492 assert!(stack.is_null());
1493 }
1494 }
1495
1496 #[test]
1497 fn test_string_to_int_empty() {
1498 unsafe {
1499 let stack = std::ptr::null_mut();
1500 let stack = push(stack, Value::String(global_string("".to_owned())));
1501
1502 let stack = string_to_int(stack);
1503
1504 let (stack, success) = pop(stack);
1505 let (stack, value) = pop(stack);
1506 assert_eq!(success, Value::Int(0));
1507 assert_eq!(value, Value::Int(0));
1508 assert!(stack.is_null());
1509 }
1510 }
1511
1512 #[test]
1513 fn test_string_to_int_leading_zeros() {
1514 unsafe {
1515 let stack = std::ptr::null_mut();
1516 let stack = push(stack, Value::String(global_string("007".to_owned())));
1517
1518 let stack = string_to_int(stack);
1519
1520 let (stack, success) = pop(stack);
1521 let (stack, value) = pop(stack);
1522 assert_eq!(success, Value::Int(1));
1523 assert_eq!(value, Value::Int(7));
1524 assert!(stack.is_null());
1525 }
1526 }
1527}