1use crate::seqstring::global_string;
14use crate::stack::{Stack, pop, push};
15use crate::value::Value;
16use std::sync::Arc;
17
18#[unsafe(no_mangle)]
27pub unsafe extern "C" fn patch_seq_string_split(stack: Stack) -> Stack {
28 use crate::value::VariantData;
29
30 assert!(!stack.is_null(), "string_split: stack is empty");
31
32 let (stack, delim_val) = unsafe { pop(stack) };
33 assert!(!stack.is_null(), "string_split: need two strings");
34 let (stack, str_val) = unsafe { pop(stack) };
35
36 match (str_val, delim_val) {
37 (Value::String(s), Value::String(d)) => {
38 let fields: Vec<Value> = s
40 .as_str()
41 .split(d.as_str())
42 .map(|part| Value::String(global_string(part.to_owned())))
43 .collect();
44
45 let variant = Value::Variant(Arc::new(VariantData::new(
47 global_string("List".to_string()),
48 fields,
49 )));
50
51 unsafe { push(stack, variant) }
52 }
53 _ => panic!("string_split: expected two strings on stack"),
54 }
55}
56
57#[unsafe(no_mangle)]
64pub unsafe extern "C" fn patch_seq_string_empty(stack: Stack) -> Stack {
65 assert!(!stack.is_null(), "string_empty: stack is empty");
66
67 let (stack, value) = unsafe { pop(stack) };
68
69 match value {
70 Value::String(s) => {
71 let is_empty = s.as_str().is_empty();
72 unsafe { push(stack, Value::Bool(is_empty)) }
73 }
74 _ => panic!("string_empty: expected String on stack"),
75 }
76}
77
78#[unsafe(no_mangle)]
85pub unsafe extern "C" fn patch_seq_string_contains(stack: Stack) -> Stack {
86 assert!(!stack.is_null(), "string_contains: stack is empty");
87
88 let (stack, substring_val) = unsafe { pop(stack) };
89 assert!(!stack.is_null(), "string_contains: need two strings");
90 let (stack, str_val) = unsafe { pop(stack) };
91
92 match (str_val, substring_val) {
93 (Value::String(s), Value::String(sub)) => {
94 let contains = s.as_str().contains(sub.as_str());
95 unsafe { push(stack, Value::Bool(contains)) }
96 }
97 _ => panic!("string_contains: expected two strings on stack"),
98 }
99}
100
101#[unsafe(no_mangle)]
108pub unsafe extern "C" fn patch_seq_string_starts_with(stack: Stack) -> Stack {
109 assert!(!stack.is_null(), "string_starts_with: stack is empty");
110
111 let (stack, prefix_val) = unsafe { pop(stack) };
112 assert!(!stack.is_null(), "string_starts_with: need two strings");
113 let (stack, str_val) = unsafe { pop(stack) };
114
115 match (str_val, prefix_val) {
116 (Value::String(s), Value::String(prefix)) => {
117 let starts = s.as_str().starts_with(prefix.as_str());
118 unsafe { push(stack, Value::Bool(starts)) }
119 }
120 _ => panic!("string_starts_with: expected two strings on stack"),
121 }
122}
123
124#[unsafe(no_mangle)]
131pub unsafe extern "C" fn patch_seq_string_concat(stack: Stack) -> Stack {
132 assert!(!stack.is_null(), "string_concat: stack is empty");
133
134 let (stack, str2_val) = unsafe { pop(stack) };
135 assert!(!stack.is_null(), "string_concat: need two strings");
136 let (stack, str1_val) = unsafe { pop(stack) };
137
138 match (str1_val, str2_val) {
139 (Value::String(s1), Value::String(s2)) => {
140 let result = format!("{}{}", s1.as_str(), s2.as_str());
141 unsafe { push(stack, Value::String(global_string(result))) }
142 }
143 _ => panic!("string_concat: expected two strings on stack"),
144 }
145}
146
147#[unsafe(no_mangle)]
157pub unsafe extern "C" fn patch_seq_string_length(stack: Stack) -> Stack {
158 assert!(!stack.is_null(), "string_length: stack is empty");
159
160 let (stack, str_val) = unsafe { pop(stack) };
161
162 match str_val {
163 Value::String(s) => {
164 let len = s.as_str().chars().count() as i64;
165 unsafe { push(stack, Value::Int(len)) }
166 }
167 _ => panic!("string_length: expected String on stack"),
168 }
169}
170
171#[unsafe(no_mangle)]
180pub unsafe extern "C" fn patch_seq_string_byte_length(stack: Stack) -> Stack {
181 assert!(!stack.is_null(), "string_byte_length: stack is empty");
182
183 let (stack, str_val) = unsafe { pop(stack) };
184
185 match str_val {
186 Value::String(s) => {
187 let len = s.as_str().len() as i64;
188 unsafe { push(stack, Value::Int(len)) }
189 }
190 _ => panic!("string_byte_length: expected String on stack"),
191 }
192}
193
194#[unsafe(no_mangle)]
204pub unsafe extern "C" fn patch_seq_string_char_at(stack: Stack) -> Stack {
205 assert!(!stack.is_null(), "string_char_at: stack is empty");
206
207 let (stack, index_val) = unsafe { pop(stack) };
208 assert!(!stack.is_null(), "string_char_at: need string and index");
209 let (stack, str_val) = unsafe { pop(stack) };
210
211 match (str_val, index_val) {
212 (Value::String(s), Value::Int(index)) => {
213 let result = if index < 0 {
214 -1
215 } else {
216 s.as_str()
217 .chars()
218 .nth(index as usize)
219 .map(|c| c as i64)
220 .unwrap_or(-1)
221 };
222 unsafe { push(stack, Value::Int(result)) }
223 }
224 _ => panic!("string_char_at: expected String and Int on stack"),
225 }
226}
227
228#[unsafe(no_mangle)]
244pub unsafe extern "C" fn patch_seq_string_substring(stack: Stack) -> Stack {
245 assert!(!stack.is_null(), "string_substring: stack is empty");
246
247 let (stack, len_val) = unsafe { pop(stack) };
248 assert!(
249 !stack.is_null(),
250 "string_substring: need string, start, len"
251 );
252 let (stack, start_val) = unsafe { pop(stack) };
253 assert!(
254 !stack.is_null(),
255 "string_substring: need string, start, len"
256 );
257 let (stack, str_val) = unsafe { pop(stack) };
258
259 match (str_val, start_val, len_val) {
260 (Value::String(s), Value::Int(start), Value::Int(len)) => {
261 let result = if start < 0 || len < 0 {
262 String::new()
263 } else {
264 s.as_str()
265 .chars()
266 .skip(start as usize)
267 .take(len as usize)
268 .collect()
269 };
270 unsafe { push(stack, Value::String(global_string(result))) }
271 }
272 _ => panic!("string_substring: expected String, Int, Int on stack"),
273 }
274}
275
276#[unsafe(no_mangle)]
286pub unsafe extern "C" fn patch_seq_char_to_string(stack: Stack) -> Stack {
287 assert!(!stack.is_null(), "char_to_string: stack is empty");
288
289 let (stack, code_point_val) = unsafe { pop(stack) };
290
291 match code_point_val {
292 Value::Int(code_point) => {
293 let result = if !(0..=0x10FFFF).contains(&code_point) {
294 String::new()
296 } else {
297 match char::from_u32(code_point as u32) {
298 Some(c) => c.to_string(),
299 None => String::new(), }
301 };
302 unsafe { push(stack, Value::String(global_string(result))) }
303 }
304 _ => panic!("char_to_string: expected Int on stack"),
305 }
306}
307
308#[unsafe(no_mangle)]
318pub unsafe extern "C" fn patch_seq_string_find(stack: Stack) -> Stack {
319 assert!(!stack.is_null(), "string_find: stack is empty");
320
321 let (stack, needle_val) = unsafe { pop(stack) };
322 assert!(!stack.is_null(), "string_find: need string and needle");
323 let (stack, str_val) = unsafe { pop(stack) };
324
325 match (str_val, needle_val) {
326 (Value::String(haystack), Value::String(needle)) => {
327 let haystack_str = haystack.as_str();
328 let needle_str = needle.as_str();
329
330 let result = match haystack_str.find(needle_str) {
332 Some(byte_pos) => {
333 haystack_str[..byte_pos].chars().count() as i64
335 }
336 None => -1,
337 };
338 unsafe { push(stack, Value::Int(result)) }
339 }
340 _ => panic!("string_find: expected two Strings on stack"),
341 }
342}
343
344#[unsafe(no_mangle)]
351pub unsafe extern "C" fn patch_seq_string_trim(stack: Stack) -> Stack {
352 assert!(!stack.is_null(), "string_trim: stack is empty");
353
354 let (stack, str_val) = unsafe { pop(stack) };
355
356 match str_val {
357 Value::String(s) => {
358 let trimmed = s.as_str().trim();
359 unsafe { push(stack, Value::String(global_string(trimmed.to_owned()))) }
360 }
361 _ => panic!("string_trim: expected String on stack"),
362 }
363}
364
365#[unsafe(no_mangle)]
372pub unsafe extern "C" fn patch_seq_string_to_upper(stack: Stack) -> Stack {
373 assert!(!stack.is_null(), "string_to_upper: stack is empty");
374
375 let (stack, str_val) = unsafe { pop(stack) };
376
377 match str_val {
378 Value::String(s) => {
379 let upper = s.as_str().to_uppercase();
380 unsafe { push(stack, Value::String(global_string(upper))) }
381 }
382 _ => panic!("string_to_upper: expected String on stack"),
383 }
384}
385
386#[unsafe(no_mangle)]
393pub unsafe extern "C" fn patch_seq_string_to_lower(stack: Stack) -> Stack {
394 assert!(!stack.is_null(), "string_to_lower: stack is empty");
395
396 let (stack, str_val) = unsafe { pop(stack) };
397
398 match str_val {
399 Value::String(s) => {
400 let lower = s.as_str().to_lowercase();
401 unsafe { push(stack, Value::String(global_string(lower))) }
402 }
403 _ => panic!("string_to_lower: expected String on stack"),
404 }
405}
406
407#[unsafe(no_mangle)]
414pub unsafe extern "C" fn patch_seq_string_equal(stack: Stack) -> Stack {
415 assert!(!stack.is_null(), "string_equal: stack is empty");
416
417 let (stack, str2_val) = unsafe { pop(stack) };
418 assert!(!stack.is_null(), "string_equal: need two strings");
419 let (stack, str1_val) = unsafe { pop(stack) };
420
421 match (str1_val, str2_val) {
422 (Value::String(s1), Value::String(s2)) => {
423 let equal = s1.as_str() == s2.as_str();
424 unsafe { push(stack, Value::Bool(equal)) }
425 }
426 _ => panic!("string_equal: expected two strings on stack"),
427 }
428}
429
430#[unsafe(no_mangle)]
440pub unsafe extern "C" fn patch_seq_symbol_equal(stack: Stack) -> Stack {
441 assert!(!stack.is_null(), "symbol_equal: stack is empty");
442
443 let (stack, sym2_val) = unsafe { pop(stack) };
444 assert!(!stack.is_null(), "symbol_equal: need two symbols");
445 let (stack, sym1_val) = unsafe { pop(stack) };
446
447 match (sym1_val, sym2_val) {
448 (Value::Symbol(s1), Value::Symbol(s2)) => {
449 let equal = if s1.is_interned() && s2.is_interned() {
451 s1.as_ptr() == s2.as_ptr()
452 } else {
453 s1.as_str() == s2.as_str()
455 };
456 unsafe { push(stack, Value::Bool(equal)) }
457 }
458 _ => panic!("symbol_equal: expected two symbols on stack"),
459 }
460}
461
462#[unsafe(no_mangle)]
479pub unsafe extern "C" fn patch_seq_json_escape(stack: Stack) -> Stack {
480 assert!(!stack.is_null(), "json_escape: stack is empty");
481
482 let (stack, value) = unsafe { pop(stack) };
483
484 match value {
485 Value::String(s) => {
486 let input = s.as_str();
487 let mut result = String::with_capacity(input.len() + 16);
488
489 for ch in input.chars() {
490 match ch {
491 '"' => result.push_str("\\\""),
492 '\\' => result.push_str("\\\\"),
493 '\n' => result.push_str("\\n"),
494 '\r' => result.push_str("\\r"),
495 '\t' => result.push_str("\\t"),
496 '\x08' => result.push_str("\\b"), '\x0C' => result.push_str("\\f"), c if c.is_control() => {
501 result.push_str(&format!("\\u{:04X}", c as u32));
502 }
503 c => result.push(c),
504 }
505 }
506
507 unsafe { push(stack, Value::String(global_string(result))) }
508 }
509 _ => panic!("json_escape: expected String on stack"),
510 }
511}
512
513#[unsafe(no_mangle)]
522pub unsafe extern "C" fn patch_seq_string_to_int(stack: Stack) -> Stack {
523 assert!(!stack.is_null(), "string->int: stack is empty");
524 let (stack, val) = unsafe { pop(stack) };
525
526 match val {
527 Value::String(s) => match s.as_str().trim().parse::<i64>() {
528 Ok(i) => {
529 let stack = unsafe { push(stack, Value::Int(i)) };
530 unsafe { push(stack, Value::Bool(true)) }
531 }
532 Err(_) => {
533 let stack = unsafe { push(stack, Value::Int(0)) };
534 unsafe { push(stack, Value::Bool(false)) }
535 }
536 },
537 _ => panic!("string->int: expected String on stack"),
538 }
539}
540
541#[unsafe(no_mangle)]
551pub unsafe extern "C" fn patch_seq_string_chomp(stack: Stack) -> Stack {
552 assert!(!stack.is_null(), "string_chomp: stack is empty");
553
554 let (stack, str_val) = unsafe { pop(stack) };
555
556 match str_val {
557 Value::String(s) => {
558 let mut result = s.as_str().to_owned();
559 if result.ends_with('\n') {
560 result.pop();
561 if result.ends_with('\r') {
562 result.pop();
563 }
564 }
565 unsafe { push(stack, Value::String(global_string(result))) }
566 }
567 _ => panic!("string_chomp: expected String on stack"),
568 }
569}
570
571pub use patch_seq_char_to_string as char_to_string;
573pub use patch_seq_json_escape as json_escape;
574pub use patch_seq_string_byte_length as string_byte_length;
575pub use patch_seq_string_char_at as string_char_at;
576pub use patch_seq_string_chomp as string_chomp;
577pub use patch_seq_string_concat as string_concat;
578pub use patch_seq_string_contains as string_contains;
579pub use patch_seq_string_empty as string_empty;
580pub use patch_seq_string_equal as string_equal;
581pub use patch_seq_string_find as string_find;
582pub use patch_seq_string_length as string_length;
583pub use patch_seq_string_split as string_split;
584pub use patch_seq_string_starts_with as string_starts_with;
585pub use patch_seq_string_substring as string_substring;
586pub use patch_seq_string_to_int as string_to_int;
587pub use patch_seq_string_to_lower as string_to_lower;
588pub use patch_seq_string_to_upper as string_to_upper;
589pub use patch_seq_string_trim as string_trim;
590
591#[unsafe(no_mangle)]
619pub unsafe extern "C" fn patch_seq_string_to_cstring(stack: Stack, _out: *mut u8) -> *mut u8 {
620 assert!(!stack.is_null(), "string_to_cstring: stack is empty");
621
622 use crate::stack::{DISC_STRING, peek_sv};
623
624 let sv = unsafe { peek_sv(stack) };
626 if sv.slot0 != DISC_STRING {
627 panic!(
628 "string_to_cstring: expected String on stack, got discriminant {}",
629 sv.slot0
630 );
631 }
632
633 let str_ptr = sv.slot1 as *const u8;
635 let len = sv.slot2 as usize;
636
637 let alloc_size = len.checked_add(1).unwrap_or_else(|| {
639 panic!(
640 "string_to_cstring: string too large for C conversion (len={})",
641 len
642 )
643 });
644
645 let ptr = unsafe { libc::malloc(alloc_size) as *mut u8 };
647 if ptr.is_null() {
648 panic!("string_to_cstring: malloc failed");
649 }
650
651 unsafe {
653 std::ptr::copy_nonoverlapping(str_ptr, ptr, len);
654 *ptr.add(len) = 0;
656 }
657
658 ptr
659}
660
661#[unsafe(no_mangle)]
670pub unsafe extern "C" fn patch_seq_cstring_to_string(stack: Stack, cstr: *const u8) -> Stack {
671 if cstr.is_null() {
672 return unsafe { push(stack, Value::String(global_string(String::new()))) };
674 }
675
676 let len = unsafe { libc::strlen(cstr as *const libc::c_char) };
678
679 let slice = unsafe { std::slice::from_raw_parts(cstr, len) };
681 let s = String::from_utf8_lossy(slice).into_owned();
682
683 unsafe { push(stack, Value::String(global_string(s))) }
684}
685
686#[cfg(test)]
687mod tests {
688 use super::*;
689
690 #[test]
691 fn test_string_split_simple() {
692 unsafe {
693 let stack = crate::stack::alloc_test_stack();
694 let stack = push(stack, Value::String(global_string("a b c".to_owned())));
695 let stack = push(stack, Value::String(global_string(" ".to_owned())));
696
697 let stack = string_split(stack);
698
699 let (_stack, result) = pop(stack);
701 match result {
702 Value::Variant(v) => {
703 assert_eq!(v.tag.as_str(), "List");
704 assert_eq!(v.fields.len(), 3);
705 assert_eq!(v.fields[0], Value::String(global_string("a".to_owned())));
706 assert_eq!(v.fields[1], Value::String(global_string("b".to_owned())));
707 assert_eq!(v.fields[2], Value::String(global_string("c".to_owned())));
708 }
709 _ => panic!("Expected Variant, got {:?}", result),
710 }
711 }
712 }
713
714 #[test]
715 fn test_string_split_empty() {
716 unsafe {
717 let stack = crate::stack::alloc_test_stack();
718 let stack = push(stack, Value::String(global_string("".to_owned())));
719 let stack = push(stack, Value::String(global_string(" ".to_owned())));
720
721 let stack = string_split(stack);
722
723 let (_stack, result) = pop(stack);
725 match result {
726 Value::Variant(v) => {
727 assert_eq!(v.tag.as_str(), "List");
728 assert_eq!(v.fields.len(), 1);
729 assert_eq!(v.fields[0], Value::String(global_string("".to_owned())));
730 }
731 _ => panic!("Expected Variant, got {:?}", result),
732 }
733 }
734 }
735
736 #[test]
737 fn test_string_empty_true() {
738 unsafe {
739 let stack = crate::stack::alloc_test_stack();
740 let stack = push(stack, Value::String(global_string("".to_owned())));
741
742 let stack = string_empty(stack);
743
744 let (_stack, result) = pop(stack);
745 assert_eq!(result, Value::Bool(true));
746 }
747 }
748
749 #[test]
750 fn test_string_empty_false() {
751 unsafe {
752 let stack = crate::stack::alloc_test_stack();
753 let stack = push(stack, Value::String(global_string("hello".to_owned())));
754
755 let stack = string_empty(stack);
756
757 let (_stack, result) = pop(stack);
758 assert_eq!(result, Value::Bool(false));
759 }
760 }
761
762 #[test]
763 fn test_string_contains_true() {
764 unsafe {
765 let stack = crate::stack::alloc_test_stack();
766 let stack = push(
767 stack,
768 Value::String(global_string("hello world".to_owned())),
769 );
770 let stack = push(stack, Value::String(global_string("world".to_owned())));
771
772 let stack = string_contains(stack);
773
774 let (_stack, result) = pop(stack);
775 assert_eq!(result, Value::Bool(true));
776 }
777 }
778
779 #[test]
780 fn test_string_contains_false() {
781 unsafe {
782 let stack = crate::stack::alloc_test_stack();
783 let stack = push(
784 stack,
785 Value::String(global_string("hello world".to_owned())),
786 );
787 let stack = push(stack, Value::String(global_string("foo".to_owned())));
788
789 let stack = string_contains(stack);
790
791 let (_stack, result) = pop(stack);
792 assert_eq!(result, Value::Bool(false));
793 }
794 }
795
796 #[test]
797 fn test_string_starts_with_true() {
798 unsafe {
799 let stack = crate::stack::alloc_test_stack();
800 let stack = push(
801 stack,
802 Value::String(global_string("hello world".to_owned())),
803 );
804 let stack = push(stack, Value::String(global_string("hello".to_owned())));
805
806 let stack = string_starts_with(stack);
807
808 let (_stack, result) = pop(stack);
809 assert_eq!(result, Value::Bool(true));
810 }
811 }
812
813 #[test]
814 fn test_string_starts_with_false() {
815 unsafe {
816 let stack = crate::stack::alloc_test_stack();
817 let stack = push(
818 stack,
819 Value::String(global_string("hello world".to_owned())),
820 );
821 let stack = push(stack, Value::String(global_string("world".to_owned())));
822
823 let stack = string_starts_with(stack);
824
825 let (_stack, result) = pop(stack);
826 assert_eq!(result, Value::Bool(false));
827 }
828 }
829
830 #[test]
831 fn test_http_request_line_parsing() {
832 unsafe {
834 let stack = crate::stack::alloc_test_stack();
835 let stack = push(
836 stack,
837 Value::String(global_string("GET /api/users HTTP/1.1".to_owned())),
838 );
839 let stack = push(stack, Value::String(global_string(" ".to_owned())));
840
841 let stack = string_split(stack);
842
843 let (_stack, result) = pop(stack);
845 match result {
846 Value::Variant(v) => {
847 assert_eq!(v.tag.as_str(), "List");
848 assert_eq!(v.fields.len(), 3);
849 assert_eq!(v.fields[0], Value::String(global_string("GET".to_owned())));
850 assert_eq!(
851 v.fields[1],
852 Value::String(global_string("/api/users".to_owned()))
853 );
854 assert_eq!(
855 v.fields[2],
856 Value::String(global_string("HTTP/1.1".to_owned()))
857 );
858 }
859 _ => panic!("Expected Variant, got {:?}", result),
860 }
861 }
862 }
863
864 #[test]
865 fn test_path_routing() {
866 unsafe {
868 let stack = crate::stack::alloc_test_stack();
869 let stack = push(stack, Value::String(global_string("/api/users".to_owned())));
870 let stack = push(stack, Value::String(global_string("/api/".to_owned())));
871
872 let stack = string_starts_with(stack);
873
874 let (_stack, result) = pop(stack);
875 assert_eq!(result, Value::Bool(true));
876 }
877 }
878
879 #[test]
880 fn test_string_concat() {
881 unsafe {
882 let stack = crate::stack::alloc_test_stack();
883 let stack = push(stack, Value::String(global_string("Hello, ".to_owned())));
884 let stack = push(stack, Value::String(global_string("World!".to_owned())));
885
886 let stack = string_concat(stack);
887
888 let (_stack, result) = pop(stack);
889 assert_eq!(
890 result,
891 Value::String(global_string("Hello, World!".to_owned()))
892 );
893 }
894 }
895
896 #[test]
897 fn test_string_length() {
898 unsafe {
899 let stack = crate::stack::alloc_test_stack();
900 let stack = push(stack, Value::String(global_string("Hello".to_owned())));
901
902 let stack = string_length(stack);
903
904 let (_stack, result) = pop(stack);
905 assert_eq!(result, Value::Int(5));
906 }
907 }
908
909 #[test]
910 fn test_string_length_empty() {
911 unsafe {
912 let stack = crate::stack::alloc_test_stack();
913 let stack = push(stack, Value::String(global_string("".to_owned())));
914
915 let stack = string_length(stack);
916
917 let (_stack, result) = pop(stack);
918 assert_eq!(result, Value::Int(0));
919 }
920 }
921
922 #[test]
923 fn test_string_trim() {
924 unsafe {
925 let stack = crate::stack::alloc_test_stack();
926 let stack = push(
927 stack,
928 Value::String(global_string(" Hello, World! ".to_owned())),
929 );
930
931 let stack = string_trim(stack);
932
933 let (_stack, result) = pop(stack);
934 assert_eq!(
935 result,
936 Value::String(global_string("Hello, World!".to_owned()))
937 );
938 }
939 }
940
941 #[test]
942 fn test_string_to_upper() {
943 unsafe {
944 let stack = crate::stack::alloc_test_stack();
945 let stack = push(
946 stack,
947 Value::String(global_string("Hello, World!".to_owned())),
948 );
949
950 let stack = string_to_upper(stack);
951
952 let (_stack, result) = pop(stack);
953 assert_eq!(
954 result,
955 Value::String(global_string("HELLO, WORLD!".to_owned()))
956 );
957 }
958 }
959
960 #[test]
961 fn test_string_to_lower() {
962 unsafe {
963 let stack = crate::stack::alloc_test_stack();
964 let stack = push(
965 stack,
966 Value::String(global_string("Hello, World!".to_owned())),
967 );
968
969 let stack = string_to_lower(stack);
970
971 let (_stack, result) = pop(stack);
972 assert_eq!(
973 result,
974 Value::String(global_string("hello, world!".to_owned()))
975 );
976 }
977 }
978
979 #[test]
980 fn test_http_header_content_length() {
981 unsafe {
983 let stack = crate::stack::alloc_test_stack();
984 let stack = push(
985 stack,
986 Value::String(global_string("Content-Length: ".to_owned())),
987 );
988 let stack = push(stack, Value::String(global_string("42".to_owned())));
989
990 let stack = string_concat(stack);
991
992 let (_stack, result) = pop(stack);
993 assert_eq!(
994 result,
995 Value::String(global_string("Content-Length: 42".to_owned()))
996 );
997 }
998 }
999
1000 #[test]
1001 fn test_string_equal_true() {
1002 unsafe {
1003 let stack = crate::stack::alloc_test_stack();
1004 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1005 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1006
1007 let stack = string_equal(stack);
1008
1009 let (_stack, result) = pop(stack);
1010 assert_eq!(result, Value::Bool(true));
1011 }
1012 }
1013
1014 #[test]
1015 fn test_string_equal_false() {
1016 unsafe {
1017 let stack = crate::stack::alloc_test_stack();
1018 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1019 let stack = push(stack, Value::String(global_string("world".to_owned())));
1020
1021 let stack = string_equal(stack);
1022
1023 let (_stack, result) = pop(stack);
1024 assert_eq!(result, Value::Bool(false));
1025 }
1026 }
1027
1028 #[test]
1029 fn test_string_equal_empty_strings() {
1030 unsafe {
1031 let stack = crate::stack::alloc_test_stack();
1032 let stack = push(stack, Value::String(global_string("".to_owned())));
1033 let stack = push(stack, Value::String(global_string("".to_owned())));
1034
1035 let stack = string_equal(stack);
1036
1037 let (_stack, result) = pop(stack);
1038 assert_eq!(result, Value::Bool(true));
1039 }
1040 }
1041
1042 #[test]
1045 fn test_string_length_utf8() {
1046 unsafe {
1048 let stack = crate::stack::alloc_test_stack();
1049 let stack = push(stack, Value::String(global_string("héllo".to_owned())));
1050
1051 let stack = string_length(stack);
1052
1053 let (_stack, result) = pop(stack);
1054 assert_eq!(result, Value::Int(5)); }
1056 }
1057
1058 #[test]
1059 fn test_string_length_emoji() {
1060 unsafe {
1062 let stack = crate::stack::alloc_test_stack();
1063 let stack = push(stack, Value::String(global_string("hi🎉".to_owned())));
1064
1065 let stack = string_length(stack);
1066
1067 let (_stack, result) = pop(stack);
1068 assert_eq!(result, Value::Int(3)); }
1070 }
1071
1072 #[test]
1073 fn test_string_byte_length_ascii() {
1074 unsafe {
1075 let stack = crate::stack::alloc_test_stack();
1076 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1077
1078 let stack = string_byte_length(stack);
1079
1080 let (_stack, result) = pop(stack);
1081 assert_eq!(result, Value::Int(5)); }
1083 }
1084
1085 #[test]
1086 fn test_string_byte_length_utf8() {
1087 unsafe {
1089 let stack = crate::stack::alloc_test_stack();
1090 let stack = push(stack, Value::String(global_string("héllo".to_owned())));
1091
1092 let stack = string_byte_length(stack);
1093
1094 let (_stack, result) = pop(stack);
1095 assert_eq!(result, Value::Int(6)); }
1097 }
1098
1099 #[test]
1100 fn test_string_char_at_ascii() {
1101 unsafe {
1102 let stack = crate::stack::alloc_test_stack();
1103 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1104 let stack = push(stack, Value::Int(0));
1105
1106 let stack = string_char_at(stack);
1107
1108 let (_stack, result) = pop(stack);
1109 assert_eq!(result, Value::Int(104)); }
1111 }
1112
1113 #[test]
1114 fn test_string_char_at_utf8() {
1115 unsafe {
1117 let stack = crate::stack::alloc_test_stack();
1118 let stack = push(stack, Value::String(global_string("héllo".to_owned())));
1119 let stack = push(stack, Value::Int(1));
1120
1121 let stack = string_char_at(stack);
1122
1123 let (_stack, result) = pop(stack);
1124 assert_eq!(result, Value::Int(233)); }
1126 }
1127
1128 #[test]
1129 fn test_string_char_at_out_of_bounds() {
1130 unsafe {
1131 let stack = crate::stack::alloc_test_stack();
1132 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1133 let stack = push(stack, Value::Int(10)); let stack = string_char_at(stack);
1136
1137 let (_stack, result) = pop(stack);
1138 assert_eq!(result, Value::Int(-1));
1139 }
1140 }
1141
1142 #[test]
1143 fn test_string_char_at_negative() {
1144 unsafe {
1145 let stack = crate::stack::alloc_test_stack();
1146 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1147 let stack = push(stack, Value::Int(-1));
1148
1149 let stack = string_char_at(stack);
1150
1151 let (_stack, result) = pop(stack);
1152 assert_eq!(result, Value::Int(-1));
1153 }
1154 }
1155
1156 #[test]
1157 fn test_string_substring_simple() {
1158 unsafe {
1159 let stack = crate::stack::alloc_test_stack();
1160 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1161 let stack = push(stack, Value::Int(1)); let stack = push(stack, Value::Int(3)); let stack = string_substring(stack);
1165
1166 let (_stack, result) = pop(stack);
1167 assert_eq!(result, Value::String(global_string("ell".to_owned())));
1168 }
1169 }
1170
1171 #[test]
1172 fn test_string_substring_utf8() {
1173 unsafe {
1175 let stack = crate::stack::alloc_test_stack();
1176 let stack = push(stack, Value::String(global_string("héllo".to_owned())));
1177 let stack = push(stack, Value::Int(1)); let stack = push(stack, Value::Int(3)); let stack = string_substring(stack);
1181
1182 let (_stack, result) = pop(stack);
1183 assert_eq!(result, Value::String(global_string("éll".to_owned())));
1184 }
1185 }
1186
1187 #[test]
1188 fn test_string_substring_clamp() {
1189 unsafe {
1191 let stack = crate::stack::alloc_test_stack();
1192 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1193 let stack = push(stack, Value::Int(2)); let stack = push(stack, Value::Int(100)); let stack = string_substring(stack);
1197
1198 let (_stack, result) = pop(stack);
1199 assert_eq!(result, Value::String(global_string("llo".to_owned())));
1200 }
1201 }
1202
1203 #[test]
1204 fn test_string_substring_beyond_end() {
1205 unsafe {
1207 let stack = crate::stack::alloc_test_stack();
1208 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1209 let stack = push(stack, Value::Int(10)); let stack = push(stack, Value::Int(3)); let stack = string_substring(stack);
1213
1214 let (_stack, result) = pop(stack);
1215 assert_eq!(result, Value::String(global_string("".to_owned())));
1216 }
1217 }
1218
1219 #[test]
1220 fn test_char_to_string_ascii() {
1221 unsafe {
1222 let stack = crate::stack::alloc_test_stack();
1223 let stack = push(stack, Value::Int(65)); let stack = char_to_string(stack);
1226
1227 let (_stack, result) = pop(stack);
1228 assert_eq!(result, Value::String(global_string("A".to_owned())));
1229 }
1230 }
1231
1232 #[test]
1233 fn test_char_to_string_utf8() {
1234 unsafe {
1235 let stack = crate::stack::alloc_test_stack();
1236 let stack = push(stack, Value::Int(233)); let stack = char_to_string(stack);
1239
1240 let (_stack, result) = pop(stack);
1241 assert_eq!(result, Value::String(global_string("é".to_owned())));
1242 }
1243 }
1244
1245 #[test]
1246 fn test_char_to_string_newline() {
1247 unsafe {
1248 let stack = crate::stack::alloc_test_stack();
1249 let stack = push(stack, Value::Int(10)); let stack = char_to_string(stack);
1252
1253 let (_stack, result) = pop(stack);
1254 assert_eq!(result, Value::String(global_string("\n".to_owned())));
1255 }
1256 }
1257
1258 #[test]
1259 fn test_char_to_string_invalid() {
1260 unsafe {
1261 let stack = crate::stack::alloc_test_stack();
1262 let stack = push(stack, Value::Int(-1)); let stack = char_to_string(stack);
1265
1266 let (_stack, result) = pop(stack);
1267 assert_eq!(result, Value::String(global_string("".to_owned())));
1268 }
1269 }
1270
1271 #[test]
1272 fn test_string_find_found() {
1273 unsafe {
1274 let stack = crate::stack::alloc_test_stack();
1275 let stack = push(
1276 stack,
1277 Value::String(global_string("hello world".to_owned())),
1278 );
1279 let stack = push(stack, Value::String(global_string("world".to_owned())));
1280
1281 let stack = string_find(stack);
1282
1283 let (_stack, result) = pop(stack);
1284 assert_eq!(result, Value::Int(6)); }
1286 }
1287
1288 #[test]
1289 fn test_string_find_not_found() {
1290 unsafe {
1291 let stack = crate::stack::alloc_test_stack();
1292 let stack = push(
1293 stack,
1294 Value::String(global_string("hello world".to_owned())),
1295 );
1296 let stack = push(stack, Value::String(global_string("xyz".to_owned())));
1297
1298 let stack = string_find(stack);
1299
1300 let (_stack, result) = pop(stack);
1301 assert_eq!(result, Value::Int(-1));
1302 }
1303 }
1304
1305 #[test]
1306 fn test_string_find_first_match() {
1307 unsafe {
1309 let stack = crate::stack::alloc_test_stack();
1310 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1311 let stack = push(stack, Value::String(global_string("l".to_owned())));
1312
1313 let stack = string_find(stack);
1314
1315 let (_stack, result) = pop(stack);
1316 assert_eq!(result, Value::Int(2)); }
1318 }
1319
1320 #[test]
1321 fn test_string_find_utf8() {
1322 unsafe {
1324 let stack = crate::stack::alloc_test_stack();
1325 let stack = push(
1326 stack,
1327 Value::String(global_string("héllo wörld".to_owned())),
1328 );
1329 let stack = push(stack, Value::String(global_string("wörld".to_owned())));
1330
1331 let stack = string_find(stack);
1332
1333 let (_stack, result) = pop(stack);
1334 assert_eq!(result, Value::Int(6)); }
1336 }
1337
1338 #[test]
1341 fn test_json_escape_quotes() {
1342 unsafe {
1343 let stack = crate::stack::alloc_test_stack();
1344 let stack = push(
1345 stack,
1346 Value::String(global_string("hello \"world\"".to_owned())),
1347 );
1348
1349 let stack = json_escape(stack);
1350
1351 let (_stack, result) = pop(stack);
1352 assert_eq!(
1353 result,
1354 Value::String(global_string("hello \\\"world\\\"".to_owned()))
1355 );
1356 }
1357 }
1358
1359 #[test]
1360 fn test_json_escape_backslash() {
1361 unsafe {
1362 let stack = crate::stack::alloc_test_stack();
1363 let stack = push(
1364 stack,
1365 Value::String(global_string("path\\to\\file".to_owned())),
1366 );
1367
1368 let stack = json_escape(stack);
1369
1370 let (_stack, result) = pop(stack);
1371 assert_eq!(
1372 result,
1373 Value::String(global_string("path\\\\to\\\\file".to_owned()))
1374 );
1375 }
1376 }
1377
1378 #[test]
1379 fn test_json_escape_newline_tab() {
1380 unsafe {
1381 let stack = crate::stack::alloc_test_stack();
1382 let stack = push(
1383 stack,
1384 Value::String(global_string("line1\nline2\ttabbed".to_owned())),
1385 );
1386
1387 let stack = json_escape(stack);
1388
1389 let (_stack, result) = pop(stack);
1390 assert_eq!(
1391 result,
1392 Value::String(global_string("line1\\nline2\\ttabbed".to_owned()))
1393 );
1394 }
1395 }
1396
1397 #[test]
1398 fn test_json_escape_carriage_return() {
1399 unsafe {
1400 let stack = crate::stack::alloc_test_stack();
1401 let stack = push(
1402 stack,
1403 Value::String(global_string("line1\r\nline2".to_owned())),
1404 );
1405
1406 let stack = json_escape(stack);
1407
1408 let (_stack, result) = pop(stack);
1409 assert_eq!(
1410 result,
1411 Value::String(global_string("line1\\r\\nline2".to_owned()))
1412 );
1413 }
1414 }
1415
1416 #[test]
1417 fn test_json_escape_control_chars() {
1418 unsafe {
1419 let stack = crate::stack::alloc_test_stack();
1420 let stack = push(
1422 stack,
1423 Value::String(global_string("a\x08b\x0Cc".to_owned())),
1424 );
1425
1426 let stack = json_escape(stack);
1427
1428 let (_stack, result) = pop(stack);
1429 assert_eq!(result, Value::String(global_string("a\\bb\\fc".to_owned())));
1430 }
1431 }
1432
1433 #[test]
1434 fn test_json_escape_unicode_control() {
1435 unsafe {
1436 let stack = crate::stack::alloc_test_stack();
1437 let stack = push(stack, Value::String(global_string("a\x00b".to_owned())));
1439
1440 let stack = json_escape(stack);
1441
1442 let (_stack, result) = pop(stack);
1443 assert_eq!(result, Value::String(global_string("a\\u0000b".to_owned())));
1444 }
1445 }
1446
1447 #[test]
1448 fn test_json_escape_mixed_special_chars() {
1449 unsafe {
1451 let stack = crate::stack::alloc_test_stack();
1452 let stack = push(
1453 stack,
1454 Value::String(global_string("Line 1\nLine \"2\"\ttab\r\n".to_owned())),
1455 );
1456
1457 let stack = json_escape(stack);
1458
1459 let (_stack, result) = pop(stack);
1460 assert_eq!(
1461 result,
1462 Value::String(global_string(
1463 "Line 1\\nLine \\\"2\\\"\\ttab\\r\\n".to_owned()
1464 ))
1465 );
1466 }
1467 }
1468
1469 #[test]
1470 fn test_json_escape_no_change() {
1471 unsafe {
1473 let stack = crate::stack::alloc_test_stack();
1474 let stack = push(
1475 stack,
1476 Value::String(global_string("Hello, World!".to_owned())),
1477 );
1478
1479 let stack = json_escape(stack);
1480
1481 let (_stack, result) = pop(stack);
1482 assert_eq!(
1483 result,
1484 Value::String(global_string("Hello, World!".to_owned()))
1485 );
1486 }
1487 }
1488
1489 #[test]
1490 fn test_json_escape_empty_string() {
1491 unsafe {
1492 let stack = crate::stack::alloc_test_stack();
1493 let stack = push(stack, Value::String(global_string("".to_owned())));
1494
1495 let stack = json_escape(stack);
1496
1497 let (_stack, result) = pop(stack);
1498 assert_eq!(result, Value::String(global_string("".to_owned())));
1499 }
1500 }
1501
1502 #[test]
1505 fn test_string_to_int_success() {
1506 unsafe {
1507 let stack = crate::stack::alloc_test_stack();
1508 let stack = push(stack, Value::String(global_string("42".to_owned())));
1509
1510 let stack = string_to_int(stack);
1511
1512 let (stack, success) = pop(stack);
1513 let (_stack, value) = pop(stack);
1514 assert_eq!(success, Value::Bool(true));
1515 assert_eq!(value, Value::Int(42));
1516 }
1517 }
1518
1519 #[test]
1520 fn test_string_to_int_negative() {
1521 unsafe {
1522 let stack = crate::stack::alloc_test_stack();
1523 let stack = push(stack, Value::String(global_string("-99".to_owned())));
1524
1525 let stack = string_to_int(stack);
1526
1527 let (stack, success) = pop(stack);
1528 let (_stack, value) = pop(stack);
1529 assert_eq!(success, Value::Bool(true));
1530 assert_eq!(value, Value::Int(-99));
1531 }
1532 }
1533
1534 #[test]
1535 fn test_string_to_int_with_whitespace() {
1536 unsafe {
1537 let stack = crate::stack::alloc_test_stack();
1538 let stack = push(stack, Value::String(global_string(" 123 ".to_owned())));
1539
1540 let stack = string_to_int(stack);
1541
1542 let (stack, success) = pop(stack);
1543 let (_stack, value) = pop(stack);
1544 assert_eq!(success, Value::Bool(true));
1545 assert_eq!(value, Value::Int(123));
1546 }
1547 }
1548
1549 #[test]
1550 fn test_string_to_int_failure() {
1551 unsafe {
1552 let stack = crate::stack::alloc_test_stack();
1553 let stack = push(
1554 stack,
1555 Value::String(global_string("not a number".to_owned())),
1556 );
1557
1558 let stack = string_to_int(stack);
1559
1560 let (stack, success) = pop(stack);
1561 let (_stack, value) = pop(stack);
1562 assert_eq!(success, Value::Bool(false));
1563 assert_eq!(value, Value::Int(0));
1564 }
1565 }
1566
1567 #[test]
1568 fn test_string_to_int_empty() {
1569 unsafe {
1570 let stack = crate::stack::alloc_test_stack();
1571 let stack = push(stack, Value::String(global_string("".to_owned())));
1572
1573 let stack = string_to_int(stack);
1574
1575 let (stack, success) = pop(stack);
1576 let (_stack, value) = pop(stack);
1577 assert_eq!(success, Value::Bool(false));
1578 assert_eq!(value, Value::Int(0));
1579 }
1580 }
1581
1582 #[test]
1583 fn test_string_to_int_leading_zeros() {
1584 unsafe {
1585 let stack = crate::stack::alloc_test_stack();
1586 let stack = push(stack, Value::String(global_string("007".to_owned())));
1587
1588 let stack = string_to_int(stack);
1589
1590 let (stack, success) = pop(stack);
1591 let (_stack, value) = pop(stack);
1592 assert_eq!(success, Value::Bool(true));
1593 assert_eq!(value, Value::Int(7));
1594 }
1595 }
1596}