use crate::seqstring::{global_bytes, global_string};
use crate::stack::{Stack, pop, push};
use crate::value::Value;
use std::sync::Arc;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_string_split(stack: Stack) -> Stack {
use crate::value::VariantData;
assert!(!stack.is_null(), "string_split: stack is empty");
let (stack, delim_val) = unsafe { pop(stack) };
assert!(!stack.is_null(), "string_split: need two strings");
let (stack, str_val) = unsafe { pop(stack) };
match (str_val, delim_val) {
(Value::String(s), Value::String(d)) => {
let bytes = s.as_bytes();
let needle = d.as_bytes();
let parts: Vec<Vec<u8>> = if needle.is_empty() {
let mut parts = Vec::with_capacity(bytes.len() + 2);
parts.push(Vec::new());
for b in bytes {
parts.push(vec![*b]);
}
parts.push(Vec::new());
parts
} else {
let mut parts: Vec<Vec<u8>> = Vec::new();
let mut last = 0usize;
let mut i = 0usize;
while i + needle.len() <= bytes.len() {
if &bytes[i..i + needle.len()] == needle {
parts.push(bytes[last..i].to_vec());
i += needle.len();
last = i;
} else {
i += 1;
}
}
parts.push(bytes[last..].to_vec());
parts
};
let fields: Vec<Value> = parts
.into_iter()
.map(|part| Value::String(global_bytes(part)))
.collect();
let variant = Value::Variant(Arc::new(VariantData::new(
global_string("List".to_string()),
fields,
)));
unsafe { push(stack, variant) }
}
_ => panic!("string_split: expected two strings on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_string_contains(stack: Stack) -> Stack {
assert!(!stack.is_null(), "string_contains: stack is empty");
let (stack, substring_val) = unsafe { pop(stack) };
assert!(!stack.is_null(), "string_contains: need two strings");
let (stack, str_val) = unsafe { pop(stack) };
match (str_val, substring_val) {
(Value::String(s), Value::String(sub)) => {
let contains = byte_contains(s.as_bytes(), sub.as_bytes());
unsafe { push(stack, Value::Bool(contains)) }
}
_ => panic!("string_contains: expected two strings on stack"),
}
}
fn byte_contains(haystack: &[u8], needle: &[u8]) -> bool {
if needle.is_empty() {
return true;
}
haystack.windows(needle.len()).any(|w| w == needle)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_string_starts_with(stack: Stack) -> Stack {
assert!(!stack.is_null(), "string_starts_with: stack is empty");
let (stack, prefix_val) = unsafe { pop(stack) };
assert!(!stack.is_null(), "string_starts_with: need two strings");
let (stack, str_val) = unsafe { pop(stack) };
match (str_val, prefix_val) {
(Value::String(s), Value::String(prefix)) => {
let starts = s.as_bytes().starts_with(prefix.as_bytes());
unsafe { push(stack, Value::Bool(starts)) }
}
_ => panic!("string_starts_with: expected two strings on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_string_char_at(stack: Stack) -> Stack {
assert!(!stack.is_null(), "string_char_at: stack is empty");
let (stack, index_val) = unsafe { pop(stack) };
assert!(!stack.is_null(), "string_char_at: need string and index");
let (stack, str_val) = unsafe { pop(stack) };
match (str_val, index_val) {
(Value::String(s), Value::Int(index)) => {
let result = if index < 0 {
-1
} else {
s.as_str_or_empty()
.chars()
.nth(index as usize)
.map(|c| c as i64)
.unwrap_or(-1)
};
unsafe { push(stack, Value::Int(result)) }
}
_ => panic!("string_char_at: expected String and Int on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_string_substring(stack: Stack) -> Stack {
assert!(!stack.is_null(), "string_substring: stack is empty");
let (stack, len_val) = unsafe { pop(stack) };
assert!(
!stack.is_null(),
"string_substring: need string, start, len"
);
let (stack, start_val) = unsafe { pop(stack) };
assert!(
!stack.is_null(),
"string_substring: need string, start, len"
);
let (stack, str_val) = unsafe { pop(stack) };
match (str_val, start_val, len_val) {
(Value::String(s), Value::Int(start), Value::Int(len)) => {
let result = if start < 0 || len < 0 {
String::new()
} else {
s.as_str_or_empty()
.chars()
.skip(start as usize)
.take(len as usize)
.collect()
};
unsafe { push(stack, Value::String(global_string(result))) }
}
_ => panic!("string_substring: expected String, Int, Int on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_char_to_string(stack: Stack) -> Stack {
assert!(!stack.is_null(), "char_to_string: stack is empty");
let (stack, code_point_val) = unsafe { pop(stack) };
match code_point_val {
Value::Int(code_point) => {
let result = if !(0..=0x10FFFF).contains(&code_point) {
String::new()
} else {
match char::from_u32(code_point as u32) {
Some(c) => c.to_string(),
None => String::new(), }
};
unsafe { push(stack, Value::String(global_string(result))) }
}
_ => panic!("char_to_string: expected Int on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_string_find(stack: Stack) -> Stack {
assert!(!stack.is_null(), "string_find: stack is empty");
let (stack, needle_val) = unsafe { pop(stack) };
assert!(!stack.is_null(), "string_find: need string and needle");
let (stack, str_val) = unsafe { pop(stack) };
match (str_val, needle_val) {
(Value::String(haystack), Value::String(needle)) => {
let haystack_str = haystack.as_str_or_empty();
let needle_str = needle.as_str_or_empty();
let result = match haystack_str.find(needle_str) {
Some(byte_pos) => {
haystack_str[..byte_pos].chars().count() as i64
}
None => -1,
};
unsafe { push(stack, Value::Int(result)) }
}
_ => panic!("string_find: expected two Strings on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_string_join(stack: Stack) -> Stack {
unsafe {
let (stack, sep_val) = pop(stack);
let sep = match &sep_val {
Value::String(s) => s.as_str_or_empty().to_owned(),
_ => panic!("string.join: expected String separator, got {:?}", sep_val),
};
let (stack, list_val) = pop(stack);
let variant_data = match &list_val {
Value::Variant(v) => v,
_ => panic!("string.join: expected Variant (list), got {:?}", list_val),
};
let parts: Vec<String> = variant_data
.fields
.iter()
.map(|v| match v {
Value::String(s) => s.as_str_or_empty().to_owned(),
Value::Int(n) => n.to_string(),
Value::Float(f) => f.to_string(),
Value::Bool(b) => if *b { "true" } else { "false" }.to_string(),
Value::Symbol(s) => format!(":{}", s.as_str_or_empty()),
_ => format!("{:?}", v),
})
.collect();
let result = parts.join(&sep);
push(stack, Value::String(global_string(result)))
}
}