use std::{cell::Cell, fmt::Write, mem};
use hashbrown::HashTable;
use smallvec::SmallVec;
use super::{MontyIter, PyTrait};
use crate::{
args::ArgValues,
bytecode::{CallResult, ContainsVM, DropWithVM, RecursionToken, VM},
defer_drop, defer_drop_mut, defer_drop_vm_mut,
exception_private::{ExcType, RunResult},
hash::HashValue,
heap::{
BorrowedHeapRead, BorrowedHeapReadMut, ContainsHeap, DropWithHeap, HeapData, HeapGuard, HeapId, HeapItem,
HeapRead, HeapReadOutput, heap_read_ref_as_field, heap_read_ref_as_field_mut,
},
intern::StaticStrings,
resource::ResourceTracker,
types::{LazyHeapSet, Type},
value::{EitherStr, Value},
};
#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct SetEntry {
pub(crate) value: Value,
pub(crate) hash: u64,
}
#[derive(Debug, Default)]
pub(crate) struct SetStorage {
indices: HashTable<usize>,
entries: Vec<SetEntry>,
}
impl SetStorage {
fn new() -> Self {
Self::default()
}
fn with_capacity(capacity: usize) -> Self {
Self {
indices: HashTable::with_capacity(capacity),
entries: Vec::with_capacity(capacity),
}
}
fn from_entries(entries: Vec<(Value, u64)>) -> Self {
let mut storage = Self::with_capacity(entries.len());
for (idx, (value, hash)) in entries.into_iter().enumerate() {
storage.entries.push(SetEntry { value, hash });
storage.indices.insert_unique(hash, idx, |&i| storage.entries[i].hash);
}
storage
}
fn clone_entries(&self, heap: &impl ContainsHeap) -> Vec<(Value, u64)> {
self.entries
.iter()
.map(|e| (e.value.clone_with_heap(heap), e.hash))
.collect()
}
fn len(&self) -> usize {
self.entries.len()
}
fn is_empty(&self) -> bool {
self.entries.is_empty()
}
fn add(&mut self, value: Value, vm: &mut VM<'_, impl ResourceTracker>) -> RunResult<bool> {
let mut value_guard = HeapGuard::new(value, vm);
let (value, vm) = value_guard.as_parts_mut();
let hash = set_element_hash(value, vm)?;
let existing = self
.indices
.find(hash, |&idx| value.py_eq(&self.entries[idx].value, vm).unwrap_or(false));
if existing.is_some() {
Ok(false)
} else {
vm.heap.track_growth(mem::size_of::<SetEntry>())?;
let index = self.entries.len();
let value = value_guard.into_inner();
self.entries.push(SetEntry { value, hash });
self.indices.insert_unique(hash, index, |&idx| self.entries[idx].hash);
Ok(true)
}
}
}
impl<'h> HeapRead<'h, SetStorage> {
fn remove(&mut self, value: &Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<bool> {
let hash = set_element_hash(value, vm)?;
let mut candidates: SmallVec<[usize; 2]> = SmallVec::new();
let storage = &self.get(vm.heap);
storage.indices.find(hash, |&idx| {
if storage.entries[idx].hash == hash {
candidates.push(idx);
}
false
});
let mut found_index = None;
for candidate_index in candidates {
let candidate_value = self.get(vm.heap).entries[candidate_index].value.clone_with_heap(vm);
defer_drop!(candidate_value, vm);
if value.py_eq(candidate_value, vm)? {
found_index = Some(candidate_index);
break;
}
}
let Some(index) = found_index else {
return Ok(false);
};
let storage = self.get_mut(vm.heap);
let removed_entry = storage.entries.remove(index);
storage.indices.clear();
for (idx, e) in storage.entries.iter().enumerate() {
storage.indices.insert_unique(e.hash, idx, |&i| storage.entries[i].hash);
}
removed_entry.value.drop_with_heap(vm);
Ok(true)
}
fn discard(&mut self, value: &Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<()> {
self.remove(value, vm)?;
Ok(())
}
fn pop(&mut self, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Value> {
if self.get(vm.heap).is_empty() {
return Err(ExcType::key_error_pop_empty_set());
}
let storage = self.get_mut(vm.heap);
let entry = storage.entries.pop().expect("checked non-empty");
storage
.indices
.find_entry(entry.hash, |&idx| idx == storage.entries.len())
.expect("entry must exist")
.remove();
Ok(entry.value)
}
fn clear(&mut self, vm: &mut VM<'h, impl ResourceTracker>) {
let entries = mem::take(&mut self.get_mut(vm.heap).entries);
self.get_mut(vm.heap).indices.clear();
entries.drop_with_heap(vm);
}
}
impl SetStorage {
fn clone_with_heap(&self, heap: &impl ContainsHeap) -> Self {
Self {
indices: self.indices.clone(),
entries: self
.entries
.iter()
.map(|entry| SetEntry {
value: entry.value.clone_with_heap(heap),
hash: entry.hash,
})
.collect(),
}
}
}
impl<'h> HeapRead<'h, SetStorage> {
pub fn contains(&self, value: &Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<bool> {
let hash = set_element_hash(value, vm)?;
let mut candidates: SmallVec<[usize; 2]> = SmallVec::new();
let storage = &self.get(vm.heap);
storage.indices.find(hash, |&idx| {
if storage.entries[idx].hash == hash {
candidates.push(idx);
}
false
});
for candidate_index in candidates {
let candidate_value = self.get(vm.heap).entries[candidate_index].value.clone_with_heap(vm);
defer_drop!(candidate_value, vm);
if value.py_eq(candidate_value, vm)? {
return Ok(true);
}
}
Ok(false)
}
}
impl SetStorage {
pub(crate) fn iter(&self) -> impl Iterator<Item = &Value> {
self.entries.iter().map(|e| &e.value)
}
pub(crate) fn value_at(&self, index: usize) -> Option<&Value> {
self.entries.get(index).map(|e| &e.value)
}
fn collect_dec_ref_ids(&mut self, stack: &mut Vec<HeapId>) {
for entry in &mut self.entries {
if let Value::Ref(id) = &entry.value {
stack.push(*id);
#[cfg(feature = "memory-model-checks")]
entry.value.dec_ref_forget();
}
}
}
}
impl<'h> HeapRead<'h, SetStorage> {
fn eq(&self, other: &Self, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<bool> {
if self.get(vm.heap).len() != other.get(vm.heap).len() {
return Ok(false);
}
let iter = self.iter(vm)?;
defer_drop_vm_mut!(iter, vm);
while let Some(elem) = iter.next(vm)? {
if !other.contains(elem, vm)? {
return Ok(false);
}
}
Ok(true)
}
#[expect(clippy::iter_not_returning_iterator)]
pub(crate) fn iter<R: ResourceTracker>(&self, vm: &mut VM<'h, R>) -> RunResult<SetIter<'_, 'h>> {
SetIter::new(self, vm)
}
}
pub(crate) struct SetIter<'a, 'h> {
storage: &'a HeapRead<'h, SetStorage>,
index: usize,
expected_len: usize,
token: RecursionToken,
current: Value,
}
impl<'a, 'h> SetIter<'a, 'h> {
fn new<R: ResourceTracker>(storage: &'a HeapRead<'h, SetStorage>, vm: &mut VM<'h, R>) -> RunResult<Self> {
let expected_len = storage.get(vm.heap).entries.len();
let token = vm.recursion_token()?;
Ok(Self {
storage,
index: 0,
expected_len,
token,
current: Value::Undefined,
})
}
pub(crate) fn next<'i, R: ResourceTracker>(&'i mut self, vm: &mut VM<'h, R>) -> RunResult<Option<&'i Value>> {
mem::replace(&mut self.current, Value::Undefined).drop_with_heap(vm.heap);
vm.heap.check_time()?;
let current = self.storage.get(vm.heap);
if current.entries.len() != self.expected_len {
return Err(ExcType::runtime_error_set_changed_size());
}
if self.index >= self.expected_len {
return Ok(None);
}
self.current = current.entries[self.index].value.clone_with_heap(vm.heap);
self.index += 1;
Ok(Some(&self.current))
}
}
impl<'h> DropWithVM<'h> for SetIter<'_, 'h> {
fn drop_with_vm(self, container: &mut impl ContainsVM<'h>) {
self.current.drop_with_heap(container);
self.token.drop_with_vm(container);
}
}
impl SetStorage {
fn is_subset(&self, other: &Self, vm: &mut VM<'_, impl ResourceTracker>) -> RunResult<bool> {
for entry in &self.entries {
if !vm.heap.protect(other).contains(&entry.value, vm)? {
return Ok(false);
}
}
Ok(true)
}
fn is_superset(&self, other: &Self, vm: &mut VM<'_, impl ResourceTracker>) -> RunResult<bool> {
other.is_subset(self, vm)
}
fn is_disjoint(&self, other: &Self, vm: &mut VM<'_, impl ResourceTracker>) -> RunResult<bool> {
let (smaller, larger) = if self.len() <= other.len() {
(self, other)
} else {
(other, self)
};
for entry in &smaller.entries {
if vm.heap.protect(larger).contains(&entry.value, vm)? {
return Ok(false);
}
}
Ok(true)
}
}
impl<'h> HeapRead<'h, SetStorage> {
fn union(&self, other: &Self, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<SetStorage> {
let mut result_guard = HeapGuard::new(self.get(vm.heap).clone_with_heap(vm), vm);
let (result, vm) = result_guard.as_parts_mut();
let len = other.get(vm.heap).len();
for idx in 0..len {
let value = other.get(vm.heap).entries[idx].value.clone_with_heap(vm);
result.add(value, vm)?;
}
Ok(result_guard.into_inner())
}
fn intersection(&self, other: &Self, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<SetStorage> {
let mut result_guard = HeapGuard::new(SetStorage::new(), vm);
let (result, vm) = result_guard.as_parts_mut();
let (smaller, larger) = if self.get(vm.heap).len() <= other.get(vm.heap).len() {
(self, other)
} else {
(other, self)
};
let len = smaller.get(vm.heap).len();
for idx in 0..len {
let value = smaller.get(vm.heap).entries[idx].value.clone_with_heap(vm);
let mut value_guard = HeapGuard::new(value, vm);
let (value, vm) = value_guard.as_parts_mut();
if larger.contains(value, vm)? {
let (value, vm) = value_guard.into_parts();
result.add(value, vm)?;
}
}
Ok(result_guard.into_inner())
}
fn difference(&self, other: &Self, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<SetStorage> {
let mut result_guard = HeapGuard::new(SetStorage::new(), vm);
let (result, vm) = result_guard.as_parts_mut();
let len = self.get(vm.heap).len();
for idx in 0..len {
let value = self.get(vm.heap).entries[idx].value.clone_with_heap(vm);
let mut value_guard = HeapGuard::new(value, vm);
let (value, vm) = value_guard.as_parts_mut();
if !other.contains(value, vm)? {
let (value, vm) = value_guard.into_parts();
result.add(value, vm)?;
}
}
Ok(result_guard.into_inner())
}
fn symmetric_difference(&self, other: &Self, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<SetStorage> {
let mut result_guard = HeapGuard::new(SetStorage::new(), vm);
let (result, vm) = result_guard.as_parts_mut();
let len = self.get(vm.heap).len();
for idx in 0..len {
let value = self.get(vm.heap).entries[idx].value.clone_with_heap(vm);
let mut value_guard = HeapGuard::new(value, vm);
let (value, vm) = value_guard.as_parts_mut();
if !other.contains(value, vm)? {
let (value, vm) = value_guard.into_parts();
result.add(value, vm)?;
}
}
let len = other.get(vm.heap).len();
for idx in 0..len {
let value = other.get(vm.heap).entries[idx].value.clone_with_heap(vm);
let mut value_guard = HeapGuard::new(value, vm);
let (value, vm) = value_guard.as_parts_mut();
if !self.contains(value, vm)? {
let (value, vm) = value_guard.into_parts();
result.add(value, vm)?;
}
}
Ok(result_guard.into_inner())
}
}
impl<'h> HeapRead<'h, SetStorage> {
fn repr_fmt<T: ResourceTracker>(
&self,
f: &mut impl Write,
vm: &mut VM<'h, T>,
heap_ids: &mut LazyHeapSet,
type_name: &str,
) -> RunResult<()> {
let len = self.get(vm.heap).len();
if len == 0 {
return Ok(write!(f, "{type_name}()")?);
}
let Ok(mut guard) = vm.recursion_guard() else {
return Ok(f.write_str("{...}")?);
};
let vm = &mut *guard;
let needs_prefix = type_name != "set";
if needs_prefix {
write!(f, "{type_name}(")?;
}
f.write_char('{')?;
for i in 0..len {
if i > 0 {
if vm.heap.check_time().is_err() {
f.write_str(", ...[timeout]")?;
break;
}
f.write_str(", ")?;
}
let value = self
.get(vm.heap)
.value_at(i)
.expect("index in range")
.clone_with_heap(vm.heap);
defer_drop!(value, vm);
value.py_repr_fmt(f, vm, heap_ids)?;
}
f.write_char('}')?;
if needs_prefix {
f.write_char(')')?;
}
Ok(())
}
}
impl SetStorage {
fn estimate_size(&self) -> usize {
mem::size_of::<Self>() + self.len() * mem::size_of::<SetEntry>()
}
}
#[derive(Debug, Default)]
pub(crate) struct Set(SetStorage);
impl Set {
#[must_use]
pub fn new() -> Self {
Self(SetStorage::new())
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self(SetStorage::with_capacity(capacity))
}
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn add(&mut self, value: Value, vm: &mut VM<'_, impl ResourceTracker>) -> RunResult<bool> {
self.0.add(value, vm)
}
}
impl<'h> HeapRead<'h, Set> {
pub fn remove(&mut self, value: &Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<()> {
if self.storage_mut().remove(value, vm)? {
Ok(())
} else {
Err(ExcType::key_error(value, vm))
}
}
pub fn discard(&mut self, value: &Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<()> {
self.storage_mut().discard(value, vm)
}
pub fn pop(&mut self, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Value> {
self.storage_mut().pop(vm)
}
pub fn clear(&mut self, vm: &mut VM<'h, impl ResourceTracker>) {
self.storage_mut().clear(vm);
}
#[must_use]
pub fn copy(&self, vm: &VM<'h, impl ResourceTracker>) -> Set {
Set(self.get(vm.heap).0.clone_with_heap(vm.heap))
}
fn storage(&self) -> BorrowedHeapRead<'_, 'h, SetStorage> {
heap_read_ref_as_field!(self, Set, 0)
}
fn storage_mut(&mut self) -> BorrowedHeapReadMut<'_, 'h, SetStorage> {
heap_read_ref_as_field_mut!(self, Set, 0)
}
}
impl Set {
pub(crate) fn storage(&self) -> &SetStorage {
&self.0
}
pub(crate) fn iter(&self) -> impl Iterator<Item = &Value> {
self.0.iter()
}
pub fn init(vm: &mut VM<'_, impl ResourceTracker>, args: ArgValues) -> RunResult<Value> {
let value = args.get_zero_one_arg("set", vm.heap)?;
let set = match value {
None => Self::new(),
Some(v) => Self::from_iterable(v, vm)?,
};
let heap_id = vm.heap.allocate(HeapData::Set(set))?;
Ok(Value::Ref(heap_id))
}
fn from_iterator(iter: MontyIter, vm: &mut VM<'_, impl ResourceTracker>) -> RunResult<Self> {
defer_drop_mut!(iter, vm);
let mut set = Self::with_capacity(iter.preallocation_hint(mem::size_of::<SetEntry>(), vm)?);
while let Some(item) = iter.for_next(vm)? {
set.add(item, vm)?;
}
Ok(set)
}
fn from_iterable(iterable: Value, vm: &mut VM<'_, impl ResourceTracker>) -> RunResult<Self> {
let iter = MontyIter::new(iterable, vm)?;
let set = Self::from_iterator(iter, vm)?;
Ok(set)
}
}
impl<'h> HeapRead<'h, Set> {
pub fn add(&mut self, value: Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<bool> {
let mut value_guard = HeapGuard::new(value, vm);
let (value, vm) = value_guard.as_parts();
let hash = set_element_hash(value, vm)?;
let mut candidates: SmallVec<[usize; 2]> = SmallVec::new();
let storage = &self.get(vm.heap).0;
storage.indices.find(hash, |&idx| {
if storage.entries[idx].hash == hash {
candidates.push(idx);
}
false
});
for candidate_index in candidates {
let candidate_value = self.get(vm.heap).0.entries[candidate_index].value.clone_with_heap(vm);
defer_drop!(candidate_value, vm);
if value.py_eq(candidate_value, vm)? {
return Ok(false);
}
}
vm.heap.track_growth(mem::size_of::<SetEntry>())?;
let (value, vm) = value_guard.into_parts();
let storage = &mut self.get_mut(vm.heap).0;
let index = storage.entries.len();
storage.entries.push(SetEntry { value, hash });
storage
.indices
.insert_unique(hash, index, |&idx| storage.entries[idx].hash);
Ok(true)
}
pub(crate) fn contains(&self, value: &Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<bool> {
self.storage().contains(value, vm)
}
fn hr_update(&mut self, other: Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<()> {
let entries_opt = {
match &other {
Value::Ref(id) => match vm.heap.get(*id) {
HeapData::Set(s) => Some(s.0.clone_entries(vm.heap)),
HeapData::FrozenSet(fs) => Some(fs.storage.clone_entries(vm.heap)),
_ => None,
},
_ => None,
}
};
if let Some(entries) = entries_opt {
other.drop_with_heap(vm);
for (value, _hash) in entries {
self.add(value, vm)?;
}
return Ok(());
}
let temp_set = Set::from_iterable(other, vm)?;
let entries: Vec<SetEntry> = temp_set.0.entries.into_iter().collect();
for entry in entries {
self.add(entry.value, vm)?;
}
Ok(())
}
fn set_algebra(&self, other: Value, op: SetAlgebra, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Value> {
let other_storage = Set::get_storage_from_value(other, vm)?;
defer_drop!(other_storage, vm);
let other_storage = vm.heap.protect(other_storage);
let result = match op {
SetAlgebra::Union => self.storage().union(&other_storage, vm)?,
SetAlgebra::Intersection => self.storage().intersection(&other_storage, vm)?,
SetAlgebra::Difference => self.storage().difference(&other_storage, vm)?,
SetAlgebra::SymmetricDifference => self.storage().symmetric_difference(&other_storage, vm)?,
};
let heap_id = vm.heap.allocate(HeapData::Set(Set(result)))?;
Ok(Value::Ref(heap_id))
}
fn comparison_op(
&self,
other: &Value,
op: SetComparison,
vm: &mut VM<'h, impl ResourceTracker>,
) -> RunResult<bool> {
let entries_opt = match other {
Value::Ref(id) => match vm.heap.get(*id) {
HeapData::Set(s) => Some(s.0.clone_entries(vm.heap)),
HeapData::FrozenSet(fs) => Some(fs.storage.clone_entries(vm.heap)),
_ => None,
},
_ => None,
};
let other_storage = if let Some(entries) = entries_opt {
SetStorage::from_entries(entries)
} else {
let temp = Set::from_iterable(other.clone_with_heap(vm), vm)?;
temp.0
};
defer_drop!(other_storage, vm);
let self_storage = self.get(vm.heap).0.clone_with_heap(vm.heap);
defer_drop!(self_storage, vm);
match op {
SetComparison::Subset => self_storage.is_subset(other_storage, vm),
SetComparison::Superset => self_storage.is_superset(other_storage, vm),
SetComparison::Disjoint => self_storage.is_disjoint(other_storage, vm),
}
}
}
#[derive(Debug, Clone, Copy)]
enum SetAlgebra {
Union,
Intersection,
Difference,
SymmetricDifference,
}
#[derive(Debug, Clone, Copy)]
enum SetComparison {
Subset,
Superset,
Disjoint,
}
impl DropWithHeap for Set {
fn drop_with_heap<H: ContainsHeap>(self, heap: &mut H) {
self.0.drop_with_heap(heap);
}
}
impl DropWithHeap for SetStorage {
fn drop_with_heap<H: ContainsHeap>(self, heap: &mut H) {
self.entries.drop_with_heap(heap);
}
}
impl DropWithHeap for FrozenSet {
fn drop_with_heap<H: ContainsHeap>(self, heap: &mut H) {
self.storage.drop_with_heap(heap);
}
}
impl<'h> HeapRead<'h, FrozenSet> {
pub(crate) fn contains(&self, value: &Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<bool> {
self.storage().contains(value, vm)
}
pub(crate) fn binary_op_value(
&self,
other: &Value,
op: SetBinaryOp,
vm: &mut VM<'h, impl ResourceTracker>,
) -> RunResult<Option<FrozenSet>> {
let Some(other_storage) = get_storage_from_set_operand(other, vm)? else {
return Ok(None);
};
defer_drop!(other_storage, vm);
let other_storage = vm.heap.protect(other_storage);
let result = match op {
SetBinaryOp::And => FrozenSet::wrap(self.storage().intersection(&other_storage, vm)?),
SetBinaryOp::Or => FrozenSet::wrap(self.storage().union(&other_storage, vm)?),
SetBinaryOp::Xor => FrozenSet::wrap(self.storage().symmetric_difference(&other_storage, vm)?),
SetBinaryOp::Sub => FrozenSet::wrap(self.storage().difference(&other_storage, vm)?),
};
Ok(Some(result))
}
fn set_algebra(&self, other: Value, op: SetAlgebra, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Value> {
let other_storage = Set::get_storage_from_value(other, vm)?;
defer_drop!(other_storage, vm);
let other_storage = vm.heap.protect(other_storage);
let result = match op {
SetAlgebra::Union => self.storage().union(&other_storage, vm)?,
SetAlgebra::Intersection => self.storage().intersection(&other_storage, vm)?,
SetAlgebra::Difference => self.storage().difference(&other_storage, vm)?,
SetAlgebra::SymmetricDifference => self.storage().symmetric_difference(&other_storage, vm)?,
};
let heap_id = vm.heap.allocate(HeapData::FrozenSet(FrozenSet::wrap(result)))?;
Ok(Value::Ref(heap_id))
}
fn comparison_op(
&self,
other: &Value,
op: SetComparison,
vm: &mut VM<'h, impl ResourceTracker>,
) -> RunResult<bool> {
let entries_opt = match other {
Value::Ref(id) => match vm.heap.get(*id) {
HeapData::Set(s) => Some(s.0.clone_entries(vm.heap)),
HeapData::FrozenSet(fs) => Some(fs.storage.clone_entries(vm.heap)),
_ => None,
},
_ => None,
};
let other_storage = if let Some(entries) = entries_opt {
SetStorage::from_entries(entries)
} else {
let temp = Set::from_iterable(other.clone_with_heap(vm), vm)?;
temp.0
};
defer_drop!(other_storage, vm);
let self_storage = self.get(vm.heap).storage.clone_with_heap(vm.heap);
defer_drop!(self_storage, vm);
match op {
SetComparison::Subset => self_storage.is_subset(other_storage, vm),
SetComparison::Superset => self_storage.is_superset(other_storage, vm),
SetComparison::Disjoint => self_storage.is_disjoint(other_storage, vm),
}
}
fn storage(&self) -> BorrowedHeapRead<'_, 'h, SetStorage> {
heap_read_ref_as_field!(self, FrozenSet, storage)
}
}
impl DropWithHeap for SetEntry {
fn drop_with_heap<H: ContainsHeap>(self, heap: &mut H) {
self.value.drop_with_heap(heap);
}
}
impl<'h> PyTrait<'h> for HeapRead<'h, Set> {
fn py_type(&self, _vm: &VM<'h, impl ResourceTracker>) -> Type {
Type::Set
}
fn py_len(&self, vm: &VM<'h, impl ResourceTracker>) -> Option<usize> {
Some(self.get(vm.heap).len())
}
fn py_eq_impl(&self, other: &Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Option<bool>> {
match other.read_heap(vm) {
Some(HeapReadOutput::Set(other)) => Ok(Some(self.storage().eq(&other.storage(), vm)?)),
Some(HeapReadOutput::FrozenSet(other)) => Ok(Some(self.storage().eq(&other.storage(), vm)?)),
_ => Ok(None),
}
}
fn py_bool(&self, vm: &mut VM<'h, impl ResourceTracker>) -> bool {
!self.get(vm.heap).is_empty()
}
fn py_repr_fmt(
&self,
f: &mut impl Write,
vm: &mut VM<'h, impl ResourceTracker>,
heap_ids: &mut LazyHeapSet,
) -> RunResult<()> {
self.storage().repr_fmt(f, vm, heap_ids, "set")
}
fn py_call_attr(
&mut self,
_self_id: HeapId,
vm: &mut VM<'h, impl ResourceTracker>,
attr: &EitherStr,
args: ArgValues,
) -> RunResult<CallResult> {
let value = match attr.static_string() {
Some(StaticStrings::Add) => {
let value = args.get_one_arg("set.add", vm.heap)?;
self.add(value, vm)?;
Ok(Value::None)
}
Some(StaticStrings::Remove) => {
let value = args.get_one_arg("set.remove", vm.heap)?;
defer_drop!(value, vm);
self.remove(value, vm)?;
Ok(Value::None)
}
Some(StaticStrings::Discard) => {
let value = args.get_one_arg("set.discard", vm.heap)?;
defer_drop!(value, vm);
self.discard(value, vm)?;
Ok(Value::None)
}
Some(StaticStrings::Pop) => {
args.check_zero_args("set.pop", vm.heap)?;
self.pop(vm)
}
Some(StaticStrings::Clear) => {
args.check_zero_args("set.clear", vm.heap)?;
self.clear(vm);
Ok(Value::None)
}
Some(StaticStrings::Copy) => {
args.check_zero_args("set.copy", vm.heap)?;
let copy = self.copy(vm);
let heap_id = vm.heap.allocate(HeapData::Set(copy))?;
Ok(Value::Ref(heap_id))
}
Some(StaticStrings::Update) => {
let other = args.get_one_arg("set.update", vm.heap)?;
self.hr_update(other, vm)?;
Ok(Value::None)
}
Some(StaticStrings::Union) => {
let other = args.get_one_arg("set.union", vm.heap)?;
self.set_algebra(other, SetAlgebra::Union, vm)
}
Some(StaticStrings::Intersection) => {
let other = args.get_one_arg("set.intersection", vm.heap)?;
self.set_algebra(other, SetAlgebra::Intersection, vm)
}
Some(StaticStrings::Difference) => {
let other = args.get_one_arg("set.difference", vm.heap)?;
self.set_algebra(other, SetAlgebra::Difference, vm)
}
Some(StaticStrings::SymmetricDifference) => {
let other = args.get_one_arg("set.symmetric_difference", vm.heap)?;
self.set_algebra(other, SetAlgebra::SymmetricDifference, vm)
}
Some(StaticStrings::Issubset) => {
let other = args.get_one_arg("set.issubset", vm.heap)?;
defer_drop!(other, vm);
Ok(Value::Bool(self.comparison_op(other, SetComparison::Subset, vm)?))
}
Some(StaticStrings::Issuperset) => {
let other = args.get_one_arg("set.issuperset", vm.heap)?;
defer_drop!(other, vm);
Ok(Value::Bool(self.comparison_op(other, SetComparison::Superset, vm)?))
}
Some(StaticStrings::Isdisjoint) => {
let other = args.get_one_arg("set.isdisjoint", vm.heap)?;
defer_drop!(other, vm);
Ok(Value::Bool(self.comparison_op(other, SetComparison::Disjoint, vm)?))
}
_ => {
args.drop_with_heap(vm);
return Err(ExcType::attribute_error(Type::Set, attr.as_str(vm.interns)));
}
};
value.map(CallResult::Value)
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) enum SetBinaryOp {
And,
Or,
Xor,
Sub,
}
impl<'h> HeapRead<'h, Set> {
pub(crate) fn binary_op_value(
&self,
other: &Value,
op: SetBinaryOp,
vm: &mut VM<'h, impl ResourceTracker>,
) -> RunResult<Option<Set>> {
let Some(other_storage) = get_storage_from_set_operand(other, vm)? else {
return Ok(None);
};
defer_drop!(other_storage, vm);
let other_storage = vm.heap.protect(other_storage);
let result = match op {
SetBinaryOp::And => Set(self.storage().intersection(&other_storage, vm)?),
SetBinaryOp::Or => Set(self.storage().union(&other_storage, vm)?),
SetBinaryOp::Xor => Set(self.storage().symmetric_difference(&other_storage, vm)?),
SetBinaryOp::Sub => Set(self.storage().difference(&other_storage, vm)?),
};
Ok(Some(result))
}
}
impl Set {
fn get_storage_from_value(value: Value, vm: &mut VM<'_, impl ResourceTracker>) -> RunResult<SetStorage> {
let entries_opt = match &value {
Value::Ref(id) => match vm.heap.get(*id) {
HeapData::Set(set) => Some(set.0.clone_entries(vm.heap)),
HeapData::FrozenSet(set) => Some(set.storage.clone_entries(vm.heap)),
_ => None,
},
_ => None,
};
if let Some(entries) = entries_opt {
value.drop_with_heap(vm);
return Ok(SetStorage::from_entries(entries));
}
let temp_set = Self::from_iterable(value, vm)?;
Ok(temp_set.0)
}
}
impl HeapItem for Set {
fn py_estimate_size(&self) -> usize {
self.0.estimate_size()
}
fn py_dec_ref_ids(&mut self, stack: &mut Vec<HeapId>) {
self.0.collect_dec_ref_ids(stack);
}
}
#[derive(Debug, Default)]
pub(crate) struct FrozenSet {
storage: SetStorage,
cached_hash: Cell<Option<HashValue>>,
}
impl FrozenSet {
#[must_use]
pub fn wrap(storage: SetStorage) -> Self {
Self {
storage,
cached_hash: Cell::new(None),
}
}
#[must_use]
pub fn new() -> Self {
Self::wrap(SetStorage::new())
}
#[must_use]
pub fn len(&self) -> usize {
self.storage.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.storage.is_empty()
}
pub(crate) fn storage(&self) -> &SetStorage {
&self.storage
}
}
impl FrozenSet {
pub fn from_set(set: Set) -> Self {
Self::wrap(set.0)
}
pub fn init(vm: &mut VM<'_, impl ResourceTracker>, args: ArgValues) -> RunResult<Value> {
let value = args.get_zero_one_arg("frozenset", vm.heap)?;
let frozenset = match value {
None => Self::new(),
Some(v) => Self::from_set(Set::from_iterable(v, vm)?),
};
let heap_id = vm.heap.allocate(HeapData::FrozenSet(frozenset))?;
Ok(Value::Ref(heap_id))
}
}
impl<'h> PyTrait<'h> for HeapRead<'h, FrozenSet> {
fn py_type(&self, _vm: &VM<'h, impl ResourceTracker>) -> Type {
Type::FrozenSet
}
fn py_len(&self, vm: &VM<'h, impl ResourceTracker>) -> Option<usize> {
Some(self.get(vm.heap).len())
}
fn py_eq_impl(&self, other: &Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Option<bool>> {
match other.read_heap(vm) {
Some(HeapReadOutput::FrozenSet(other)) => Ok(Some(self.storage().eq(&other.storage(), vm)?)),
Some(HeapReadOutput::Set(other)) => Ok(Some(self.storage().eq(&other.storage(), vm)?)),
_ => Ok(None),
}
}
fn py_hash(&self, _self_id: HeapId, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Option<HashValue>> {
if let Some(cached) = self.get(vm.heap).cached_hash.get() {
return Ok(Some(cached));
}
let mut hash: u64 = 0;
let storage = self.storage();
let iter = storage.iter(vm)?;
defer_drop_vm_mut!(iter, vm);
while let Some(item) = iter.next(vm)? {
hash ^= set_element_hash(item, vm)?;
}
let hash = HashValue::new(hash);
self.get(vm.heap).cached_hash.set(Some(hash));
Ok(Some(hash))
}
fn py_bool(&self, vm: &mut VM<'h, impl ResourceTracker>) -> bool {
!self.get(vm.heap).is_empty()
}
fn py_repr_fmt(
&self,
f: &mut impl Write,
vm: &mut VM<'h, impl ResourceTracker>,
heap_ids: &mut LazyHeapSet,
) -> RunResult<()> {
self.storage().repr_fmt(f, vm, heap_ids, "frozenset")
}
fn py_call_attr(
&mut self,
_self_id: HeapId,
vm: &mut VM<'h, impl ResourceTracker>,
attr: &EitherStr,
args: ArgValues,
) -> RunResult<CallResult> {
let value = match attr.static_string() {
Some(StaticStrings::Copy) => {
args.check_zero_args("frozenset.copy", vm.heap)?;
let cloned = self.get(vm.heap).storage.clone_with_heap(vm.heap);
let heap_id = vm.heap.allocate(HeapData::FrozenSet(FrozenSet::wrap(cloned)))?;
Ok(Value::Ref(heap_id))
}
Some(StaticStrings::Union) => {
let other = args.get_one_arg("frozenset.union", vm.heap)?;
self.set_algebra(other, SetAlgebra::Union, vm)
}
Some(StaticStrings::Intersection) => {
let other = args.get_one_arg("frozenset.intersection", vm.heap)?;
self.set_algebra(other, SetAlgebra::Intersection, vm)
}
Some(StaticStrings::Difference) => {
let other = args.get_one_arg("frozenset.difference", vm.heap)?;
self.set_algebra(other, SetAlgebra::Difference, vm)
}
Some(StaticStrings::SymmetricDifference) => {
let other = args.get_one_arg("frozenset.symmetric_difference", vm.heap)?;
self.set_algebra(other, SetAlgebra::SymmetricDifference, vm)
}
Some(StaticStrings::Issubset) => {
let other = args.get_one_arg("frozenset.issubset", vm.heap)?;
defer_drop!(other, vm);
Ok(Value::Bool(self.comparison_op(other, SetComparison::Subset, vm)?))
}
Some(StaticStrings::Issuperset) => {
let other = args.get_one_arg("frozenset.issuperset", vm.heap)?;
defer_drop!(other, vm);
Ok(Value::Bool(self.comparison_op(other, SetComparison::Superset, vm)?))
}
Some(StaticStrings::Isdisjoint) => {
let other = args.get_one_arg("frozenset.isdisjoint", vm.heap)?;
defer_drop!(other, vm);
Ok(Value::Bool(self.comparison_op(other, SetComparison::Disjoint, vm)?))
}
_ => {
args.drop_with_heap(vm);
return Err(ExcType::attribute_error(Type::FrozenSet, attr.as_str(vm.interns)));
}
};
value.map(CallResult::Value)
}
}
impl HeapItem for FrozenSet {
fn py_estimate_size(&self) -> usize {
self.storage.estimate_size()
}
fn py_dec_ref_ids(&mut self, stack: &mut Vec<HeapId>) {
self.storage.collect_dec_ref_ids(stack);
}
}
fn get_storage_from_set_operand(value: &Value, vm: &mut VM<'_, impl ResourceTracker>) -> RunResult<Option<SetStorage>> {
let Value::Ref(id) = value else {
return Ok(None);
};
match vm.heap.read(*id) {
HeapReadOutput::Set(set) => Ok(Some(SetStorage::from_entries(
set.get(vm.heap).0.clone_entries(vm.heap),
))),
HeapReadOutput::FrozenSet(set) => Ok(Some(SetStorage::from_entries(
set.get(vm.heap).storage.clone_entries(vm.heap),
))),
HeapReadOutput::DictKeysView(view) => {
let Set(storage) = view.to_set(vm)?;
Ok(Some(storage))
}
HeapReadOutput::DictItemsView(view) => {
let Set(storage) = view.to_set(vm)?;
Ok(Some(storage))
}
_ => Ok(None),
}
}
impl serde::Serialize for SetStorage {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.entries.serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for SetStorage {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let entries: Vec<SetEntry> = serde::Deserialize::deserialize(deserializer)?;
let mut indices = HashTable::with_capacity(entries.len());
for (idx, entry) in entries.iter().enumerate() {
indices.insert_unique(entry.hash, idx, |&i| entries[i].hash);
}
Ok(Self { indices, entries })
}
}
impl serde::Serialize for Set {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.0.serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for Set {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
Ok(Self(SetStorage::deserialize(deserializer)?))
}
}
impl serde::Serialize for FrozenSet {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.storage.serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for FrozenSet {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
Ok(Self::wrap(SetStorage::deserialize(deserializer)?))
}
}
fn set_element_hash(value: &Value, vm: &mut VM<'_, impl ResourceTracker>) -> RunResult<u64> {
match value.py_hash(vm)? {
Some(h) => Ok(h.raw()),
None => Err(ExcType::type_error_unhashable_set_element(&value.py_type_name(vm))),
}
}