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(0, fields)));
47
48 unsafe { push(stack, variant) }
49 }
50 _ => panic!("string_split: expected two strings on stack"),
51 }
52}
53
54#[unsafe(no_mangle)]
61pub unsafe extern "C" fn patch_seq_string_empty(stack: Stack) -> Stack {
62 assert!(!stack.is_null(), "string_empty: stack is empty");
63
64 let (stack, value) = unsafe { pop(stack) };
65
66 match value {
67 Value::String(s) => {
68 let is_empty = s.as_str().is_empty();
69 let result = if is_empty { 1 } else { 0 };
70 unsafe { push(stack, Value::Int(result)) }
71 }
72 _ => panic!("string_empty: expected String on stack"),
73 }
74}
75
76#[unsafe(no_mangle)]
83pub unsafe extern "C" fn patch_seq_string_contains(stack: Stack) -> Stack {
84 assert!(!stack.is_null(), "string_contains: stack is empty");
85
86 let (stack, substring_val) = unsafe { pop(stack) };
87 assert!(!stack.is_null(), "string_contains: need two strings");
88 let (stack, str_val) = unsafe { pop(stack) };
89
90 match (str_val, substring_val) {
91 (Value::String(s), Value::String(sub)) => {
92 let contains = s.as_str().contains(sub.as_str());
93 let result = if contains { 1 } else { 0 };
94 unsafe { push(stack, Value::Int(result)) }
95 }
96 _ => panic!("string_contains: expected two strings on stack"),
97 }
98}
99
100#[unsafe(no_mangle)]
107pub unsafe extern "C" fn patch_seq_string_starts_with(stack: Stack) -> Stack {
108 assert!(!stack.is_null(), "string_starts_with: stack is empty");
109
110 let (stack, prefix_val) = unsafe { pop(stack) };
111 assert!(!stack.is_null(), "string_starts_with: need two strings");
112 let (stack, str_val) = unsafe { pop(stack) };
113
114 match (str_val, prefix_val) {
115 (Value::String(s), Value::String(prefix)) => {
116 let starts = s.as_str().starts_with(prefix.as_str());
117 let result = if starts { 1 } else { 0 };
118 unsafe { push(stack, Value::Int(result)) }
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 let result = if equal { 1 } else { 0 };
425 unsafe { push(stack, Value::Int(result)) }
426 }
427 _ => panic!("string_equal: expected two strings on stack"),
428 }
429}
430
431#[unsafe(no_mangle)]
448pub unsafe extern "C" fn patch_seq_json_escape(stack: Stack) -> Stack {
449 assert!(!stack.is_null(), "json_escape: stack is empty");
450
451 let (stack, value) = unsafe { pop(stack) };
452
453 match value {
454 Value::String(s) => {
455 let input = s.as_str();
456 let mut result = String::with_capacity(input.len() + 16);
457
458 for ch in input.chars() {
459 match ch {
460 '"' => result.push_str("\\\""),
461 '\\' => result.push_str("\\\\"),
462 '\n' => result.push_str("\\n"),
463 '\r' => result.push_str("\\r"),
464 '\t' => result.push_str("\\t"),
465 '\x08' => result.push_str("\\b"), '\x0C' => result.push_str("\\f"), c if c.is_control() => {
470 result.push_str(&format!("\\u{:04X}", c as u32));
471 }
472 c => result.push(c),
473 }
474 }
475
476 unsafe { push(stack, Value::String(global_string(result))) }
477 }
478 _ => panic!("json_escape: expected String on stack"),
479 }
480}
481
482#[unsafe(no_mangle)]
491pub unsafe extern "C" fn patch_seq_string_to_int(stack: Stack) -> Stack {
492 assert!(!stack.is_null(), "string->int: stack is empty");
493 let (stack, val) = unsafe { pop(stack) };
494
495 match val {
496 Value::String(s) => match s.as_str().trim().parse::<i64>() {
497 Ok(i) => {
498 let stack = unsafe { push(stack, Value::Int(i)) };
499 unsafe { push(stack, Value::Int(1)) }
500 }
501 Err(_) => {
502 let stack = unsafe { push(stack, Value::Int(0)) };
503 unsafe { push(stack, Value::Int(0)) }
504 }
505 },
506 _ => panic!("string->int: expected String on stack"),
507 }
508}
509
510#[unsafe(no_mangle)]
520pub unsafe extern "C" fn patch_seq_string_chomp(stack: Stack) -> Stack {
521 assert!(!stack.is_null(), "string_chomp: stack is empty");
522
523 let (stack, str_val) = unsafe { pop(stack) };
524
525 match str_val {
526 Value::String(s) => {
527 let mut result = s.as_str().to_owned();
528 if result.ends_with('\n') {
529 result.pop();
530 if result.ends_with('\r') {
531 result.pop();
532 }
533 }
534 unsafe { push(stack, Value::String(global_string(result))) }
535 }
536 _ => panic!("string_chomp: expected String on stack"),
537 }
538}
539
540pub use patch_seq_char_to_string as char_to_string;
542pub use patch_seq_json_escape as json_escape;
543pub use patch_seq_string_byte_length as string_byte_length;
544pub use patch_seq_string_char_at as string_char_at;
545pub use patch_seq_string_chomp as string_chomp;
546pub use patch_seq_string_concat as string_concat;
547pub use patch_seq_string_contains as string_contains;
548pub use patch_seq_string_empty as string_empty;
549pub use patch_seq_string_equal as string_equal;
550pub use patch_seq_string_find as string_find;
551pub use patch_seq_string_length as string_length;
552pub use patch_seq_string_split as string_split;
553pub use patch_seq_string_starts_with as string_starts_with;
554pub use patch_seq_string_substring as string_substring;
555pub use patch_seq_string_to_int as string_to_int;
556pub use patch_seq_string_to_lower as string_to_lower;
557pub use patch_seq_string_to_upper as string_to_upper;
558pub use patch_seq_string_trim as string_trim;
559
560#[unsafe(no_mangle)]
588pub unsafe extern "C" fn patch_seq_string_to_cstring(stack: Stack, _out: *mut u8) -> *mut u8 {
589 assert!(!stack.is_null(), "string_to_cstring: stack is empty");
590
591 let node = unsafe { &*stack };
593 match &node.value {
594 Value::String(s) => {
595 let bytes = s.as_str().as_bytes();
596 let len = bytes.len();
597
598 let alloc_size = len.checked_add(1).unwrap_or_else(|| {
600 panic!(
601 "string_to_cstring: string too large for C conversion (len={})",
602 len
603 )
604 });
605
606 let ptr = unsafe { libc::malloc(alloc_size) as *mut u8 };
608 if ptr.is_null() {
609 panic!("string_to_cstring: malloc failed");
610 }
611
612 unsafe {
614 std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, len);
615 *ptr.add(len) = 0;
617 }
618
619 ptr
620 }
621 _ => panic!("string_to_cstring: expected String on stack"),
622 }
623}
624
625#[unsafe(no_mangle)]
634pub unsafe extern "C" fn patch_seq_cstring_to_string(stack: Stack, cstr: *const u8) -> Stack {
635 if cstr.is_null() {
636 return unsafe { push(stack, Value::String(global_string(String::new()))) };
638 }
639
640 let len = unsafe { libc::strlen(cstr as *const libc::c_char) };
642
643 let slice = unsafe { std::slice::from_raw_parts(cstr, len) };
645 let s = String::from_utf8_lossy(slice).into_owned();
646
647 unsafe { push(stack, Value::String(global_string(s))) }
648}
649
650#[cfg(test)]
651mod tests {
652 use super::*;
653
654 #[test]
655 fn test_string_split_simple() {
656 unsafe {
657 let stack = std::ptr::null_mut();
658 let stack = push(stack, Value::String(global_string("a b c".to_owned())));
659 let stack = push(stack, Value::String(global_string(" ".to_owned())));
660
661 let stack = string_split(stack);
662
663 let (stack, result) = pop(stack);
665 match result {
666 Value::Variant(v) => {
667 assert_eq!(v.tag, 0);
668 assert_eq!(v.fields.len(), 3);
669 assert_eq!(v.fields[0], Value::String(global_string("a".to_owned())));
670 assert_eq!(v.fields[1], Value::String(global_string("b".to_owned())));
671 assert_eq!(v.fields[2], Value::String(global_string("c".to_owned())));
672 }
673 _ => panic!("Expected Variant, got {:?}", result),
674 }
675
676 assert!(stack.is_null());
677 }
678 }
679
680 #[test]
681 fn test_string_split_empty() {
682 unsafe {
683 let stack = std::ptr::null_mut();
684 let stack = push(stack, Value::String(global_string("".to_owned())));
685 let stack = push(stack, Value::String(global_string(" ".to_owned())));
686
687 let stack = string_split(stack);
688
689 let (stack, result) = pop(stack);
691 match result {
692 Value::Variant(v) => {
693 assert_eq!(v.tag, 0);
694 assert_eq!(v.fields.len(), 1);
695 assert_eq!(v.fields[0], Value::String(global_string("".to_owned())));
696 }
697 _ => panic!("Expected Variant, got {:?}", result),
698 }
699
700 assert!(stack.is_null());
701 }
702 }
703
704 #[test]
705 fn test_string_empty_true() {
706 unsafe {
707 let stack = std::ptr::null_mut();
708 let stack = push(stack, Value::String(global_string("".to_owned())));
709
710 let stack = string_empty(stack);
711
712 let (stack, result) = pop(stack);
713 assert_eq!(result, Value::Int(1));
714 assert!(stack.is_null());
715 }
716 }
717
718 #[test]
719 fn test_string_empty_false() {
720 unsafe {
721 let stack = std::ptr::null_mut();
722 let stack = push(stack, Value::String(global_string("hello".to_owned())));
723
724 let stack = string_empty(stack);
725
726 let (stack, result) = pop(stack);
727 assert_eq!(result, Value::Int(0));
728 assert!(stack.is_null());
729 }
730 }
731
732 #[test]
733 fn test_string_contains_true() {
734 unsafe {
735 let stack = std::ptr::null_mut();
736 let stack = push(
737 stack,
738 Value::String(global_string("hello world".to_owned())),
739 );
740 let stack = push(stack, Value::String(global_string("world".to_owned())));
741
742 let stack = string_contains(stack);
743
744 let (stack, result) = pop(stack);
745 assert_eq!(result, Value::Int(1));
746 assert!(stack.is_null());
747 }
748 }
749
750 #[test]
751 fn test_string_contains_false() {
752 unsafe {
753 let stack = std::ptr::null_mut();
754 let stack = push(
755 stack,
756 Value::String(global_string("hello world".to_owned())),
757 );
758 let stack = push(stack, Value::String(global_string("foo".to_owned())));
759
760 let stack = string_contains(stack);
761
762 let (stack, result) = pop(stack);
763 assert_eq!(result, Value::Int(0));
764 assert!(stack.is_null());
765 }
766 }
767
768 #[test]
769 fn test_string_starts_with_true() {
770 unsafe {
771 let stack = std::ptr::null_mut();
772 let stack = push(
773 stack,
774 Value::String(global_string("hello world".to_owned())),
775 );
776 let stack = push(stack, Value::String(global_string("hello".to_owned())));
777
778 let stack = string_starts_with(stack);
779
780 let (stack, result) = pop(stack);
781 assert_eq!(result, Value::Int(1));
782 assert!(stack.is_null());
783 }
784 }
785
786 #[test]
787 fn test_string_starts_with_false() {
788 unsafe {
789 let stack = std::ptr::null_mut();
790 let stack = push(
791 stack,
792 Value::String(global_string("hello world".to_owned())),
793 );
794 let stack = push(stack, Value::String(global_string("world".to_owned())));
795
796 let stack = string_starts_with(stack);
797
798 let (stack, result) = pop(stack);
799 assert_eq!(result, Value::Int(0));
800 assert!(stack.is_null());
801 }
802 }
803
804 #[test]
805 fn test_http_request_line_parsing() {
806 unsafe {
808 let stack = std::ptr::null_mut();
809 let stack = push(
810 stack,
811 Value::String(global_string("GET /api/users HTTP/1.1".to_owned())),
812 );
813 let stack = push(stack, Value::String(global_string(" ".to_owned())));
814
815 let stack = string_split(stack);
816
817 let (stack, result) = pop(stack);
819 match result {
820 Value::Variant(v) => {
821 assert_eq!(v.tag, 0);
822 assert_eq!(v.fields.len(), 3);
823 assert_eq!(v.fields[0], Value::String(global_string("GET".to_owned())));
824 assert_eq!(
825 v.fields[1],
826 Value::String(global_string("/api/users".to_owned()))
827 );
828 assert_eq!(
829 v.fields[2],
830 Value::String(global_string("HTTP/1.1".to_owned()))
831 );
832 }
833 _ => panic!("Expected Variant, got {:?}", result),
834 }
835
836 assert!(stack.is_null());
837 }
838 }
839
840 #[test]
841 fn test_path_routing() {
842 unsafe {
844 let stack = std::ptr::null_mut();
845 let stack = push(stack, Value::String(global_string("/api/users".to_owned())));
846 let stack = push(stack, Value::String(global_string("/api/".to_owned())));
847
848 let stack = string_starts_with(stack);
849
850 let (stack, result) = pop(stack);
851 assert_eq!(result, Value::Int(1));
852 assert!(stack.is_null());
853 }
854 }
855
856 #[test]
857 fn test_string_concat() {
858 unsafe {
859 let stack = std::ptr::null_mut();
860 let stack = push(stack, Value::String(global_string("Hello, ".to_owned())));
861 let stack = push(stack, Value::String(global_string("World!".to_owned())));
862
863 let stack = string_concat(stack);
864
865 let (stack, result) = pop(stack);
866 assert_eq!(
867 result,
868 Value::String(global_string("Hello, World!".to_owned()))
869 );
870 assert!(stack.is_null());
871 }
872 }
873
874 #[test]
875 fn test_string_length() {
876 unsafe {
877 let stack = std::ptr::null_mut();
878 let stack = push(stack, Value::String(global_string("Hello".to_owned())));
879
880 let stack = string_length(stack);
881
882 let (stack, result) = pop(stack);
883 assert_eq!(result, Value::Int(5));
884 assert!(stack.is_null());
885 }
886 }
887
888 #[test]
889 fn test_string_length_empty() {
890 unsafe {
891 let stack = std::ptr::null_mut();
892 let stack = push(stack, Value::String(global_string("".to_owned())));
893
894 let stack = string_length(stack);
895
896 let (stack, result) = pop(stack);
897 assert_eq!(result, Value::Int(0));
898 assert!(stack.is_null());
899 }
900 }
901
902 #[test]
903 fn test_string_trim() {
904 unsafe {
905 let stack = std::ptr::null_mut();
906 let stack = push(
907 stack,
908 Value::String(global_string(" Hello, World! ".to_owned())),
909 );
910
911 let stack = string_trim(stack);
912
913 let (stack, result) = pop(stack);
914 assert_eq!(
915 result,
916 Value::String(global_string("Hello, World!".to_owned()))
917 );
918 assert!(stack.is_null());
919 }
920 }
921
922 #[test]
923 fn test_string_to_upper() {
924 unsafe {
925 let stack = std::ptr::null_mut();
926 let stack = push(
927 stack,
928 Value::String(global_string("Hello, World!".to_owned())),
929 );
930
931 let stack = string_to_upper(stack);
932
933 let (stack, result) = pop(stack);
934 assert_eq!(
935 result,
936 Value::String(global_string("HELLO, WORLD!".to_owned()))
937 );
938 assert!(stack.is_null());
939 }
940 }
941
942 #[test]
943 fn test_string_to_lower() {
944 unsafe {
945 let stack = std::ptr::null_mut();
946 let stack = push(
947 stack,
948 Value::String(global_string("Hello, World!".to_owned())),
949 );
950
951 let stack = string_to_lower(stack);
952
953 let (stack, result) = pop(stack);
954 assert_eq!(
955 result,
956 Value::String(global_string("hello, world!".to_owned()))
957 );
958 assert!(stack.is_null());
959 }
960 }
961
962 #[test]
963 fn test_http_header_content_length() {
964 unsafe {
966 let stack = std::ptr::null_mut();
967 let stack = push(
968 stack,
969 Value::String(global_string("Content-Length: ".to_owned())),
970 );
971 let stack = push(stack, Value::String(global_string("42".to_owned())));
972
973 let stack = string_concat(stack);
974
975 let (stack, result) = pop(stack);
976 assert_eq!(
977 result,
978 Value::String(global_string("Content-Length: 42".to_owned()))
979 );
980 assert!(stack.is_null());
981 }
982 }
983
984 #[test]
985 fn test_string_equal_true() {
986 unsafe {
987 let stack = std::ptr::null_mut();
988 let stack = push(stack, Value::String(global_string("hello".to_owned())));
989 let stack = push(stack, Value::String(global_string("hello".to_owned())));
990
991 let stack = string_equal(stack);
992
993 let (stack, result) = pop(stack);
994 assert_eq!(result, Value::Int(1));
995 assert!(stack.is_null());
996 }
997 }
998
999 #[test]
1000 fn test_string_equal_false() {
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::String(global_string("world".to_owned())));
1005
1006 let stack = string_equal(stack);
1007
1008 let (stack, result) = pop(stack);
1009 assert_eq!(result, Value::Int(0));
1010 assert!(stack.is_null());
1011 }
1012 }
1013
1014 #[test]
1015 fn test_string_equal_empty_strings() {
1016 unsafe {
1017 let stack = std::ptr::null_mut();
1018 let stack = push(stack, Value::String(global_string("".to_owned())));
1019 let stack = push(stack, Value::String(global_string("".to_owned())));
1020
1021 let stack = string_equal(stack);
1022
1023 let (stack, result) = pop(stack);
1024 assert_eq!(result, Value::Int(1));
1025 assert!(stack.is_null());
1026 }
1027 }
1028
1029 #[test]
1032 fn test_string_length_utf8() {
1033 unsafe {
1035 let stack = std::ptr::null_mut();
1036 let stack = push(stack, Value::String(global_string("héllo".to_owned())));
1037
1038 let stack = string_length(stack);
1039
1040 let (stack, result) = pop(stack);
1041 assert_eq!(result, Value::Int(5)); assert!(stack.is_null());
1043 }
1044 }
1045
1046 #[test]
1047 fn test_string_length_emoji() {
1048 unsafe {
1050 let stack = std::ptr::null_mut();
1051 let stack = push(stack, Value::String(global_string("hi🎉".to_owned())));
1052
1053 let stack = string_length(stack);
1054
1055 let (stack, result) = pop(stack);
1056 assert_eq!(result, Value::Int(3)); assert!(stack.is_null());
1058 }
1059 }
1060
1061 #[test]
1062 fn test_string_byte_length_ascii() {
1063 unsafe {
1064 let stack = std::ptr::null_mut();
1065 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1066
1067 let stack = string_byte_length(stack);
1068
1069 let (stack, result) = pop(stack);
1070 assert_eq!(result, Value::Int(5)); assert!(stack.is_null());
1072 }
1073 }
1074
1075 #[test]
1076 fn test_string_byte_length_utf8() {
1077 unsafe {
1079 let stack = std::ptr::null_mut();
1080 let stack = push(stack, Value::String(global_string("héllo".to_owned())));
1081
1082 let stack = string_byte_length(stack);
1083
1084 let (stack, result) = pop(stack);
1085 assert_eq!(result, Value::Int(6)); assert!(stack.is_null());
1087 }
1088 }
1089
1090 #[test]
1091 fn test_string_char_at_ascii() {
1092 unsafe {
1093 let stack = std::ptr::null_mut();
1094 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1095 let stack = push(stack, Value::Int(0));
1096
1097 let stack = string_char_at(stack);
1098
1099 let (stack, result) = pop(stack);
1100 assert_eq!(result, Value::Int(104)); assert!(stack.is_null());
1102 }
1103 }
1104
1105 #[test]
1106 fn test_string_char_at_utf8() {
1107 unsafe {
1109 let stack = std::ptr::null_mut();
1110 let stack = push(stack, Value::String(global_string("héllo".to_owned())));
1111 let stack = push(stack, Value::Int(1));
1112
1113 let stack = string_char_at(stack);
1114
1115 let (stack, result) = pop(stack);
1116 assert_eq!(result, Value::Int(233)); assert!(stack.is_null());
1118 }
1119 }
1120
1121 #[test]
1122 fn test_string_char_at_out_of_bounds() {
1123 unsafe {
1124 let stack = std::ptr::null_mut();
1125 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1126 let stack = push(stack, Value::Int(10)); let stack = string_char_at(stack);
1129
1130 let (stack, result) = pop(stack);
1131 assert_eq!(result, Value::Int(-1));
1132 assert!(stack.is_null());
1133 }
1134 }
1135
1136 #[test]
1137 fn test_string_char_at_negative() {
1138 unsafe {
1139 let stack = std::ptr::null_mut();
1140 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1141 let stack = push(stack, Value::Int(-1));
1142
1143 let stack = string_char_at(stack);
1144
1145 let (stack, result) = pop(stack);
1146 assert_eq!(result, Value::Int(-1));
1147 assert!(stack.is_null());
1148 }
1149 }
1150
1151 #[test]
1152 fn test_string_substring_simple() {
1153 unsafe {
1154 let stack = std::ptr::null_mut();
1155 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1156 let stack = push(stack, Value::Int(1)); let stack = push(stack, Value::Int(3)); let stack = string_substring(stack);
1160
1161 let (stack, result) = pop(stack);
1162 assert_eq!(result, Value::String(global_string("ell".to_owned())));
1163 assert!(stack.is_null());
1164 }
1165 }
1166
1167 #[test]
1168 fn test_string_substring_utf8() {
1169 unsafe {
1171 let stack = std::ptr::null_mut();
1172 let stack = push(stack, Value::String(global_string("héllo".to_owned())));
1173 let stack = push(stack, Value::Int(1)); let stack = push(stack, Value::Int(3)); let stack = string_substring(stack);
1177
1178 let (stack, result) = pop(stack);
1179 assert_eq!(result, Value::String(global_string("éll".to_owned())));
1180 assert!(stack.is_null());
1181 }
1182 }
1183
1184 #[test]
1185 fn test_string_substring_clamp() {
1186 unsafe {
1188 let stack = std::ptr::null_mut();
1189 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1190 let stack = push(stack, Value::Int(2)); let stack = push(stack, Value::Int(100)); let stack = string_substring(stack);
1194
1195 let (stack, result) = pop(stack);
1196 assert_eq!(result, Value::String(global_string("llo".to_owned())));
1197 assert!(stack.is_null());
1198 }
1199 }
1200
1201 #[test]
1202 fn test_string_substring_beyond_end() {
1203 unsafe {
1205 let stack = std::ptr::null_mut();
1206 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1207 let stack = push(stack, Value::Int(10)); let stack = push(stack, Value::Int(3)); let stack = string_substring(stack);
1211
1212 let (stack, result) = pop(stack);
1213 assert_eq!(result, Value::String(global_string("".to_owned())));
1214 assert!(stack.is_null());
1215 }
1216 }
1217
1218 #[test]
1219 fn test_char_to_string_ascii() {
1220 unsafe {
1221 let stack = std::ptr::null_mut();
1222 let stack = push(stack, Value::Int(65)); let stack = char_to_string(stack);
1225
1226 let (stack, result) = pop(stack);
1227 assert_eq!(result, Value::String(global_string("A".to_owned())));
1228 assert!(stack.is_null());
1229 }
1230 }
1231
1232 #[test]
1233 fn test_char_to_string_utf8() {
1234 unsafe {
1235 let stack = std::ptr::null_mut();
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 assert!(stack.is_null());
1243 }
1244 }
1245
1246 #[test]
1247 fn test_char_to_string_newline() {
1248 unsafe {
1249 let stack = std::ptr::null_mut();
1250 let stack = push(stack, Value::Int(10)); let stack = char_to_string(stack);
1253
1254 let (stack, result) = pop(stack);
1255 assert_eq!(result, Value::String(global_string("\n".to_owned())));
1256 assert!(stack.is_null());
1257 }
1258 }
1259
1260 #[test]
1261 fn test_char_to_string_invalid() {
1262 unsafe {
1263 let stack = std::ptr::null_mut();
1264 let stack = push(stack, Value::Int(-1)); let stack = char_to_string(stack);
1267
1268 let (stack, result) = pop(stack);
1269 assert_eq!(result, Value::String(global_string("".to_owned())));
1270 assert!(stack.is_null());
1271 }
1272 }
1273
1274 #[test]
1275 fn test_string_find_found() {
1276 unsafe {
1277 let stack = std::ptr::null_mut();
1278 let stack = push(
1279 stack,
1280 Value::String(global_string("hello world".to_owned())),
1281 );
1282 let stack = push(stack, Value::String(global_string("world".to_owned())));
1283
1284 let stack = string_find(stack);
1285
1286 let (stack, result) = pop(stack);
1287 assert_eq!(result, Value::Int(6)); assert!(stack.is_null());
1289 }
1290 }
1291
1292 #[test]
1293 fn test_string_find_not_found() {
1294 unsafe {
1295 let stack = std::ptr::null_mut();
1296 let stack = push(
1297 stack,
1298 Value::String(global_string("hello world".to_owned())),
1299 );
1300 let stack = push(stack, Value::String(global_string("xyz".to_owned())));
1301
1302 let stack = string_find(stack);
1303
1304 let (stack, result) = pop(stack);
1305 assert_eq!(result, Value::Int(-1));
1306 assert!(stack.is_null());
1307 }
1308 }
1309
1310 #[test]
1311 fn test_string_find_first_match() {
1312 unsafe {
1314 let stack = std::ptr::null_mut();
1315 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1316 let stack = push(stack, Value::String(global_string("l".to_owned())));
1317
1318 let stack = string_find(stack);
1319
1320 let (stack, result) = pop(stack);
1321 assert_eq!(result, Value::Int(2)); assert!(stack.is_null());
1323 }
1324 }
1325
1326 #[test]
1327 fn test_string_find_utf8() {
1328 unsafe {
1330 let stack = std::ptr::null_mut();
1331 let stack = push(
1332 stack,
1333 Value::String(global_string("héllo wörld".to_owned())),
1334 );
1335 let stack = push(stack, Value::String(global_string("wörld".to_owned())));
1336
1337 let stack = string_find(stack);
1338
1339 let (stack, result) = pop(stack);
1340 assert_eq!(result, Value::Int(6)); assert!(stack.is_null());
1342 }
1343 }
1344
1345 #[test]
1348 fn test_json_escape_quotes() {
1349 unsafe {
1350 let stack = std::ptr::null_mut();
1351 let stack = push(
1352 stack,
1353 Value::String(global_string("hello \"world\"".to_owned())),
1354 );
1355
1356 let stack = json_escape(stack);
1357
1358 let (stack, result) = pop(stack);
1359 assert_eq!(
1360 result,
1361 Value::String(global_string("hello \\\"world\\\"".to_owned()))
1362 );
1363 assert!(stack.is_null());
1364 }
1365 }
1366
1367 #[test]
1368 fn test_json_escape_backslash() {
1369 unsafe {
1370 let stack = std::ptr::null_mut();
1371 let stack = push(
1372 stack,
1373 Value::String(global_string("path\\to\\file".to_owned())),
1374 );
1375
1376 let stack = json_escape(stack);
1377
1378 let (stack, result) = pop(stack);
1379 assert_eq!(
1380 result,
1381 Value::String(global_string("path\\\\to\\\\file".to_owned()))
1382 );
1383 assert!(stack.is_null());
1384 }
1385 }
1386
1387 #[test]
1388 fn test_json_escape_newline_tab() {
1389 unsafe {
1390 let stack = std::ptr::null_mut();
1391 let stack = push(
1392 stack,
1393 Value::String(global_string("line1\nline2\ttabbed".to_owned())),
1394 );
1395
1396 let stack = json_escape(stack);
1397
1398 let (stack, result) = pop(stack);
1399 assert_eq!(
1400 result,
1401 Value::String(global_string("line1\\nline2\\ttabbed".to_owned()))
1402 );
1403 assert!(stack.is_null());
1404 }
1405 }
1406
1407 #[test]
1408 fn test_json_escape_carriage_return() {
1409 unsafe {
1410 let stack = std::ptr::null_mut();
1411 let stack = push(
1412 stack,
1413 Value::String(global_string("line1\r\nline2".to_owned())),
1414 );
1415
1416 let stack = json_escape(stack);
1417
1418 let (stack, result) = pop(stack);
1419 assert_eq!(
1420 result,
1421 Value::String(global_string("line1\\r\\nline2".to_owned()))
1422 );
1423 assert!(stack.is_null());
1424 }
1425 }
1426
1427 #[test]
1428 fn test_json_escape_control_chars() {
1429 unsafe {
1430 let stack = std::ptr::null_mut();
1431 let stack = push(
1433 stack,
1434 Value::String(global_string("a\x08b\x0Cc".to_owned())),
1435 );
1436
1437 let stack = json_escape(stack);
1438
1439 let (stack, result) = pop(stack);
1440 assert_eq!(result, Value::String(global_string("a\\bb\\fc".to_owned())));
1441 assert!(stack.is_null());
1442 }
1443 }
1444
1445 #[test]
1446 fn test_json_escape_unicode_control() {
1447 unsafe {
1448 let stack = std::ptr::null_mut();
1449 let stack = push(stack, Value::String(global_string("a\x00b".to_owned())));
1451
1452 let stack = json_escape(stack);
1453
1454 let (stack, result) = pop(stack);
1455 assert_eq!(result, Value::String(global_string("a\\u0000b".to_owned())));
1456 assert!(stack.is_null());
1457 }
1458 }
1459
1460 #[test]
1461 fn test_json_escape_mixed_special_chars() {
1462 unsafe {
1464 let stack = std::ptr::null_mut();
1465 let stack = push(
1466 stack,
1467 Value::String(global_string("Line 1\nLine \"2\"\ttab\r\n".to_owned())),
1468 );
1469
1470 let stack = json_escape(stack);
1471
1472 let (stack, result) = pop(stack);
1473 assert_eq!(
1474 result,
1475 Value::String(global_string(
1476 "Line 1\\nLine \\\"2\\\"\\ttab\\r\\n".to_owned()
1477 ))
1478 );
1479 assert!(stack.is_null());
1480 }
1481 }
1482
1483 #[test]
1484 fn test_json_escape_no_change() {
1485 unsafe {
1487 let stack = std::ptr::null_mut();
1488 let stack = push(
1489 stack,
1490 Value::String(global_string("Hello, World!".to_owned())),
1491 );
1492
1493 let stack = json_escape(stack);
1494
1495 let (stack, result) = pop(stack);
1496 assert_eq!(
1497 result,
1498 Value::String(global_string("Hello, World!".to_owned()))
1499 );
1500 assert!(stack.is_null());
1501 }
1502 }
1503
1504 #[test]
1505 fn test_json_escape_empty_string() {
1506 unsafe {
1507 let stack = std::ptr::null_mut();
1508 let stack = push(stack, Value::String(global_string("".to_owned())));
1509
1510 let stack = json_escape(stack);
1511
1512 let (stack, result) = pop(stack);
1513 assert_eq!(result, Value::String(global_string("".to_owned())));
1514 assert!(stack.is_null());
1515 }
1516 }
1517
1518 #[test]
1521 fn test_string_to_int_success() {
1522 unsafe {
1523 let stack = std::ptr::null_mut();
1524 let stack = push(stack, Value::String(global_string("42".to_owned())));
1525
1526 let stack = string_to_int(stack);
1527
1528 let (stack, success) = pop(stack);
1529 let (stack, value) = pop(stack);
1530 assert_eq!(success, Value::Int(1));
1531 assert_eq!(value, Value::Int(42));
1532 assert!(stack.is_null());
1533 }
1534 }
1535
1536 #[test]
1537 fn test_string_to_int_negative() {
1538 unsafe {
1539 let stack = std::ptr::null_mut();
1540 let stack = push(stack, Value::String(global_string("-99".to_owned())));
1541
1542 let stack = string_to_int(stack);
1543
1544 let (stack, success) = pop(stack);
1545 let (stack, value) = pop(stack);
1546 assert_eq!(success, Value::Int(1));
1547 assert_eq!(value, Value::Int(-99));
1548 assert!(stack.is_null());
1549 }
1550 }
1551
1552 #[test]
1553 fn test_string_to_int_with_whitespace() {
1554 unsafe {
1555 let stack = std::ptr::null_mut();
1556 let stack = push(stack, Value::String(global_string(" 123 ".to_owned())));
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::Int(1));
1563 assert_eq!(value, Value::Int(123));
1564 assert!(stack.is_null());
1565 }
1566 }
1567
1568 #[test]
1569 fn test_string_to_int_failure() {
1570 unsafe {
1571 let stack = std::ptr::null_mut();
1572 let stack = push(
1573 stack,
1574 Value::String(global_string("not a number".to_owned())),
1575 );
1576
1577 let stack = string_to_int(stack);
1578
1579 let (stack, success) = pop(stack);
1580 let (stack, value) = pop(stack);
1581 assert_eq!(success, Value::Int(0));
1582 assert_eq!(value, Value::Int(0));
1583 assert!(stack.is_null());
1584 }
1585 }
1586
1587 #[test]
1588 fn test_string_to_int_empty() {
1589 unsafe {
1590 let stack = std::ptr::null_mut();
1591 let stack = push(stack, Value::String(global_string("".to_owned())));
1592
1593 let stack = string_to_int(stack);
1594
1595 let (stack, success) = pop(stack);
1596 let (stack, value) = pop(stack);
1597 assert_eq!(success, Value::Int(0));
1598 assert_eq!(value, Value::Int(0));
1599 assert!(stack.is_null());
1600 }
1601 }
1602
1603 #[test]
1604 fn test_string_to_int_leading_zeros() {
1605 unsafe {
1606 let stack = std::ptr::null_mut();
1607 let stack = push(stack, Value::String(global_string("007".to_owned())));
1608
1609 let stack = string_to_int(stack);
1610
1611 let (stack, success) = pop(stack);
1612 let (stack, value) = pop(stack);
1613 assert_eq!(success, Value::Int(1));
1614 assert_eq!(value, Value::Int(7));
1615 assert!(stack.is_null());
1616 }
1617 }
1618}