use std::{
collections::hash_map::DefaultHasher,
fmt::Write,
hash::{Hash, Hasher},
mem, slice, vec,
};
use hashbrown::HashTable;
use serde::ser::SerializeStruct;
use smallvec::{SmallVec, smallvec};
use super::{DictItemsView, DictKeysView, DictValuesView, LazyHeapSet, MontyIter, PyTrait, allocate_tuple};
use crate::{
args::{ArgValues, FromArgs, KwargsValues},
bytecode::{CallResult, ContainsVM, DropWithVM, RecursionToken, VM},
defer_drop, defer_drop_mut, defer_drop_vm_mut,
exception_private::{ExcType, RunResult},
heap::{ContainsHeap, DropWithHeap, Heap, HeapData, HeapGuard, HeapId, HeapItem, HeapRead, HeapReadOutput},
intern::{Interns, StaticStrings},
resource::ResourceTracker,
types::Type,
value::{EitherStr, VALUE_SIZE, Value},
};
#[derive(Debug, Default)]
pub(crate) struct Dict {
indices: HashTable<usize>,
entries: Vec<DictEntry>,
contains_refs: bool,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct DictEntry {
key: Value,
value: Value,
hash: u64,
}
impl Dict {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
indices: HashTable::with_capacity(capacity),
entries: Vec::with_capacity(capacity),
contains_refs: false,
}
}
#[inline]
#[must_use]
pub fn has_refs(&self) -> bool {
self.contains_refs
}
pub fn from_pairs(pairs: Vec<(Value, Value)>, vm: &mut VM<'_, impl ResourceTracker>) -> RunResult<Self> {
let pairs_iter = pairs.into_iter();
defer_drop_mut!(pairs_iter, vm);
let dict = Self::with_capacity(pairs_iter.len());
let mut dict_guard = HeapGuard::new(dict, vm);
let (dict, vm) = dict_guard.as_parts_mut();
for (key, value) in pairs_iter {
if let Some(old_value) = dict.set(key, value, vm)? {
old_value.drop_with_heap(vm);
}
}
Ok(dict_guard.into_inner())
}
pub fn set_json_string_key(
&mut self,
key: Value,
value: Value,
vm: &mut VM<'_, impl ResourceTracker>,
) -> RunResult<Option<Value>> {
debug_assert!(json_key_string_slice(&key, vm.heap, vm.interns).is_some());
if matches!(key, Value::Ref(_)) || matches!(value, Value::Ref(_)) {
self.contains_refs = true;
}
let hash = key
.py_hash(vm)?
.expect("json object keys are always hashable strings")
.raw();
let opt_index = self.find_json_string_key_index(hash, &key, vm.heap, vm.interns);
let entry = DictEntry { key, value, hash };
if let Some(index) = opt_index {
let old_entry = mem::replace(&mut self.entries[index], entry);
old_entry.key.drop_with_heap(vm);
Ok(Some(old_entry.value))
} else {
vm.heap.track_growth(2 * VALUE_SIZE)?;
let index = self.entries.len();
self.entries.push(entry);
self.indices.insert_unique(hash, index, |&i| self.entries[i].hash);
Ok(None)
}
}
fn find_json_string_key_index(
&self,
hash: u64,
key: &Value,
heap: &Heap<impl ResourceTracker>,
interns: &Interns,
) -> Option<usize> {
let key_str = json_key_string_slice(key, heap, interns).expect("json object keys are always string values");
self.indices
.find(hash, |&idx| {
let entry = &self.entries[idx];
entry.hash == hash && json_key_equals_str(&entry.key, key_str, heap, interns)
})
.copied()
}
}
fn json_key_string_slice<'a>(
key: &'a Value,
heap: &'a Heap<impl ResourceTracker>,
interns: &'a Interns,
) -> Option<&'a str> {
match key {
Value::InternString(id) => Some(interns.get_str(*id)),
Value::Ref(id) => match heap.get(*id) {
HeapData::Str(string) => Some(string.as_str()),
_ => None,
},
_ => None,
}
}
fn json_key_equals_str(key: &Value, expected: &str, heap: &Heap<impl ResourceTracker>, interns: &Interns) -> bool {
match key {
Value::InternString(id) => interns.get_str(*id) == expected,
Value::Ref(id) => match heap.get(*id) {
HeapData::Str(string) => string.as_str() == expected,
_ => false,
},
_ => false,
}
}
impl<'h> HeapRead<'h, Dict> {
pub(crate) fn eq_dict(&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((key, value)) = iter.next(vm)? {
let Some(other_value) = other.dict_get(key, vm)? else {
return Ok(false);
};
defer_drop!(other_value, vm);
if !value.py_eq(other_value, vm)? {
return Ok(false);
}
}
Ok(true)
}
pub(crate) fn dict_get<'a>(
&'a self,
key: &Value,
vm: &'a mut VM<'h, impl ResourceTracker>,
) -> RunResult<Option<Value>> {
let (opt_index, _hash) = self.find_index_hash(key, vm)?;
if let Some(index) = opt_index {
Ok(Some(self.get(vm.heap).entries[index].value.clone_with_heap(vm.heap)))
} else {
Ok(None)
}
}
}
impl Dict {
pub fn get_by_str(&self, key_str: &str, heap: &Heap<impl ResourceTracker>, interns: &Interns) -> Option<&Value> {
let mut hasher = DefaultHasher::new();
key_str.hash(&mut hasher);
let hash = hasher.finish();
self.indices
.find(hash, |&idx| {
let entry_key = &self.entries[idx].key;
match entry_key {
Value::InternString(id) => interns.get_str(*id) == key_str,
Value::Ref(id) => {
if let HeapData::Str(s) = heap.get(*id) {
s.as_str() == key_str
} else {
false
}
}
_ => false,
}
})
.map(|&idx| &self.entries[idx].value)
}
pub fn set(&mut self, key: Value, value: Value, vm: &mut VM<'_, impl ResourceTracker>) -> RunResult<Option<Value>> {
vm.heap.protect_mut(self).set(key, value, vm)
}
}
impl<'h> HeapRead<'h, Dict> {
pub fn set(&mut self, key: Value, value: Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Option<Value>> {
if matches!(key, Value::Ref(_)) || matches!(value, Value::Ref(_)) {
self.get_mut(vm.heap).contains_refs = true;
}
let (opt_index, hash) = match self.find_index_hash(&key, vm) {
Ok(result) => result,
Err(e) => {
key.drop_with_heap(vm);
value.drop_with_heap(vm);
return Err(e);
}
};
let entry = DictEntry { key, value, hash };
if let Some(index) = opt_index {
let old_entry = mem::replace(&mut self.get_mut(vm.heap).entries[index], entry);
old_entry.key.drop_with_heap(vm);
Ok(Some(old_entry.value))
} else {
vm.heap.track_growth(2 * VALUE_SIZE)?;
let this = self.get_mut(vm.heap);
let index = this.entries.len();
this.entries.push(entry);
this.indices
.insert_unique(hash, index, |index| this.entries[*index].hash);
Ok(None)
}
}
pub fn pop(&mut self, key: &Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Option<(Value, Value)>> {
let (opt_index, _hash) = self.find_index_hash(key, vm)?;
if let Some(index) = opt_index {
let entry = self.get_mut(vm.heap).entries.remove(index);
let this = self.get_mut(vm.heap);
this.indices.clear();
for (idx, e) in this.entries.iter().enumerate() {
this.indices.insert_unique(e.hash, idx, |&i| this.entries[i].hash);
}
Ok(Some((entry.key, entry.value)))
} else {
Ok(None)
}
}
}
impl Dict {
#[must_use]
pub fn len(&self) -> usize {
self.entries.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn iter(&self) -> DictEntriesIter<'_> {
self.into_iter()
}
pub fn key_at(&self, index: usize) -> Option<&Value> {
self.entries.get(index).map(|e| &e.key)
}
pub fn value_at(&self, index: usize) -> Option<&Value> {
self.entries.get(index).map(|e| &e.value)
}
pub fn item_at(&self, index: usize) -> Option<(&Value, &Value)> {
self.entries.get(index).map(|entry| (&entry.key, &entry.value))
}
pub fn init(vm: &mut VM<'_, impl ResourceTracker>, args: ArgValues) -> RunResult<Value> {
let DictInitArgs { source, extras } = DictInitArgs::from_args(args, vm)?;
let dict = Self::new();
let mut dict_guard = HeapGuard::new(dict, vm);
{
let (dict, vm) = dict_guard.as_parts_mut();
let mut kwargs_guard = HeapGuard::new(extras, vm);
if let Some(other_value) = source {
let other_value_guard = HeapGuard::new(other_value, kwargs_guard.heap());
let other_value = other_value_guard.into_inner();
dict_merge_from_value(dict, other_value, kwargs_guard.heap())?;
}
let kwargs = kwargs_guard.into_inner();
dict_merge_from_kwargs(dict, kwargs, vm)?;
}
let dict = dict_guard.into_inner();
let heap_id = vm.heap.allocate(HeapData::Dict(dict))?;
Ok(Value::Ref(heap_id))
}
}
#[derive(FromArgs)]
#[from_args(name = "dict")]
struct DictInitArgs {
#[from_args(pos_only, default)]
source: Option<Value>,
#[from_args(varkwargs)]
extras: KwargsValues,
}
impl<'h> HeapRead<'h, Dict> {
fn find_index_hash(&self, key: &Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<(Option<usize>, u64)> {
let hash = key
.py_hash(vm)?
.ok_or_else(|| ExcType::type_error_unhashable_dict_key(&key.py_type_name(vm)))?
.raw();
let mut candidates: SmallVec<[usize; 2]> = SmallVec::new();
let this = self.get(vm.heap);
this.indices.find(hash, |v| {
if this.entries[*v].hash == hash {
candidates.push(*v);
}
false
});
for candidate_index in candidates {
let candidate_key = self.get(vm.heap).entries[candidate_index].key.clone_with_heap(vm);
defer_drop!(candidate_key, vm);
if key.py_eq(candidate_key, vm)? {
return Ok((Some(candidate_index), hash));
}
}
Ok((None, hash))
}
pub(crate) fn contains_key(&self, key: &Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<bool> {
let (opt_index, _hash) = self.find_index_hash(key, vm)?;
Ok(opt_index.is_some())
}
#[expect(clippy::iter_not_returning_iterator)]
pub(crate) fn iter<R: ResourceTracker>(&self, vm: &mut VM<'h, R>) -> RunResult<DictIter<'_, 'h>> {
DictIter::new(self, vm)
}
fn merge_from_value(&mut self, other_value: Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<()> {
let mut guard = HeapGuard::new(other_value, vm);
let (other_value, vm) = guard.as_parts_mut();
if let Value::Ref(id) = other_value {
let src_id = *id;
if let HeapReadOutput::Dict(src) = vm.heap.read(src_id) {
let iter = src.iter(vm)?;
defer_drop_vm_mut!(iter, vm);
while let Some((key, value)) = iter.next_owned(vm)? {
let old_value = self.set(key, value, vm)?;
old_value.drop_with_heap(vm);
}
return Ok(());
}
}
let (other_value, vm) = guard.into_parts();
self.merge_from_iterable_pairs(other_value, vm)
}
fn merge_from_iterable_pairs(&mut self, iterable: Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<()> {
let iter = MontyIter::new(iterable, vm)?;
defer_drop_mut!(iter, vm);
while let Some(item) = iter.for_next(vm)? {
let pair_iter = MontyIter::new(item, vm)?;
defer_drop_mut!(pair_iter, vm);
let Some(key) = pair_iter.for_next(vm)? else {
return Err(ExcType::type_error(
"dictionary update sequence element has length 0; 2 is required",
));
};
let mut key_guard = HeapGuard::new(key, vm);
let Some(value) = pair_iter.for_next(key_guard.heap())? else {
return Err(ExcType::type_error(
"dictionary update sequence element has length 1; 2 is required",
));
};
let mut value_guard = HeapGuard::new(value, key_guard.heap());
if let Some(extra) = pair_iter.for_next(value_guard.heap())? {
extra.drop_with_heap(value_guard.heap());
return Err(ExcType::type_error(
"dictionary update sequence element has length > 2; 2 is required",
));
}
let value = value_guard.into_inner();
let key = key_guard.into_inner();
if let Some(old_value) = self.set(key, value, vm)? {
old_value.drop_with_heap(vm);
}
}
Ok(())
}
fn merge_from_kwargs(&mut self, kwargs: KwargsValues, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<()> {
let kwargs_iter = kwargs.into_iter();
defer_drop_mut!(kwargs_iter, vm);
for (key, value) in kwargs_iter {
let old_value = self.set(key, value, vm)?;
old_value.drop_with_heap(vm);
}
Ok(())
}
}
pub(crate) struct DictEntriesIter<'a>(slice::Iter<'a, DictEntry>);
impl<'a> Iterator for DictEntriesIter<'a> {
type Item = (&'a Value, &'a Value);
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|e| (&e.key, &e.value))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
fn fold<B, F>(self, init: B, mut f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
self.0.fold(init, |acc, e| f(acc, (&e.key, &e.value)))
}
}
impl<'a> IntoIterator for &'a Dict {
type Item = (&'a Value, &'a Value);
type IntoIter = DictEntriesIter<'a>;
fn into_iter(self) -> Self::IntoIter {
DictEntriesIter(self.entries.iter())
}
}
pub(crate) struct DictIntoIter(vec::IntoIter<DictEntry>);
impl Iterator for DictIntoIter {
type Item = (Value, Value);
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|e| (e.key, e.value))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl ExactSizeIterator for DictIntoIter {}
impl IntoIterator for Dict {
type Item = (Value, Value);
type IntoIter = DictIntoIter;
fn into_iter(self) -> Self::IntoIter {
DictIntoIter(self.entries.into_iter())
}
}
pub(crate) struct DictIter<'a, 'h> {
dict: &'a HeapRead<'h, Dict>,
index: usize,
expected_len: usize,
token: RecursionToken,
current_key: Value,
current_value: Value,
}
impl<'a, 'h> DictIter<'a, 'h> {
fn new<R: ResourceTracker>(dict: &'a HeapRead<'h, Dict>, vm: &mut VM<'h, R>) -> RunResult<Self> {
let expected_len = dict.get(vm.heap).entries.len();
let token = vm.recursion_token()?;
Ok(Self {
dict,
index: 0,
expected_len,
token,
current_key: Value::Undefined,
current_value: Value::Undefined,
})
}
pub(crate) fn next<'i, R: ResourceTracker>(
&'i mut self,
vm: &mut VM<'h, R>,
) -> RunResult<Option<(&'i Value, &'i Value)>> {
let Some(entry_index) = self.advance(vm)? else {
return Ok(None);
};
let entry = &self.dict.get(vm.heap).entries[entry_index];
self.current_key = entry.key.clone_with_heap(vm.heap);
self.current_value = entry.value.clone_with_heap(vm.heap);
Ok(Some((&self.current_key, &self.current_value)))
}
pub(crate) fn next_owned<R: ResourceTracker>(&mut self, vm: &mut VM<'h, R>) -> RunResult<Option<(Value, Value)>> {
let Some(entry_index) = self.advance(vm)? else {
return Ok(None);
};
let entry = &self.dict.get(vm.heap).entries[entry_index];
let pair = (entry.key.clone_with_heap(vm.heap), entry.value.clone_with_heap(vm.heap));
Ok(Some(pair))
}
fn advance<R: ResourceTracker>(&mut self, vm: &mut VM<'h, R>) -> RunResult<Option<usize>> {
mem::replace(&mut self.current_key, Value::Undefined).drop_with_heap(vm.heap);
mem::replace(&mut self.current_value, Value::Undefined).drop_with_heap(vm.heap);
vm.heap.check_time()?;
let current = self.dict.get(vm.heap);
if current.entries.len() != self.expected_len {
return Err(ExcType::runtime_error_dict_changed_size());
}
if self.index >= self.expected_len {
return Ok(None);
}
let entry_index = self.index;
self.index += 1;
Ok(Some(entry_index))
}
}
impl<'h> DropWithVM<'h> for DictIter<'_, 'h> {
fn drop_with_vm(self, container: &mut impl ContainsVM<'h>) {
self.current_key.drop_with_heap(container);
self.current_value.drop_with_heap(container);
self.token.drop_with_vm(container);
}
}
impl<'h> PyTrait<'h> for HeapRead<'h, Dict> {
fn py_type(&self, _vm: &VM<'h, impl ResourceTracker>) -> Type {
Type::Dict
}
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::Dict(other)) => Ok(Some(self.eq_dict(&other, 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<()> {
if self.get(vm.heap).is_empty() {
return Ok(f.write_str("{}")?);
}
let Ok(mut guard) = vm.recursion_guard() else {
return Ok(f.write_str("{...}")?);
};
let vm = &mut *guard;
f.write_char('{')?;
let len = self.get(vm.heap).len();
for i in 0..len {
if i > 0 {
if vm.heap.check_time().is_err() {
f.write_str(", ...[timeout]")?;
break;
}
f.write_str(", ")?;
}
let key = self
.get(vm.heap)
.key_at(i)
.expect("index in range")
.clone_with_heap(vm.heap);
defer_drop!(key, vm);
key.py_repr_fmt(f, vm, heap_ids)?;
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('}')?;
Ok(())
}
fn py_getitem(&self, key: &Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Value> {
match self.dict_get(key, vm)? {
Some(value) => Ok(value),
None => Err(ExcType::key_error(key, vm)),
}
}
fn py_setitem(&mut self, key: Value, value: Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<()> {
if let Some(old_value) = self.set(key, value, vm)? {
old_value.drop_with_heap(vm);
}
Ok(())
}
fn py_call_attr(
&mut self,
self_id: HeapId,
vm: &mut VM<'h, impl ResourceTracker>,
attr: &EitherStr,
args: ArgValues,
) -> RunResult<CallResult> {
let Some(method) = attr.static_string() else {
args.drop_with_heap(vm);
return Err(ExcType::attribute_error(Type::Dict, attr.as_str(vm.interns)));
};
let value = match method {
StaticStrings::Get => {
let (key, default) = args.get_one_two_args("get", vm.heap)?;
defer_drop!(key, vm);
let default = default.unwrap_or(Value::None);
let mut default_guard = HeapGuard::new(default, vm);
let vm = default_guard.heap();
match self.dict_get(key, vm)? {
Some(v) => Ok(v),
None => Ok(default_guard.into_inner()),
}
}
StaticStrings::Keys => {
args.check_zero_args("dict.keys", vm.heap)?;
let view_id = vm.heap.allocate(HeapData::DictKeysView(DictKeysView::new(self_id)))?;
vm.heap.inc_ref(self_id);
Ok(Value::Ref(view_id))
}
StaticStrings::Values => {
args.check_zero_args("dict.values", vm.heap)?;
let view_id = vm
.heap
.allocate(HeapData::DictValuesView(DictValuesView::new(self_id)))?;
vm.heap.inc_ref(self_id);
Ok(Value::Ref(view_id))
}
StaticStrings::Items => {
args.check_zero_args("dict.items", vm.heap)?;
let view_id = vm.heap.allocate(HeapData::DictItemsView(DictItemsView::new(self_id)))?;
vm.heap.inc_ref(self_id);
Ok(Value::Ref(view_id))
}
StaticStrings::Pop => {
let (key, default) = args.get_one_two_args("pop", vm.heap)?;
defer_drop!(key, vm);
let mut default_guard = HeapGuard::new(default, vm);
let vm = default_guard.heap();
if let Some((old_key, value)) = self.pop(key, vm)? {
old_key.drop_with_heap(vm);
Ok(value)
} else {
let (default, vm) = default_guard.into_parts();
if let Some(d) = default {
Ok(d)
} else {
Err(ExcType::key_error(key, vm))
}
}
}
StaticStrings::Clear => {
args.check_zero_args("dict.clear", vm.heap)?;
dict_clear(self, vm);
Ok(Value::None)
}
StaticStrings::Copy => {
args.check_zero_args("dict.copy", vm.heap)?;
dict_copy(self, vm)
}
StaticStrings::Update => dict_update(self, args, vm),
StaticStrings::Setdefault => dict_setdefault(self, args, vm),
StaticStrings::Popitem => {
args.check_zero_args("dict.popitem", vm.heap)?;
dict_popitem(self, vm)
}
StaticStrings::Fromkeys => dict_fromkeys(args, vm),
_ => {
args.drop_with_heap(vm);
return Err(ExcType::attribute_error(Type::Dict, attr.as_str(vm.interns)));
}
};
value.map(CallResult::Value)
}
}
impl HeapItem for Dict {
fn py_estimate_size(&self) -> usize {
mem::size_of::<Self>() + self.len() * 2 * VALUE_SIZE
}
fn py_dec_ref_ids(&mut self, stack: &mut Vec<HeapId>) {
if !self.contains_refs {
return;
}
for entry in &mut self.entries {
if let Value::Ref(id) = &entry.key {
stack.push(*id);
#[cfg(feature = "memory-model-checks")]
entry.key.dec_ref_forget();
}
if let Value::Ref(id) = &entry.value {
stack.push(*id);
#[cfg(feature = "memory-model-checks")]
entry.value.dec_ref_forget();
}
}
}
}
impl DropWithHeap for Dict {
fn drop_with_heap<H: ContainsHeap>(self, heap: &mut H) {
self.entries.drop_with_heap(heap);
}
}
impl DropWithHeap for DictEntry {
fn drop_with_heap<H: ContainsHeap>(self, heap: &mut H) {
self.key.drop_with_heap(heap);
self.value.drop_with_heap(heap);
}
}
fn dict_clear<'h>(dict: &mut HeapRead<'h, Dict>, vm: &mut VM<'h, impl ResourceTracker>) {
dict.get_mut(vm.heap).indices.clear();
mem::take(&mut dict.get_mut(vm.heap).entries).drop_with_heap(vm.heap);
}
fn dict_copy<'h>(dict: &mut HeapRead<'h, Dict>, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Value> {
let pairs: Vec<(Value, Value)> = dict
.get(vm.heap)
.iter()
.map(|(k, v)| (k.clone_with_heap(vm), v.clone_with_heap(vm)))
.collect();
let new_dict = Dict::from_pairs(pairs, vm)?;
let heap_id = vm.heap.allocate(HeapData::Dict(new_dict))?;
Ok(Value::Ref(heap_id))
}
fn dict_update<'h>(
dict: &mut HeapRead<'h, Dict>,
args: ArgValues,
vm: &mut VM<'h, impl ResourceTracker>,
) -> RunResult<Value> {
let DictUpdateArgs { source, extras } = DictUpdateArgs::from_args(args, vm)?;
let mut kwargs_guard = HeapGuard::new(extras, vm);
if let Some(other_value) = source {
let other_value_guard = HeapGuard::new(other_value, kwargs_guard.heap());
let other_value = other_value_guard.into_inner();
dict.merge_from_value(other_value, kwargs_guard.heap())?;
}
let kwargs = kwargs_guard.into_inner();
dict.merge_from_kwargs(kwargs, vm)?;
Ok(Value::None)
}
#[derive(FromArgs)]
#[from_args(name = "update")]
struct DictUpdateArgs {
#[from_args(pos_only, default)]
source: Option<Value>,
#[from_args(varkwargs)]
extras: KwargsValues,
}
fn dict_merge_from_value(dict: &mut Dict, other_value: Value, vm: &mut VM<'_, impl ResourceTracker>) -> RunResult<()> {
let mut other_value_guard = HeapGuard::new(other_value, vm);
{
let (other_value, vm) = other_value_guard.as_parts();
if let Value::Ref(id) = other_value
&& let HeapData::Dict(src_dict) = vm.heap.get(*id)
{
let pairs: Vec<(Value, Value)> = src_dict
.iter()
.map(|(k, v)| (k.clone_with_heap(vm), v.clone_with_heap(vm)))
.collect();
for (key, value) in pairs {
let old_value = dict.set(key, value, vm)?;
old_value.drop_with_heap(vm);
}
return Ok(());
}
}
let other_value = other_value_guard.into_inner();
dict_merge_from_iterable_pairs(dict, other_value, vm)
}
fn dict_merge_from_iterable_pairs(
dict: &mut Dict,
iterable: Value,
vm: &mut VM<'_, impl ResourceTracker>,
) -> RunResult<()> {
let iter = MontyIter::new(iterable, vm)?;
defer_drop_mut!(iter, vm);
while let Some(item) = iter.for_next(vm)? {
let pair_iter = MontyIter::new(item, vm)?;
defer_drop_mut!(pair_iter, vm);
let Some(key) = pair_iter.for_next(vm)? else {
return Err(ExcType::type_error(
"dictionary update sequence element has length 0; 2 is required",
));
};
let mut key_guard = HeapGuard::new(key, vm);
let Some(value) = pair_iter.for_next(key_guard.heap())? else {
return Err(ExcType::type_error(
"dictionary update sequence element has length 1; 2 is required",
));
};
let mut value_guard = HeapGuard::new(value, key_guard.heap());
if let Some(extra) = pair_iter.for_next(value_guard.heap())? {
extra.drop_with_heap(value_guard.heap());
return Err(ExcType::type_error(
"dictionary update sequence element has length > 2; 2 is required",
));
}
let value = value_guard.into_inner();
let key = key_guard.into_inner();
if let Some(old_value) = dict.set(key, value, vm)? {
old_value.drop_with_heap(vm);
}
}
Ok(())
}
fn dict_merge_from_kwargs(
dict: &mut Dict,
kwargs: KwargsValues,
vm: &mut VM<'_, impl ResourceTracker>,
) -> RunResult<()> {
let kwargs_iter = kwargs.into_iter();
defer_drop_mut!(kwargs_iter, vm);
for (key, value) in kwargs_iter {
let old_value = dict.set(key, value, vm)?;
old_value.drop_with_heap(vm);
}
Ok(())
}
fn dict_setdefault<'h>(
dict: &mut HeapRead<'h, Dict>,
args: ArgValues,
vm: &mut VM<'h, impl ResourceTracker>,
) -> RunResult<Value> {
let (key, default) = args.get_one_two_args("setdefault", vm.heap)?;
let default = default.unwrap_or(Value::None);
let mut key_guard = HeapGuard::new(key, vm);
let (key, vm) = key_guard.as_parts();
if let Some(existing) = dict.dict_get(key, vm)? {
default.drop_with_heap(vm);
Ok(existing)
} else {
let return_value = default.clone_with_heap(vm);
let (key, vm) = key_guard.into_parts();
if let Some(old_value) = dict.set(key, default, vm)? {
old_value.drop_with_heap(vm);
}
Ok(return_value)
}
}
fn dict_popitem<'h>(dict: &mut HeapRead<'h, Dict>, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Value> {
let this = dict.get_mut(vm.heap);
if this.is_empty() {
return Err(ExcType::key_error_popitem_empty_dict());
}
let entry = this.entries.pop().expect("dict is not empty");
this.indices.clear();
for (idx, e) in this.entries.iter().enumerate() {
this.indices.insert_unique(e.hash, idx, |&i| this.entries[i].hash);
}
Ok(allocate_tuple(smallvec![entry.key, entry.value], vm.heap)?)
}
impl serde::Serialize for Dict {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut state = serializer.serialize_struct("Dict", 2)?;
state.serialize_field("entries", &self.entries)?;
state.serialize_field("contains_refs", &self.contains_refs)?;
state.end()
}
}
impl<'de> serde::Deserialize<'de> for Dict {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
#[derive(serde::Deserialize)]
struct DictFields {
entries: Vec<DictEntry>,
contains_refs: bool,
}
let fields = DictFields::deserialize(deserializer)?;
let mut indices = HashTable::with_capacity(fields.entries.len());
for (idx, entry) in fields.entries.iter().enumerate() {
indices.insert_unique(entry.hash, idx, |&i| fields.entries[i].hash);
}
Ok(Self {
indices,
entries: fields.entries,
contains_refs: fields.contains_refs,
})
}
}
pub fn dict_fromkeys(args: ArgValues, vm: &mut VM<'_, impl ResourceTracker>) -> RunResult<Value> {
let (iterable, default) = args.get_one_two_args("dict.fromkeys", vm.heap)?;
let default = default.unwrap_or(Value::None);
defer_drop!(default, vm);
let iter = MontyIter::new(iterable, vm)?;
defer_drop_mut!(iter, vm);
let dict = Dict::new();
let mut dict_guard = HeapGuard::new(dict, vm);
{
let (dict, vm) = dict_guard.as_parts_mut();
while let Some(key) = iter.for_next(vm)? {
let old_value = dict.set(key, default.clone_with_heap(vm), vm)?;
old_value.drop_with_heap(vm);
}
}
let dict = dict_guard.into_inner();
let heap_id = vm.heap.allocate(HeapData::Dict(dict))?;
Ok(Value::Ref(heap_id))
}