use std::cmp::Ordering;
use std::collections::{BTreeMap, HashMap};
use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::iter::FromIterator;
use std::mem;
use std::ops::{Index, IndexMut};
#[cfg(feature = "indexmap")]
use indexmap::IndexMap;
use crate::string::IString;
use crate::thin::{ThinMut, ThinMutExt, ThinRef};
use crate::value::object::{Header, HeaderMut, HeaderRef, KeyValuePair, ObjectRepr};
use crate::value::IValue;
unsafe fn build_entry<'a>(header: ThinMut<'a, Header>, key: IString) -> Entry<'a> {
match header.split().find_bucket(&key) {
Err(bucket) => Entry::Vacant(VacantEntry {
header,
bucket,
key,
}),
Ok(bucket) => Entry::Occupied(OccupiedEntry { header, bucket }),
}
}
unsafe fn build_entry_or_clone<'a>(header: ThinMut<'a, Header>, key: &IString) -> Entry<'a> {
match header.split().find_bucket(key) {
Err(bucket) => Entry::Vacant(VacantEntry {
header,
bucket,
key: key.clone(),
}),
Ok(bucket) => Entry::Occupied(OccupiedEntry { header, bucket }),
}
}
pub struct OccupiedEntry<'a> {
header: ThinMut<'a, Header>,
bucket: usize,
}
impl Debug for OccupiedEntry<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("OccupiedEntry")
.field("key", self.key())
.field("value", &self.get())
.finish()
}
}
impl<'a> OccupiedEntry<'a> {
fn get_key_value(&self) -> (&IString, &IValue) {
let split = self.header.split();
unsafe {
let index = *split.table.get_unchecked(self.bucket);
let kvp = split.items.get_unchecked(index);
(&kvp.key, &kvp.value)
}
}
fn get_key_value_mut(&mut self) -> (&IString, &mut IValue) {
let split = self.header.reborrow().split_mut();
unsafe {
let index = *split.table.get_unchecked(self.bucket);
let kvp = split.items.get_unchecked_mut(index);
(&kvp.key, &mut kvp.value)
}
}
fn into_get_key_value_mut(self) -> (&'a IString, &'a mut IValue) {
let split = self.header.split_mut();
unsafe {
let index = *split.table.get_unchecked(self.bucket);
let kvp = split.items.get_unchecked_mut(index);
(&kvp.key, &mut kvp.value)
}
}
#[must_use]
pub fn key(&self) -> &IString {
self.get_key_value().0
}
pub fn remove_entry(mut self) -> (IString, IValue) {
unsafe {
self.header
.reborrow()
.split_mut()
.remove_bucket(self.bucket);
self.header.pop()
}
}
#[must_use]
pub fn get(&self) -> &IValue {
self.get_key_value().1
}
pub fn get_mut(&mut self) -> &mut IValue {
self.get_key_value_mut().1
}
#[must_use]
pub fn into_mut(self) -> &'a mut IValue {
self.into_get_key_value_mut().1
}
pub fn insert(&mut self, value: impl Into<IValue>) -> IValue {
mem::replace(self.get_mut(), value.into())
}
pub fn remove(self) -> IValue {
self.remove_entry().1
}
}
pub struct VacantEntry<'a> {
header: ThinMut<'a, Header>,
bucket: usize,
key: IString,
}
impl Debug for VacantEntry<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("VacantEntry")
.field("key", self.key())
.finish()
}
}
impl<'a> VacantEntry<'a> {
#[must_use]
pub fn key(&self) -> &IString {
&self.key
}
#[must_use]
pub fn into_key(self) -> IString {
self.key
}
pub fn insert(mut self, value: impl Into<IValue>) -> &'a mut IValue {
unsafe {
let index = self.header.push(self.key, value.into());
let mut split = self.header.split_mut();
split.shift(self.bucket, index);
&mut split.items.last_mut().unwrap().value
}
}
}
#[derive(Debug)]
pub enum Entry<'a> {
Occupied(OccupiedEntry<'a>),
Vacant(VacantEntry<'a>),
}
impl<'a> Entry<'a> {
pub fn or_insert(self, default: IValue) -> &'a mut IValue {
match self {
Entry::Occupied(occ) => occ.into_mut(),
Entry::Vacant(vac) => vac.insert(default),
}
}
pub fn or_insert_with(self, default: impl FnOnce() -> IValue) -> &'a mut IValue {
match self {
Entry::Occupied(occ) => occ.into_mut(),
Entry::Vacant(vac) => vac.insert(default()),
}
}
#[must_use]
pub fn key(&self) -> &IString {
match self {
Entry::Occupied(occ) => occ.key(),
Entry::Vacant(vac) => vac.key(),
}
}
pub fn and_modify(mut self, f: impl FnOnce(&mut IValue)) -> Self {
if let Entry::Occupied(occ) = &mut self {
f(occ.get_mut());
}
self
}
}
pub struct IntoIter {
reversed_object: IObject,
}
impl Debug for IntoIter {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("IntoIter")
.field("reversed_object", &self.reversed_object)
.finish()
}
}
impl Iterator for IntoIter {
type Item = (IString, IValue);
fn next(&mut self) -> Option<Self::Item> {
if self.reversed_object.is_empty() {
None
} else {
Some(unsafe {
self.reversed_object.header_mut().pop()
})
}
}
}
impl ExactSizeIterator for IntoIter {
fn len(&self) -> usize {
self.reversed_object.len()
}
}
#[repr(transparent)]
#[derive(Clone)]
pub struct IObject(pub(crate) IValue);
value_subtype_impls!(IObject, into_object, as_object, as_object_mut);
impl IObject {
#[must_use]
pub fn new() -> Self {
IObject(ObjectRepr::empty())
}
#[must_use]
pub fn with_capacity(cap: usize) -> Self {
IObject(ObjectRepr::with_capacity(cap))
}
unsafe fn header(&self) -> ThinRef<'_, Header> {
ObjectRepr::header(&self.0)
}
unsafe fn header_mut(&mut self) -> ThinMut<'_, Header> {
ObjectRepr::header_mut(&mut self.0)
}
#[must_use]
pub fn capacity(&self) -> usize {
unsafe { ObjectRepr::capacity(&self.0) }
}
#[must_use]
pub fn len(&self) -> usize {
unsafe { ObjectRepr::len(&self.0) }
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn reserve(&mut self, additional: usize) {
unsafe { ObjectRepr::reserve(&mut self.0, additional) }
}
pub fn entry(&mut self, key: impl Into<IString>) -> Entry<'_> {
self.reserve(1);
unsafe { build_entry(self.header_mut(), key.into()) }
}
pub fn entry_or_clone(&mut self, key: &IString) -> Entry<'_> {
self.reserve(1);
unsafe { build_entry_or_clone(self.header_mut(), key) }
}
pub fn keys(&self) -> impl Iterator<Item = &IString> {
self.iter().map(|x| x.0)
}
pub fn values(&self) -> impl Iterator<Item = &IValue> {
self.iter().map(|x| x.1)
}
#[must_use]
pub fn iter(&self) -> Iter<'_> {
Iter(unsafe { ObjectRepr::items(&self.0) }.iter())
}
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut IValue> {
self.iter_mut().map(|x| x.1)
}
pub fn iter_mut(&mut self) -> IterMut<'_> {
IterMut(
if self.is_empty() {
&mut []
} else {
unsafe { self.header_mut().split_mut().items }
}
.iter_mut(),
)
}
pub fn clear(&mut self) {
if !self.is_empty() {
unsafe {
self.header_mut().clear();
}
}
}
pub fn get_key_value(&self, k: impl ObjectIndex) -> Option<(&IString, &IValue)> {
k.index_into(self)
}
pub fn get_key_value_mut(&mut self, k: impl ObjectIndex) -> Option<(&IString, &mut IValue)> {
k.index_into_mut(self)
}
pub fn get(&self, k: impl ObjectIndex) -> Option<&IValue> {
self.get_key_value(k).map(|x| x.1)
}
pub fn get_mut(&mut self, k: impl ObjectIndex) -> Option<&mut IValue> {
self.get_key_value_mut(k).map(|x| x.1)
}
pub fn contains_key(&self, k: impl ObjectIndex) -> bool {
self.get(k).is_some()
}
pub fn insert(&mut self, k: impl Into<IString>, v: impl Into<IValue>) -> Option<IValue> {
match self.entry(k) {
Entry::Occupied(mut occ) => Some(occ.insert(v)),
Entry::Vacant(vac) => {
vac.insert(v);
None
}
}
}
pub fn remove_entry(&mut self, k: impl ObjectIndex) -> Option<(IString, IValue)> {
k.remove(self)
}
pub fn remove(&mut self, k: impl ObjectIndex) -> Option<IValue> {
self.remove_entry(k).map(|x| x.1)
}
pub fn shrink_to_fit(&mut self) {
unsafe { ObjectRepr::shrink_to_fit(&mut self.0) }
}
pub fn retain(&mut self, mut f: impl FnMut(&IString, &mut IValue) -> bool) {
if !self.is_empty() {
let mut hd = unsafe { self.header_mut() };
let mut index = 0;
while index < hd.len {
let mut split = hd.reborrow().split_mut();
unsafe {
let kvp = split.items.get_unchecked_mut(index);
if f(&kvp.key, &mut kvp.value) {
index += 1;
} else {
let bucket = split.as_ref().find_bucket_from_index(index);
split.remove_bucket(bucket);
hd.pop();
}
}
}
}
}
}
impl IntoIterator for IObject {
type Item = (IString, IValue);
type IntoIter = IntoIter;
fn into_iter(mut self) -> Self::IntoIter {
if !self.is_empty() {
unsafe {
let split_header = self.header_mut().split_mut();
split_header.items.reverse();
}
}
IntoIter {
reversed_object: self,
}
}
}
impl PartialEq for IObject {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Eq for IObject {}
impl PartialOrd for IObject {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl Hash for IObject {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<K: Into<IString>, V: Into<IValue>> Extend<(K, V)> for IObject {
fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
let iter = iter.into_iter();
self.reserve(iter.size_hint().0);
for (k, v) in iter {
self.insert(k, v);
}
}
}
impl<K: Into<IString>, V: Into<IValue>> FromIterator<(K, V)> for IObject {
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
let mut res = IObject::new();
res.extend(iter);
res
}
}
impl<I: ObjectIndex> Index<I> for IObject {
type Output = IValue;
#[inline]
fn index(&self, index: I) -> &IValue {
index.index_into(self).unwrap().1
}
}
impl<I: ObjectIndex> IndexMut<I> for IObject {
#[inline]
fn index_mut(&mut self, index: I) -> &mut IValue {
index.index_or_insert(self)
}
}
mod private {
#[doc(hidden)]
pub trait Sealed {}
impl Sealed for usize {}
impl Sealed for &str {}
impl Sealed for &super::IString {}
impl<T: Sealed> Sealed for &T {}
}
pub trait ObjectIndex: private::Sealed + Copy {
#[doc(hidden)]
fn index_into(self, v: &IObject) -> Option<(&IString, &IValue)>;
#[doc(hidden)]
fn index_into_mut(self, v: &mut IObject) -> Option<(&IString, &mut IValue)>;
#[doc(hidden)]
fn index_or_insert(self, v: &mut IObject) -> &mut IValue;
#[doc(hidden)]
fn remove(self, v: &mut IObject) -> Option<(IString, IValue)>;
}
impl ObjectIndex for &str {
fn index_into(self, v: &IObject) -> Option<(&IString, &IValue)> {
IString::intern(self).index_into(v)
}
fn index_into_mut(self, v: &mut IObject) -> Option<(&IString, &mut IValue)> {
IString::intern(self).index_into_mut(v)
}
fn index_or_insert(self, v: &mut IObject) -> &mut IValue {
v.entry(IString::intern(self)).or_insert(IValue::NULL)
}
fn remove(self, v: &mut IObject) -> Option<(IString, IValue)> {
IString::intern(self).remove(v)
}
}
impl ObjectIndex for &IString {
fn index_into(self, v: &IObject) -> Option<(&IString, &IValue)> {
if v.is_empty() {
return None;
}
let hd = unsafe { v.header() }.split();
if let Ok(bucket) = hd.find_bucket(self) {
unsafe {
let index = *hd.table.get_unchecked(bucket);
let item = hd.items.get_unchecked(index);
Some((&item.key, &item.value))
}
} else {
None
}
}
fn index_into_mut(self, v: &mut IObject) -> Option<(&IString, &mut IValue)> {
if v.is_empty() {
None
} else {
let hd = unsafe { v.header_mut().split_mut() };
if let Ok(bucket) = hd.as_ref().find_bucket(self) {
unsafe {
let index = *hd.table.get_unchecked(bucket);
let item = hd.items.get_unchecked_mut(index);
Some((&item.key, &mut item.value))
}
} else {
None
}
}
}
fn index_or_insert(self, v: &mut IObject) -> &mut IValue {
v.entry_or_clone(self).or_insert(IValue::NULL)
}
fn remove(self, v: &mut IObject) -> Option<(IString, IValue)> {
if v.is_empty() {
None
} else {
let mut hd = unsafe { v.header_mut() };
let mut split = hd.reborrow().split_mut();
if let Ok(bucket) = split.as_ref().find_bucket(self) {
unsafe {
split.remove_bucket(bucket);
Some(hd.pop())
}
} else {
None
}
}
}
}
impl<T: ObjectIndex> ObjectIndex for &T {
fn index_into(self, v: &IObject) -> Option<(&IString, &IValue)> {
(*self).index_into(v)
}
fn index_into_mut(self, v: &mut IObject) -> Option<(&IString, &mut IValue)> {
(*self).index_into_mut(v)
}
fn index_or_insert(self, v: &mut IObject) -> &mut IValue {
(*self).index_or_insert(v)
}
fn remove(self, v: &mut IObject) -> Option<(IString, IValue)> {
(*self).remove(v)
}
}
impl Debug for IObject {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.0, f)
}
}
#[derive(Debug)]
pub struct Iter<'a>(std::slice::Iter<'a, KeyValuePair>);
impl<'a> Iterator for Iter<'a> {
type Item = (&'a IString, &'a IValue);
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|x| (&x.key, &x.value))
}
}
impl ExactSizeIterator for Iter<'_> {
fn len(&self) -> usize {
self.0.len()
}
}
#[derive(Debug)]
pub struct IterMut<'a>(std::slice::IterMut<'a, KeyValuePair>);
impl<'a> Iterator for IterMut<'a> {
type Item = (&'a IString, &'a mut IValue);
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|x| (&x.key, &mut x.value))
}
}
impl ExactSizeIterator for IterMut<'_> {
fn len(&self) -> usize {
self.0.len()
}
}
impl<'a> IntoIterator for &'a IObject {
type Item = (&'a IString, &'a IValue);
type IntoIter = Iter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> IntoIterator for &'a mut IObject {
type Item = (&'a IString, &'a mut IValue);
type IntoIter = IterMut<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
impl<K: Into<IString>, V: Into<IValue>> From<HashMap<K, V>> for IObject {
fn from(other: HashMap<K, V>) -> Self {
let mut res = Self::with_capacity(other.len());
res.extend(other.into_iter().map(|(k, v)| (k.into(), v.into())));
res
}
}
impl<K: Into<IString>, V: Into<IValue>> From<BTreeMap<K, V>> for IObject {
fn from(other: BTreeMap<K, V>) -> Self {
let mut res = Self::with_capacity(other.len());
res.extend(other.into_iter().map(|(k, v)| (k.into(), v.into())));
res
}
}
#[cfg(feature = "indexmap")]
impl<K: Into<IString>, V: Into<IValue>> From<IndexMap<K, V>> for IObject {
fn from(other: IndexMap<K, V>) -> Self {
let mut res = Self::with_capacity(other.len());
res.extend(other.into_iter().map(|(k, v)| (k.into(), v.into())));
res
}
}
impl From<serde_json::Map<String, serde_json::Value>> for IObject {
fn from(other: serde_json::Map<String, serde_json::Value>) -> Self {
let mut res = Self::with_capacity(other.len());
res.extend(other.into_iter().map(|(k, v)| (k, IValue::from(v))));
res
}
}
impl From<IObject> for serde_json::Map<String, serde_json::Value> {
fn from(other: IObject) -> Self {
other
.into_iter()
.map(|(k, v)| (k.as_str().to_owned(), serde_json::Value::from(v)))
.collect()
}
}
impl Default for IObject {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[mockalloc::test]
fn can_create() {
let x = IObject::new();
let y = IObject::with_capacity(10);
assert_eq!(x, y);
}
#[mockalloc::test]
fn equal_objects_order_equal_through_ivalue() {
let make = || {
let mut o = IObject::new();
o.insert("k", IValue::TRUE);
IValue::from(o)
};
let a = make();
let b = make();
assert_eq!(a, b);
assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal));
let mut arr_a = crate::array::IArray::new();
arr_a.push(make());
let mut arr_b = crate::array::IArray::new();
arr_b.push(make());
let (arr_a, arr_b) = (IValue::from(arr_a), IValue::from(arr_b));
assert_eq!(arr_a, arr_b);
assert_eq!(arr_a.partial_cmp(&arr_b), Some(Ordering::Equal));
}
#[mockalloc::test]
fn empty_object_is_unallocated() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
fn hash_of(o: &IObject) -> u64 {
let mut h = DefaultHasher::new();
o.hash(&mut h);
h.finish()
}
let mut x = IObject::new();
assert_eq!(x.len(), 0);
assert_eq!(x.capacity(), 0);
assert!(x.is_empty());
assert_eq!(x.get("missing"), None);
assert_eq!(x.remove("missing"), None);
assert!(!x.contains_key("missing"));
assert_eq!(x.iter().count(), 0);
assert_eq!(format!("{x:?}"), "{}");
assert_eq!(x.clone().into_iter().count(), 0);
let allocated_empty = IObject::with_capacity(8);
assert_eq!(x, x.clone());
assert_eq!(x, allocated_empty);
assert_eq!(hash_of(&x), hash_of(&allocated_empty));
x.insert("k", IValue::NULL);
assert_eq!(x.len(), 1);
assert_eq!(x.remove("k"), Some(IValue::NULL));
assert!(x.is_empty());
}
#[mockalloc::test]
fn can_collect() {
let x = vec![
("a", IValue::NULL),
("b", IValue::TRUE),
("c", IValue::FALSE),
];
let y: IObject = x.into_iter().collect();
assert_eq!(y, y.clone());
assert_eq!(y.len(), 3);
assert_eq!(y["a"], IValue::NULL);
assert_eq!(y["b"], IValue::TRUE);
assert_eq!(y["c"], IValue::FALSE);
}
#[mockalloc::test]
fn can_convert_serde_json_map() {
let mut map = serde_json::Map::new();
map.insert("a".to_owned(), serde_json::Value::Null);
map.insert("b".to_owned(), serde_json::json!(42));
map.insert("c".to_owned(), serde_json::json!("hi"));
let obj = IObject::from(map.clone());
assert_eq!(obj.len(), 3);
assert_eq!(obj["a"], IValue::NULL);
assert_eq!(obj["b"], IValue::from(42));
assert_eq!(obj["c"], IValue::from("hi"));
let back: serde_json::Map<String, serde_json::Value> = obj.into();
assert_eq!(back, map);
}
#[mockalloc::test]
fn can_insert() {
let mut x = IObject::new();
x.insert("a", IValue::NULL);
x.insert("b", IValue::TRUE);
x.insert("c", IValue::FALSE);
assert_eq!(x.len(), 3);
assert_eq!(x["a"], IValue::NULL);
assert_eq!(x["b"], IValue::TRUE);
assert_eq!(x["c"], IValue::FALSE);
}
#[mockalloc::test]
fn can_nest() {
let mut x = IObject::new();
x.insert("a", IValue::NULL);
x.insert("b", x.clone());
x.insert("c", IValue::FALSE);
x.insert("d", x.clone());
assert_eq!(x.len(), 4);
assert_eq!(x["a"], IValue::NULL);
assert_eq!(x["b"].len(), Some(1));
assert_eq!(x["c"], IValue::FALSE);
assert_eq!(x["d"].len(), Some(3));
}
#[mockalloc::test]
fn can_remove_and_shrink() {
let x = vec![
("a", IValue::NULL),
("b", IValue::TRUE),
("c", IValue::FALSE),
];
let mut y: IObject = x.into_iter().collect();
assert_eq!(y.len(), 3);
assert_eq!(y.capacity(), 4);
assert_eq!(y.remove("b"), Some(IValue::TRUE));
assert_eq!(y.remove("b"), None);
assert_eq!(y.remove("d"), None);
assert_eq!(y.len(), 2);
assert_eq!(y.capacity(), 4);
assert_eq!(y["a"], IValue::NULL);
assert_eq!(y["c"], IValue::FALSE);
y.shrink_to_fit();
assert_eq!(y.len(), 2);
assert_eq!(y.capacity(), 2);
assert_eq!(y["a"], IValue::NULL);
assert_eq!(y["c"], IValue::FALSE);
}
#[cfg(not(miri))]
#[mockalloc::test]
fn stress_test() {
use rand::prelude::*;
for i in 0..10 {
let mut rng = StdRng::seed_from_u64(i);
let range = 0..10000;
let mut ops: Vec<i32> = range.clone().chain(range).collect();
ops.shuffle(&mut rng);
let mut x = IObject::new();
for op in ops {
let k = IString::intern(&op.to_string());
if x.contains_key(&k) {
x.remove(&k);
} else {
x.insert(k, op);
}
}
assert_eq!(x, IObject::new());
}
}
#[mockalloc::test]
fn entry_or_insert_variants() {
let mut o = IObject::new();
assert_eq!(*o.entry("a").or_insert(IValue::from(1)), IValue::from(1));
assert_eq!(*o.entry("a").or_insert(IValue::from(2)), IValue::from(1));
assert_eq!(
*o.entry("b").or_insert_with(|| IValue::from(3)),
IValue::from(3)
);
let mut called = false;
let v = o.entry("b").or_insert_with(|| {
called = true;
IValue::from(9)
});
assert_eq!(*v, IValue::from(3));
assert!(!called, "closure must not run for an occupied entry");
}
#[mockalloc::test]
fn entry_key_and_modify_and_debug() {
let mut o = IObject::new();
o.insert("x", IValue::from(1));
assert_eq!(o.entry("x").key().as_str(), "x");
assert_eq!(o.entry("y").key().as_str(), "y");
o.entry("x")
.and_modify(|v| *v = IValue::from(2))
.or_insert(IValue::from(0));
assert_eq!(o["x"], IValue::from(2));
o.entry("z")
.and_modify(|_| panic!("and_modify must not run on a vacant entry"))
.or_insert(IValue::from(5));
assert_eq!(o["z"], IValue::from(5));
assert!(format!("{:?}", o.entry("x")).contains("Occupied"));
assert!(format!("{:?}", o.entry("new")).contains("Vacant"));
}
#[mockalloc::test]
fn occupied_entry_methods() {
let mut o = IObject::new();
o.insert("k", IValue::from(1));
match o.entry("k") {
Entry::Occupied(mut occ) => {
assert_eq!(occ.key().as_str(), "k");
assert_eq!(*occ.get(), IValue::from(1));
*occ.get_mut() = IValue::from(2);
assert_eq!(*occ.get(), IValue::from(2));
assert_eq!(occ.insert(IValue::from(3)), IValue::from(2));
*occ.into_mut() = IValue::from(4);
}
Entry::Vacant(_) => panic!("expected an occupied entry"),
}
assert_eq!(o["k"], IValue::from(4));
o.insert("a", IValue::from(7));
match o.entry("a") {
Entry::Occupied(occ) => {
assert_eq!(occ.remove_entry(), (IString::intern("a"), IValue::from(7)));
}
Entry::Vacant(_) => panic!("expected an occupied entry"),
}
o.insert("b", IValue::from(8));
match o.entry("b") {
Entry::Occupied(occ) => assert_eq!(occ.remove(), IValue::from(8)),
Entry::Vacant(_) => panic!("expected an occupied entry"),
}
assert!(!o.contains_key("a"));
assert!(!o.contains_key("b"));
}
#[mockalloc::test]
fn vacant_entry_methods() {
let mut o = IObject::new();
match o.entry("k") {
Entry::Vacant(vac) => {
assert_eq!(vac.key().as_str(), "k");
*vac.insert(IValue::from(1)) = IValue::from(2);
}
Entry::Occupied(_) => panic!("expected a vacant entry"),
}
assert_eq!(o["k"], IValue::from(2));
match o.entry("unused") {
Entry::Vacant(vac) => assert_eq!(vac.into_key().as_str(), "unused"),
Entry::Occupied(_) => panic!("expected a vacant entry"),
}
assert!(!o.contains_key("unused"));
}
#[mockalloc::test]
fn entry_or_clone_reuses_key() {
let mut o = IObject::new();
let key = IString::intern("shared");
o.entry_or_clone(&key).or_insert(IValue::from(1));
o.entry_or_clone(&key).and_modify(|v| *v = IValue::from(2));
assert_eq!(o[&key], IValue::from(2));
}
#[mockalloc::test]
fn iterators() {
let mut o = IObject::new();
o.insert("a", IValue::from(1));
o.insert("b", IValue::from(2));
o.insert("c", IValue::from(3));
let keys: Vec<&str> = o.keys().map(IString::as_str).collect();
assert_eq!(keys, ["a", "b", "c"]);
let sum: i64 = o.values().map(|v| v.to_i64().unwrap()).sum();
assert_eq!(sum, 6);
assert_eq!(o.iter().len(), 3);
for v in o.values_mut() {
*v = IValue::from(v.to_i64().unwrap() * 10);
}
assert_eq!(o["a"], IValue::from(10));
assert_eq!(o.iter_mut().len(), 3);
for (k, v) in o.iter_mut() {
if k.as_str() == "b" {
*v = IValue::from(0);
}
}
assert_eq!(o["b"], IValue::from(0));
assert_eq!(o.clone().into_iter().len(), 3);
assert!(format!("{:?}", o.clone().into_iter()).contains("IntoIter"));
assert_eq!(o.into_iter().count(), 3);
}
#[mockalloc::test]
fn clear_keeps_capacity() {
let mut o = IObject::with_capacity(8);
o.insert("a", IValue::from(1));
o.insert("b", IValue::from(2));
let cap = o.capacity();
o.clear();
assert!(o.is_empty());
assert_eq!(o.capacity(), cap);
o.clear();
assert!(o.is_empty());
}
#[mockalloc::test]
fn get_key_value_variants() {
let mut o = IObject::new();
o.insert("a", IValue::from(1));
let (k, v) = o.get_key_value("a").unwrap();
assert_eq!(k.as_str(), "a");
assert_eq!(*v, IValue::from(1));
assert!(o.get_key_value("missing").is_none());
*o.get_mut("a").unwrap() = IValue::from(2);
assert_eq!(o.get("a"), Some(&IValue::from(2)));
assert!(o.get_mut("missing").is_none());
{
let (k, v) = o.get_key_value_mut("a").unwrap();
assert_eq!(k.as_str(), "a");
*v = IValue::from(3);
}
assert!(o.get_key_value_mut("missing").is_none());
assert_eq!(o["a"], IValue::from(3));
let key_ref = &"a";
assert_eq!(o.get(key_ref), Some(&IValue::from(3)));
assert!(o.contains_key(key_ref));
}
#[mockalloc::test]
fn retain_filters_and_mutates() {
let mut o = IObject::new();
for i in 0..6 {
o.insert(i.to_string(), IValue::from(i));
}
o.retain(|_k, v| {
let n = v.to_i64().unwrap();
if n % 2 == 0 {
*v = IValue::from(n * 10);
true
} else {
false
}
});
assert_eq!(o.len(), 3);
assert_eq!(o["0"], IValue::from(0));
assert_eq!(o["2"], IValue::from(20));
assert_eq!(o["4"], IValue::from(40));
assert!(!o.contains_key("1"));
let mut e = IObject::new();
e.retain(|_, _| panic!("must not visit an empty object"));
assert!(e.is_empty());
}
#[mockalloc::test]
fn index_and_index_mut() {
let mut o = IObject::new();
o.insert("a", IValue::from(1));
assert_eq!(o["a"], IValue::from(1));
o["a"] = IValue::from(2);
assert_eq!(o["a"], IValue::from(2));
o["new"] = IValue::from(5);
assert_eq!(o["new"], IValue::from(5));
}
#[mockalloc::test]
fn from_maps_and_extend() {
let mut hm = HashMap::new();
hm.insert("a", 1);
hm.insert("b", 2);
let o: IObject = hm.into();
assert_eq!(o.len(), 2);
assert_eq!(o["a"], IValue::from(1));
let mut bt = BTreeMap::new();
bt.insert("x", 10);
bt.insert("y", 20);
let mut o2: IObject = bt.into();
assert_eq!(o2["y"], IValue::from(20));
o2.extend(vec![("z", 30), ("x", 11)]);
assert_eq!(o2.len(), 3);
assert_eq!(o2["x"], IValue::from(11));
assert_eq!(o2["z"], IValue::from(30));
}
#[mockalloc::test]
fn partial_ord_delegates() {
let mut a = IObject::new();
a.insert("k", IValue::from(1));
let b = a.clone();
assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal));
assert!(a >= b);
}
}