use crate::executor::VirtualMachine;
use shape_runtime::context::ExecutionContext;
use shape_value::heap_value::{HashSetData, HeapKind, HeapValue};
use shape_value::{KindedSlot, NativeKind, VMError};
use std::sync::Arc;
#[inline]
fn type_error(msg: impl Into<String>) -> VMError {
VMError::RuntimeError(msg.into())
}
#[inline]
fn as_hashset(slot: &KindedSlot) -> Result<Arc<HashSetData>, VMError> {
if !matches!(slot.kind, NativeKind::Ptr(HeapKind::HashSet)) {
return Err(type_error(format!(
"Set method receiver must be a Set (got kind {:?})",
slot.kind
)));
}
let bits = slot.slot.raw();
if bits == 0 {
return Err(type_error("Set method receiver slot bits null"));
}
let arc = unsafe { Arc::<HashSetData>::from_raw(bits as *const HashSetData) };
let cloned = Arc::clone(&arc);
let _ = Arc::into_raw(arc);
Ok(cloned)
}
#[inline]
fn as_string_key(slot: &KindedSlot) -> Result<&str, VMError> {
match slot.kind {
NativeKind::String => slot
.as_str()
.ok_or_else(|| type_error("Set key kind=String but slot bits null")),
NativeKind::Ptr(HeapKind::String) => match slot.slot.as_heap_value() {
HeapValue::String(s) => Ok(s.as_str()),
_ => Err(type_error(
"Set key kind=Ptr(String) but heap arm mismatched",
)),
},
_ => Err(type_error(format!(
"Set key must be a string (got kind {:?})",
slot.kind
))),
}
}
fn result_slot_to_string_arc(result: &KindedSlot) -> Option<Arc<String>> {
match result.kind {
NativeKind::String | NativeKind::Ptr(HeapKind::String) => {
let bits = result.slot.raw();
if bits == 0 {
return None;
}
unsafe {
Arc::increment_strong_count(bits as *const String);
Some(Arc::from_raw(bits as *const String))
}
}
_ => None,
}
}
pub fn v2_has(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"Set.has() requires exactly 1 argument (key)",
));
}
let set = as_hashset(&args[0])?;
let key = as_string_key(&args[1])?;
Ok(KindedSlot::from_bool(set.contains(key)))
}
pub fn v2_size(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error("Set.size() takes no arguments"));
}
let set = as_hashset(&args[0])?;
Ok(KindedSlot::from_int(set.len() as i64))
}
pub fn v2_is_empty(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error("Set.isEmpty() takes no arguments"));
}
let set = as_hashset(&args[0])?;
Ok(KindedSlot::from_bool(set.is_empty()))
}
pub fn v2_to_array(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error("Set.toArray() takes no arguments"));
}
let _set = as_hashset(&args[0])?;
Err(VMError::NotImplemented(
"Set.toArray: SURFACE — V3-S5 ckpt-5-prime²a consumer-cascade. \
The deleted typed-array-data String `Arc<Buf<Arc<String>>>` payload + \
`KindedSlot::from_typed_array` DELETED at V3-S5 ckpt-1..ckpt-4. \
Rebuild = per-T v2-raw `*mut TypedArray<Arc<String>>` flat-struct \
construction (cluster-2 / ckpt-6 territory). REFUSED ON SIGHT: \
resurrection under any rename (Refusal #1)."
.to_string(),
))
}
pub fn v2_add(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"Set.add() requires exactly 1 argument (key)",
));
}
let key_arc: Arc<String> = result_slot_to_string_arc(&args[1]).ok_or_else(|| {
type_error(format!(
"Set.add(): key must be a string (got kind {:?})",
args[1].kind()
))
})?;
let mut hs: Arc<HashSetData> = as_hashset(&args[0])?;
Arc::make_mut(&mut hs).insert(key_arc);
Ok(KindedSlot::from_hashset(hs))
}
pub fn v2_delete(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"Set.delete() requires exactly 1 argument (key)",
));
}
let key = as_string_key(&args[1])?;
let mut hs: Arc<HashSetData> = as_hashset(&args[0])?;
Arc::make_mut(&mut hs).remove(key);
Ok(KindedSlot::from_hashset(hs))
}
pub fn v2_union(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"Set.union() requires exactly 1 argument (other)",
));
}
let lhs: Arc<HashSetData> = as_hashset(&args[0])?;
let rhs: Arc<HashSetData> = as_hashset(&args[1])?;
let mut keys: Vec<Arc<String>> = Vec::with_capacity(lhs.len() + rhs.len());
for k in lhs.keys.iter() {
keys.push(Arc::clone(k));
}
for k in rhs.keys.iter() {
keys.push(Arc::clone(k));
}
let result = HashSetData::from_keys(keys);
Ok(KindedSlot::from_hashset(Arc::new(result)))
}
pub fn v2_intersection(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"Set.intersection() requires exactly 1 argument (other)",
));
}
let lhs: Arc<HashSetData> = as_hashset(&args[0])?;
let rhs: Arc<HashSetData> = as_hashset(&args[1])?;
let (small, large) = if lhs.len() <= rhs.len() {
(lhs.as_ref(), rhs.as_ref())
} else {
(rhs.as_ref(), lhs.as_ref())
};
let mut keys: Vec<Arc<String>> = Vec::new();
for k in small.keys.iter() {
if large.contains(k.as_str()) {
keys.push(Arc::clone(k));
}
}
let result = HashSetData::from_keys(keys);
Ok(KindedSlot::from_hashset(Arc::new(result)))
}
pub fn v2_difference(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"Set.difference() requires exactly 1 argument (other)",
));
}
let lhs: Arc<HashSetData> = as_hashset(&args[0])?;
let rhs: Arc<HashSetData> = as_hashset(&args[1])?;
let mut keys: Vec<Arc<String>> = Vec::new();
for k in lhs.keys.iter() {
if !rhs.contains(k.as_str()) {
keys.push(Arc::clone(k));
}
}
let result = HashSetData::from_keys(keys);
Ok(KindedSlot::from_hashset(Arc::new(result)))
}
pub fn v2_for_each(
vm: &mut VirtualMachine,
args: &[KindedSlot],
mut ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"Set.forEach() requires exactly 1 argument (callback)",
));
}
let set: Arc<HashSetData> = as_hashset(&args[0])?;
let closure = &args[1];
let n = set.len();
for i in 0..n {
let key_slot = KindedSlot::from_string_arc(Arc::clone(&set.keys[i]));
let _ = vm.call_value_immediate_nb(closure, &[key_slot], ctx.as_deref_mut())?;
}
Ok(KindedSlot::none())
}
pub fn v2_map(
vm: &mut VirtualMachine,
args: &[KindedSlot],
mut ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"Set.map() requires exactly 1 argument (mapper)",
));
}
let set: Arc<HashSetData> = as_hashset(&args[0])?;
let closure = &args[1];
let n = set.len();
let mut out_keys: Vec<Arc<String>> = Vec::with_capacity(n);
for i in 0..n {
let key_slot = KindedSlot::from_string_arc(Arc::clone(&set.keys[i]));
let result = vm.call_value_immediate_nb(closure, &[key_slot], ctx.as_deref_mut())?;
let new_key: Arc<String> = result_slot_to_string_arc(&result).ok_or_else(|| {
type_error(format!(
"Set.map(): mapper must return a string (got kind {:?})",
result.kind()
))
})?;
out_keys.push(new_key);
}
let result = HashSetData::from_keys(out_keys);
Ok(KindedSlot::from_hashset(Arc::new(result)))
}
pub fn v2_filter(
vm: &mut VirtualMachine,
args: &[KindedSlot],
mut ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"Set.filter() requires exactly 1 argument (predicate)",
));
}
let set: Arc<HashSetData> = as_hashset(&args[0])?;
let closure = &args[1];
let n = set.len();
let mut out_keys: Vec<Arc<String>> = Vec::new();
for i in 0..n {
let key_arc = Arc::clone(&set.keys[i]);
let key_slot = KindedSlot::from_string_arc(Arc::clone(&key_arc));
let result = vm.call_value_immediate_nb(closure, &[key_slot], ctx.as_deref_mut())?;
let keep = result.as_bool().ok_or_else(|| {
type_error(format!(
"Set.filter(): predicate must return bool (got kind {:?})",
result.kind()
))
})?;
if keep {
out_keys.push(key_arc);
}
}
let mut index: std::collections::HashMap<u64, Vec<u32>> = std::collections::HashMap::new();
for (i, k) in out_keys.iter().enumerate() {
let h = fnv1a_hash_local(k.as_bytes());
index.entry(h).or_default().push(i as u32);
}
let result = HashSetData {
keys: Arc::new(out_keys),
index,
};
Ok(KindedSlot::from_hashset(Arc::new(result)))
}
#[inline]
fn fnv1a_hash_local(bytes: &[u8]) -> u64 {
let mut h: u64 = 0xcbf29ce484222325;
for &b in bytes {
h ^= b as u64;
h = h.wrapping_mul(0x100000001b3);
}
h
}