From 2bc889afec5d0514213b5bc9c0082b2ad251d4b0 Mon Sep 17 00:00:00 2001 From: "Heinz N. Gies" Date: Sat, 6 Jul 2024 19:14:27 +0200 Subject: [PATCH] Draft of lazy Value Signed-off-by: Heinz N. Gies --- src/impls/neon/stage1.rs | 2 +- src/macros.rs | 6 +- src/numberparse/approx.rs | 4 +- src/value.rs | 4 + src/value/borrowed.rs | 112 +++-- src/value/lazy.rs | 768 ++++++++++++++++++++++++++++++++++ src/value/lazy/array.rs | 142 +++++++ src/value/lazy/cmp.rs | 172 ++++++++ src/value/lazy/from.rs | 227 ++++++++++ src/value/lazy/object.rs | 276 ++++++++++++ src/value/owned.rs | 40 +- src/value/tape.rs | 117 ++++-- src/value/tape/array.rs | 10 +- src/value/tape/cmp.rs | 21 +- src/value/tape/object.rs | 38 +- src/value/tape/trait_impls.rs | 117 ++++-- 16 files changed, 1920 insertions(+), 136 deletions(-) create mode 100644 src/value/lazy.rs create mode 100644 src/value/lazy/array.rs create mode 100644 src/value/lazy/cmp.rs create mode 100644 src/value/lazy/from.rs create mode 100644 src/value/lazy/object.rs diff --git a/src/impls/neon/stage1.rs b/src/impls/neon/stage1.rs index 2f46bc54..ac2c5597 100644 --- a/src/impls/neon/stage1.rs +++ b/src/impls/neon/stage1.rs @@ -175,7 +175,7 @@ impl Stage1Parse for SimdInput { let cnt: usize = bits.count_ones() as usize; let mut l = base.len(); let idx_minus_64 = idx.wrapping_sub(64); - let idx_64_v = mem::transmute::<_, int32x4_t>([ + let idx_64_v = mem::transmute::<[i32; 4], int32x4_t>([ static_cast_i32!(idx_minus_64), static_cast_i32!(idx_minus_64), static_cast_i32!(idx_minus_64), diff --git a/src/macros.rs b/src/macros.rs index 35f9009f..c0b34fc1 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1262,7 +1262,7 @@ macro_rules! static_cast_i8 { #[macro_export] macro_rules! static_cast_i32 { ($v:expr) => { - std::mem::transmute::<_, i32>($v) + std::mem::transmute::($v) }; } @@ -1278,7 +1278,7 @@ macro_rules! static_cast_u32 { #[macro_export] macro_rules! static_cast_i64 { ($v:expr) => { - ::std::mem::transmute::<_, i64>($v) + ::std::mem::transmute::($v) }; } @@ -1294,7 +1294,7 @@ macro_rules! static_cast_i128 { #[macro_export] macro_rules! static_cast_u64 { ($v:expr) => { - ::std::mem::transmute::<_, u64>($v) + ::std::mem::transmute::($v) }; } diff --git a/src/numberparse/approx.rs b/src/numberparse/approx.rs index 7b1b6aad..802f72e1 100644 --- a/src/numberparse/approx.rs +++ b/src/numberparse/approx.rs @@ -259,7 +259,7 @@ impl<'de> Deserializer<'de> { } if negative && i > 9_223_372_036_854_775_808 { - //i64::min_value() * -1 + //i64::MIN * -1 return Err(Self::error_c( idx + digitcount, d as char, @@ -320,7 +320,7 @@ impl<'de> Deserializer<'de> { } if negative && i > 170_141_183_460_469_231_731_687_303_715_884_105_728_u128 { - //i64::min_value() * -1 + //i64::MIN * -1 return Err(Self::error_c( idx + digitcount, d as char, diff --git a/src/value.rs b/src/value.rs index d63aaa79..003ad621 100644 --- a/src/value.rs +++ b/src/value.rs @@ -56,6 +56,10 @@ pub mod borrowed; pub mod owned; /// Tape implementation pub mod tape; + +/// A value that starts out as a tape and upgraes to a borrowed value when mutation is needed +pub mod lazy; + pub use self::borrowed::{ to_value as to_borrowed_value, to_value_with_buffers as to_borrowed_value_with_buffers, Value as BorrowedValue, diff --git a/src/value/borrowed.rs b/src/value/borrowed.rs index 11698132..df951366 100644 --- a/src/value/borrowed.rs +++ b/src/value/borrowed.rs @@ -25,7 +25,7 @@ mod from; mod serialize; use super::ObjectHasher; -use crate::cow::Cow; +use crate::{cow::Cow, safer_unchecked::GetSaferUnchecked as _}; use crate::{prelude::*, Buffers}; use crate::{Deserializer, Node, Result}; use halfbrown::HashMap; @@ -34,6 +34,8 @@ use std::ops::{Index, IndexMut}; /// Representation of a JSON object pub type Object<'value> = HashMap, Value<'value>, ObjectHasher>; +/// Representation of a JSON array +pub type Array<'value> = Vec>; /// Parses a slice of bytes into a Value dom. This function will /// rewrite the slice to de-escape strings. @@ -176,7 +178,7 @@ impl<'value> ValueBuilder<'value> for Value<'value> { } impl<'value> ValueAsMutContainer for Value<'value> { - type Array = Vec; + type Array = Array<'value>; type Object = Object<'value>; #[cfg_attr(not(feature = "no-inline"), inline)] #[must_use] @@ -286,7 +288,7 @@ impl<'value> ValueAsScalar for Value<'value> { } } impl<'value> ValueAsContainer for Value<'value> { - type Array = Vec; + type Array = Array<'value>; type Object = Object<'value>; #[cfg_attr(not(feature = "no-inline"), inline)] @@ -320,7 +322,7 @@ impl<'value> ValueIntoString for Value<'value> { } impl<'value> ValueIntoContainer for Value<'value> { - type Array = Vec; + type Array = Array<'value>; type Object = Object<'value>; fn into_array(self) -> Option<::Array> { @@ -392,7 +394,7 @@ impl<'value> Default for Value<'value> { } } -struct BorrowDeserializer<'de>(Deserializer<'de>); +pub(super) struct BorrowDeserializer<'de>(Deserializer<'de>); impl<'de> BorrowDeserializer<'de> { pub fn from_deserializer(de: Deserializer<'de>) -> Self { @@ -444,6 +446,66 @@ impl<'de> BorrowDeserializer<'de> { Value::from(res) } } +pub(super) struct BorrowSliceDeserializer<'tape, 'de> { + tape: &'tape [Node<'de>], + idx: usize, +} +impl<'tape, 'de> BorrowSliceDeserializer<'tape, 'de> { + pub fn from_tape(de: &'tape [Node<'de>]) -> Self { + Self { tape: de, idx: 0 } + } + #[cfg_attr(not(feature = "no-inline"), inline)] + pub unsafe fn next_(&mut self) -> Node<'de> { + let r = *self.tape.get_kinda_unchecked(self.idx); + self.idx += 1; + r + } + + #[cfg_attr(not(feature = "no-inline"), inline)] + pub fn parse(&mut self) -> Value<'de> { + match unsafe { self.next_() } { + Node::Static(s) => Value::Static(s), + Node::String(s) => Value::from(s), + Node::Array { len, count: _ } => self.parse_array(len), + Node::Object { len, count: _ } => self.parse_map(len), + } + } + + #[cfg_attr(not(feature = "no-inline"), inline)] + fn parse_array(&mut self, len: usize) -> Value<'de> { + // Rust doesn't optimize the normal loop away here + // so we write our own avoiding the length + // checks during push + let mut res: Vec> = Vec::with_capacity(len); + let res_ptr = res.as_mut_ptr(); + unsafe { + for i in 0..len { + res_ptr.add(i).write(self.parse()); + } + res.set_len(len); + } + Value::Array(res) + } + + #[cfg_attr(not(feature = "no-inline"), inline)] + fn parse_map(&mut self, len: usize) -> Value<'de> { + let mut res = Object::with_capacity_and_hasher(len, ObjectHasher::default()); + + // Since we checked if it's empty we know that we at least have one + // element so we eat this + for _ in 0..len { + if let Node::String(key) = unsafe { self.next_() } { + #[cfg(not(feature = "value-no-dup-keys"))] + res.insert_nocheck(key.into(), self.parse()); + #[cfg(feature = "value-no-dup-keys")] + res.insert(key.into(), self.parse()); + } else { + unreachable!(); + } + } + Value::from(res) + } +} #[cfg(test)] mod test { @@ -483,7 +545,7 @@ mod test { #[cfg(feature = "128bit")] #[test] fn conversions_i128() { - let v = Value::from(i128::max_value()); + let v = Value::from(i128::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(!v.is_i64()); @@ -497,7 +559,7 @@ mod test { assert!(!v.is_f64()); assert!(!v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(i128::min_value()); + let v = Value::from(i128::MIN); assert!(v.is_i128()); assert!(!v.is_u128()); assert!(!v.is_i64()); @@ -515,7 +577,7 @@ mod test { #[test] fn conversions_i64() { - let v = Value::from(i64::max_value()); + let v = Value::from(i64::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -529,7 +591,7 @@ mod test { assert!(!v.is_f64()); assert!(!v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(i64::min_value()); + let v = Value::from(i64::MIN); assert!(v.is_i128()); assert!(!v.is_u128()); assert!(v.is_i64()); @@ -547,7 +609,7 @@ mod test { #[test] fn conversions_i32() { - let v = Value::from(i32::max_value()); + let v = Value::from(i32::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -561,7 +623,7 @@ mod test { assert!(!v.is_f64()); assert!(!v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(i32::min_value()); + let v = Value::from(i32::MIN); assert!(v.is_i128()); assert!(!v.is_u128()); assert!(v.is_i64()); @@ -579,7 +641,7 @@ mod test { #[test] fn conversions_i16() { - let v = Value::from(i16::max_value()); + let v = Value::from(i16::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -593,7 +655,7 @@ mod test { assert!(!v.is_f64()); assert!(!v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(i16::min_value()); + let v = Value::from(i16::MIN); assert!(v.is_i128()); assert!(!v.is_u128()); assert!(v.is_i64()); @@ -612,7 +674,7 @@ mod test { #[test] fn conversions_i8() { - let v = Value::from(i8::max_value()); + let v = Value::from(i8::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -626,7 +688,7 @@ mod test { assert!(!v.is_f64()); assert!(!v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(i8::min_value()); + let v = Value::from(i8::MIN); assert!(v.is_i128()); assert!(!v.is_u128()); assert!(v.is_i64()); @@ -644,7 +706,7 @@ mod test { #[test] fn conversions_usize() { - let v = Value::from(usize::min_value() as u64); + let v = Value::from(usize::MIN as u64); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -666,7 +728,7 @@ mod test { #[cfg(feature = "128bit")] #[test] fn conversions_u128() { - let v = Value::from(u128::min_value()); + let v = Value::from(u128::MIN); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -684,7 +746,7 @@ mod test { #[test] fn conversions_u64() { - let v = Value::from(u64::min_value()); + let v = Value::from(u64::MIN); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -702,7 +764,7 @@ mod test { #[test] fn conversions_u32() { - let v = Value::from(u32::max_value()); + let v = Value::from(u32::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -720,7 +782,7 @@ mod test { #[test] fn conversions_u16() { - let v = Value::from(u16::max_value()); + let v = Value::from(u16::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -738,7 +800,7 @@ mod test { #[test] fn conversions_u8() { - let v = Value::from(u8::max_value()); + let v = Value::from(u8::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -756,13 +818,13 @@ mod test { #[test] fn conversions_f64() { - let v = Value::from(std::f64::MAX); + let v = Value::from(f64::MAX); assert!(!v.is_i64()); assert!(!v.is_u64()); assert!(v.is_f64()); assert!(!v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(std::f64::MIN); + let v = Value::from(f64::MIN); assert!(!v.is_i64()); assert!(!v.is_u64()); assert!(v.is_f64()); @@ -774,13 +836,13 @@ mod test { #[test] fn conversions_f32() { - let v = Value::from(std::f32::MAX); + let v = Value::from(f32::MAX); assert!(!v.is_i64()); assert!(!v.is_u64()); assert!(v.is_f64()); assert!(v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(std::f32::MIN); + let v = Value::from(f32::MIN); assert!(!v.is_i64()); assert!(!v.is_u64()); assert!(v.is_f64()); diff --git a/src/value/lazy.rs b/src/value/lazy.rs new file mode 100644 index 00000000..0bfc5491 --- /dev/null +++ b/src/value/lazy.rs @@ -0,0 +1,768 @@ +use crate::cow::Cow; +use crate::prelude::*; +use crate::{borrowed, tape}; +use std::fmt; + +mod array; +mod cmp; +mod from; +mod object; + +pub use array::Array; +pub use object::Object; + +/// A lazy value, this gets initialized with a tape and as long as only non mutating operations are +/// performed it will stay a tape. If a mutating operation is performed it will upgrade to a borrowed +/// value. +#[derive(Clone, Debug, PartialEq)] +pub enum Value<'tape, 'input> { + /// tape variant + Tape(tape::Value<'tape, 'input>), + /// borrowed variant + Value(Cow<'tape, borrowed::Value<'input>>), +} + +impl Default for Value<'static, '_> { + #[cfg_attr(not(feature = "no-inline"), inline)] + fn default() -> Self { + Value::Value(Cow::Owned(borrowed::Value::default())) + } +} + +impl<'tape, 'input> Value<'tape, 'input> { + /// Creates a new lazy Value from a tape + #[must_use] + pub fn from_tape(tape: tape::Value<'tape, 'input>) -> Self { + Value::Tape(tape) + } + unsafe fn into_tape(self) -> tape::Value<'tape, 'input> { + match self { + Value::Tape(tape) => tape, + Value::Value(_) => unreachable!("we know we are not a value"), + } + } + + fn upgrade(&mut self) { + if let Value::Value(_) = &self { + return; + } + let mut dummy = Value::Tape(tape::Value::null()); + std::mem::swap(self, &mut dummy); + let tape = unsafe { dummy.into_tape() }; + + let value = super::borrowed::BorrowSliceDeserializer::from_tape(tape.0).parse(); + + *self = Value::Value(Cow::Owned(value)); + } + + fn is_tape(&self) -> bool { + match &self { + Value::Tape(_) => true, + Value::Value(_) => false, + } + } + + fn as_mut(&mut self) -> &mut borrowed::Value<'input> { + if self.is_tape() { + self.upgrade(); + } + + if let Value::Value(value) = self { + value.to_mut() + } else { + unreachable!() + } + } +} + +impl<'value> ValueBuilder<'value> for Value<'static, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn null() -> Self { + Value::Value(Cow::Owned(borrowed::Value::null())) + } + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn array_with_capacity(capacity: usize) -> Self { + Value::Value(Cow::Owned(borrowed::Value::array_with_capacity(capacity))) + } + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn object_with_capacity(capacity: usize) -> Self { + Value::Value(Cow::Owned(borrowed::Value::object_with_capacity(capacity))) + } +} + +impl<'tape, 'value> ValueAsMutContainer for Value<'tape, 'value> { + type Array = Vec>; + type Object = super::borrowed::Object<'value>; + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn as_array_mut(&mut self) -> Option<&mut Vec>> { + self.as_mut().as_array_mut() + } + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn as_object_mut(&mut self) -> Option<&mut super::borrowed::Object<'value>> { + self.as_mut().as_object_mut() + } +} + +impl<'tape, 'value> TypedValue for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn value_type(&self) -> ValueType { + match &self { + Value::Tape(tape) => tape.value_type(), + Value::Value(value) => value.value_type(), + } + } +} + +impl<'tape, 'value> ValueAsScalar for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn as_null(&self) -> Option<()> { + match &self { + Value::Tape(tape) => tape.as_null(), + Value::Value(value) => value.as_null(), + } + } + + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn as_bool(&self) -> Option { + match &self { + Value::Tape(tape) => tape.as_bool(), + Value::Value(value) => value.as_bool(), + } + } + + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn as_i64(&self) -> Option { + match &self { + Value::Tape(tape) => tape.as_i64(), + Value::Value(value) => value.as_i64(), + } + } + + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn as_i128(&self) -> Option { + match &self { + Value::Tape(tape) => tape.as_i128(), + Value::Value(value) => value.as_i128(), + } + } + + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn as_u64(&self) -> Option { + match &self { + Value::Tape(tape) => tape.as_u64(), + Value::Value(value) => value.as_u64(), + } + } + + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn as_u128(&self) -> Option { + match &self { + Value::Tape(tape) => tape.as_u128(), + Value::Value(value) => value.as_u128(), + } + } + + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn as_f64(&self) -> Option { + match &self { + Value::Tape(tape) => tape.as_f64(), + Value::Value(value) => value.as_f64(), + } + } + + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn cast_f64(&self) -> Option { + match &self { + Value::Tape(tape) => tape.cast_f64(), + Value::Value(value) => value.cast_f64(), + } + } + + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn as_str(&self) -> Option<&str> { + match &self { + Value::Tape(tape) => tape.as_str(), + Value::Value(value) => value.as_str(), + } + } +} + +// impl<'tape, 'value> ValueAsContainer for Value<'tape, 'value> { +impl<'tape, 'value> Value<'tape, 'value> { + // type Array = array::Array<'tape, 'value>; + // type Object = Object<'tape, 'value>; + + /// Tries to represent the value as an array and returns a reference to it + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + pub fn as_array(&self) -> Option> { + match self { + Value::Tape(tape) => tape.as_array().map(Array::Tape), + Value::Value(value) => value.as_array().map(array::Array::Value), + } + } + + /// Tries to represent the value as an array and returns a reference to it + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + pub fn as_object(&self) -> Option { + match self { + Value::Tape(tape) => tape.as_object().map(Object::Tape), + Value::Value(value) => value.as_object().map(Object::Value), + } + } +} + +impl<'tape, 'value> ValueIntoString for Value<'tape, 'value> { + type String = Cow<'value, str>; + + fn into_string(self) -> Option<::String> { + match self { + Value::Tape(tape) => tape.into_string().map(Cow::Borrowed), + // This is a bit complex but it allows us to avoid cloning + Value::Value(value) => match value { + Cow::Borrowed(value) => match value { + borrowed::Value::String(s) => Some(s.clone()), + _ => None, + }, + Cow::Owned(value) => match value { + borrowed::Value::String(s) => Some(s), + _ => None, + }, + }, + } + } +} + +// impl<'value> ValueIntoContainer for Value<'value> { +// type Array = Vec; +// type Object = Object<'value>; + +// fn into_array(self) -> Option<::Array> { +// match self { +// Self::Array(a) => Some(a), +// _ => None, +// } +// } + +// fn into_object(self) -> Option<::Object> { +// match self { +// Self::Object(a) => Some(*a), +// _ => None, +// } +// } +// } + +#[cfg(not(tarpaulin_include))] +impl<'tape, 'value> fmt::Display for Value<'tape, 'value> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match &self { + Value::Tape(tape) => write!(f, "{tape:?}"), + Value::Value(value) => write!(f, "{value}"), + } + } +} + +// impl<'value> Index<&str> for Value<'value> { +// type Output = Value<'value>; +// #[cfg_attr(not(feature = "no-inline"), inline)] +// #[must_use] +// fn index(&self, index: &str) -> &Self::Output { +// self.get(index).expect("index out of bounds") +// } +// } + +// impl<'value> Index for Value<'value> { +// type Output = Value<'value>; +// #[cfg_attr(not(feature = "no-inline"), inline)] +// #[must_use] +// fn index(&self, index: usize) -> &Self::Output { +// self.get_idx(index).expect("index out of bounds") +// } +// } + +// impl<'value> IndexMut<&str> for Value<'value> { +// #[cfg_attr(not(feature = "no-inline"), inline)] +// #[must_use] +// fn index_mut(&mut self, index: &str) -> &mut Self::Output { +// self.get_mut(index).expect("index out of bounds") +// } +// } + +// impl<'value> IndexMut for Value<'value> { +// #[cfg_attr(not(feature = "no-inline"), inline)] +// #[must_use] +// fn index_mut(&mut self, index: usize) -> &mut Self::Output { +// self.get_idx_mut(index).expect("index out of bounds") +// } +// } + +#[cfg(test)] +mod test { + #![allow(clippy::cognitive_complexity)] + use super::Value; + use super::*; + + #[test] + #[should_panic = "Not supported"] + #[allow(unused_variables, clippy::no_effect)] + fn object_index() { + let v = StaticNode::Null; + v["test"]; + } + + #[test] + #[should_panic = "Not supported"] + fn mut_object_index() { + let mut v = StaticNode::Null; + v["test"] = (); + } + + #[test] + #[should_panic = "Not supported"] + #[allow(unused_variables, clippy::no_effect)] + fn array_index() { + let v = StaticNode::Null; + v[0]; + } + + #[test] + #[should_panic = "Not supported"] + fn mut_array_index() { + let mut v = StaticNode::Null; + v[0] = (); + } + + #[test] + fn conversion_str() { + let v = StaticNode::Null; + assert!(!v.is_str()); + } + #[cfg(feature = "128bit")] + #[test] + fn conversions_i128() { + let v = Value::from(i128::MAX); + assert!(v.is_i128()); + assert!(v.is_u128()); + assert!(!v.is_i64()); + assert!(!v.is_u64()); + assert!(!v.is_i32()); + assert!(!v.is_u32()); + assert!(!v.is_i16()); + assert!(!v.is_u16()); + assert!(!v.is_i8()); + assert!(!v.is_u8()); + assert!(!v.is_f64()); + assert!(!v.is_f32()); + assert!(v.is_f64_castable()); + let v = Value::from(i128::MIN); + assert!(v.is_i128()); + assert!(!v.is_u128()); + assert!(!v.is_i64()); + assert!(!v.is_u64()); + assert!(!v.is_i32()); + assert!(!v.is_u32()); + assert!(!v.is_i16()); + assert!(!v.is_u16()); + assert!(!v.is_i8()); + assert!(!v.is_u8()); + assert!(!v.is_f64()); + assert!(!v.is_f32()); + assert!(v.is_f64_castable()); + } + + #[test] + fn conversions_i64() { + let v = Value::from(i64::MAX); + assert!(v.is_i128()); + assert!(v.is_u128()); + assert!(v.is_i64()); + assert!(v.is_u64()); + assert!(!v.is_i32()); + assert!(!v.is_u32()); + assert!(!v.is_i16()); + assert!(!v.is_u16()); + assert!(!v.is_i8()); + assert!(!v.is_u8()); + assert!(!v.is_f64()); + assert!(!v.is_f32()); + assert!(v.is_f64_castable()); + let v = Value::from(i64::MIN); + assert!(v.is_i128()); + assert!(!v.is_u128()); + assert!(v.is_i64()); + assert!(!v.is_u64()); + assert!(!v.is_i32()); + assert!(!v.is_u32()); + assert!(!v.is_i16()); + assert!(!v.is_u16()); + assert!(!v.is_i8()); + assert!(!v.is_u8()); + assert!(!v.is_f64()); + assert!(!v.is_f32()); + assert!(v.is_f64_castable()); + } + + #[test] + fn conversions_i32() { + let v = Value::from(i32::MAX); + assert!(v.is_i128()); + assert!(v.is_u128()); + assert!(v.is_i64()); + assert!(v.is_u64()); + assert!(v.is_i32()); + assert!(v.is_u32()); + assert!(!v.is_i16()); + assert!(!v.is_u16()); + assert!(!v.is_i8()); + assert!(!v.is_u8()); + assert!(!v.is_f64()); + assert!(!v.is_f32()); + assert!(v.is_f64_castable()); + let v = Value::from(i32::MIN); + assert!(v.is_i128()); + assert!(!v.is_u128()); + assert!(v.is_i64()); + assert!(!v.is_u64()); + assert!(v.is_i32()); + assert!(!v.is_u32()); + assert!(!v.is_i16()); + assert!(!v.is_u16()); + assert!(!v.is_i8()); + assert!(!v.is_u8()); + assert!(!v.is_f64()); + assert!(!v.is_f32()); + assert!(v.is_f64_castable()); + } + + #[test] + fn conversions_i16() { + let v = Value::from(i16::MAX); + assert!(v.is_i128()); + assert!(v.is_u128()); + assert!(v.is_i64()); + assert!(v.is_u64()); + assert!(v.is_i32()); + assert!(v.is_u32()); + assert!(v.is_i16()); + assert!(v.is_u16()); + assert!(!v.is_i8()); + assert!(!v.is_u8()); + assert!(!v.is_f64()); + assert!(!v.is_f32()); + assert!(v.is_f64_castable()); + let v = Value::from(i16::MIN); + assert!(v.is_i128()); + assert!(!v.is_u128()); + assert!(v.is_i64()); + assert!(!v.is_u64()); + assert!(v.is_i32()); + assert!(!v.is_u32()); + assert!(v.is_i16()); + assert!(!v.is_u16()); + assert!(!v.is_i8()); + assert!(!v.is_u8()); + assert!(!v.is_f64()); + assert!(!v.is_f32()); + assert!(v.is_f64_castable()); + assert!(v.is_f64_castable()); + } + + #[test] + fn conversions_i8() { + let v = Value::from(i8::MAX); + assert!(v.is_i128()); + assert!(v.is_u128()); + assert!(v.is_i64()); + assert!(v.is_u64()); + assert!(v.is_i32()); + assert!(v.is_u32()); + assert!(v.is_i16()); + assert!(v.is_u16()); + assert!(v.is_i8()); + assert!(v.is_u8()); + assert!(!v.is_f64()); + assert!(!v.is_f32()); + assert!(v.is_f64_castable()); + let v = Value::from(i8::MIN); + assert!(v.is_i128()); + assert!(!v.is_u128()); + assert!(v.is_i64()); + assert!(!v.is_u64()); + assert!(v.is_i32()); + assert!(!v.is_u32()); + assert!(v.is_i16()); + assert!(!v.is_u16()); + assert!(v.is_i8()); + assert!(!v.is_u8()); + assert!(!v.is_f64()); + assert!(!v.is_f32()); + assert!(v.is_f64_castable()); + } + + #[test] + fn conversions_usize() { + let v = Value::from(usize::MIN as u64); + assert!(v.is_i128()); + assert!(v.is_u128()); + assert!(v.is_i64()); + assert!(v.is_u64()); + assert!(v.is_usize()); + assert!(v.is_i32()); + assert!(v.is_u32()); + assert!(v.is_i16()); + assert!(v.is_u16()); + assert!(v.is_i8()); + assert!(v.is_u8()); + assert!(!v.is_f64()); + assert!(!v.is_f32()); + assert!(!v.is_f64()); + assert!(!v.is_f32()); + assert!(v.is_f64_castable()); + } + + #[cfg(feature = "128bit")] + #[test] + fn conversions_u128() { + let v = Value::from(u128::MIN); + assert!(v.is_i128()); + assert!(v.is_u128()); + assert!(v.is_i64()); + assert!(v.is_u64()); + assert!(v.is_i32()); + assert!(v.is_u32()); + assert!(v.is_i16()); + assert!(v.is_u16()); + assert!(v.is_i8()); + assert!(v.is_u8()); + assert!(!v.is_f64()); + assert!(!v.is_f32()); + assert!(v.is_f64_castable()); + } + + #[test] + fn conversions_u64() { + let v = Value::from(u64::MIN); + assert!(v.is_i128()); + assert!(v.is_u128()); + assert!(v.is_i64()); + assert!(v.is_u64()); + assert!(v.is_i32()); + assert!(v.is_u32()); + assert!(v.is_i16()); + assert!(v.is_u16()); + assert!(v.is_i8()); + assert!(v.is_u8()); + assert!(!v.is_f64()); + assert!(!v.is_f32()); + assert!(v.is_f64_castable()); + } + + #[test] + fn conversions_u32() { + let v = Value::from(u32::MAX); + assert!(v.is_i128()); + assert!(v.is_u128()); + assert!(v.is_i64()); + assert!(v.is_u64()); + assert!(!v.is_i32()); + assert!(v.is_u32()); + assert!(!v.is_i16()); + assert!(!v.is_u16()); + assert!(!v.is_i8()); + assert!(!v.is_u8()); + assert!(!v.is_f64()); + assert!(!v.is_f32()); + assert!(v.is_f64_castable()); + } + + #[test] + fn conversions_u16() { + let v = Value::from(u16::MAX); + assert!(v.is_i128()); + assert!(v.is_u128()); + assert!(v.is_i64()); + assert!(v.is_u64()); + assert!(v.is_i32()); + assert!(v.is_u32()); + assert!(!v.is_i16()); + assert!(v.is_u16()); + assert!(!v.is_i8()); + assert!(!v.is_u8()); + assert!(!v.is_f64()); + assert!(!v.is_f32()); + assert!(v.is_f64_castable()); + } + + #[test] + fn conversions_u8() { + let v = Value::from(u8::MAX); + assert!(v.is_i128()); + assert!(v.is_u128()); + assert!(v.is_i64()); + assert!(v.is_u64()); + assert!(v.is_i32()); + assert!(v.is_u32()); + assert!(v.is_i16()); + assert!(v.is_u16()); + assert!(!v.is_i8()); + assert!(v.is_u8()); + assert!(!v.is_f64()); + assert!(!v.is_f32()); + assert!(v.is_f64_castable()); + } + + #[test] + fn conversions_f64() { + let v = Value::from(f64::MAX); + assert!(!v.is_i64()); + assert!(!v.is_u64()); + assert!(v.is_f64()); + assert!(!v.is_f32()); + assert!(v.is_f64_castable()); + let v = Value::from(f64::MIN); + assert!(!v.is_i64()); + assert!(!v.is_u64()); + assert!(v.is_f64()); + assert!(!v.is_f32()); + assert!(v.is_f64_castable()); + let v = Value::from(()); + assert!(!v.is_f64_castable()); + } + + #[test] + fn conversions_f32() { + let v = Value::from(f32::MAX); + assert!(!v.is_i64()); + assert!(!v.is_u64()); + assert!(v.is_f64()); + assert!(v.is_f32()); + assert!(v.is_f64_castable()); + let v = Value::from(f32::MIN); + assert!(!v.is_i64()); + assert!(!v.is_u64()); + assert!(v.is_f64()); + assert!(v.is_f32()); + assert!(v.is_f64_castable()); + } + + #[test] + fn conversions_bool() { + let v = Value::from(true); + assert!(v.is_bool()); + assert_eq!(v.value_type(), ValueType::Bool); + let v = Value::from(()); + assert!(!v.is_bool()); + } + + #[test] + fn conversions_float() { + let v = Value::from(42.0); + assert!(v.is_f64()); + assert_eq!(v.value_type(), ValueType::F64); + let v = Value::from(()); + assert!(!v.is_f64()); + } + + #[test] + fn conversions_int() { + let v = Value::from(-42); + assert!(v.is_i64()); + assert_eq!(v.value_type(), ValueType::I64); + #[cfg(feature = "128bit")] + { + let v = Value::from(-42_i128); + assert!(v.is_i64()); + assert!(v.is_i128()); + assert_eq!(v.value_type(), ValueType::I128); + } + let v = Value::from(()); + assert!(!v.is_i64()); + assert!(!v.is_i128()); + } + + #[test] + fn conversions_uint() { + let v = Value::from(42_u64); + assert!(v.is_u64()); + assert_eq!(v.value_type(), ValueType::U64); + #[cfg(feature = "128bit")] + { + let v = Value::from(42_u128); + assert!(v.is_u64()); + assert!(v.is_u128()); + assert_eq!(v.value_type(), ValueType::U128); + } + let v = Value::from(()); + assert!(!v.is_u64()); + assert!(!v.is_u128()); + } + + #[test] + fn conversions_null() { + let v = Value::from(()); + assert!(v.is_null()); + assert_eq!(v.value_type(), ValueType::Null); + let v = Value::from(1); + assert!(!v.is_null()); + } + + #[test] + fn default() { + assert_eq!(Value::default(), Value::null()); + } + + #[test] + fn mixed_int_cmp() { + assert_eq!(Value::from(1_u64), Value::from(1_i64)); + assert_eq!(Value::from(1_i64), Value::from(1_u64)); + } + + #[test] + #[cfg(feature = "128bit")] + fn mixed_int_cmp_128() { + assert_eq!(Value::from(1_u64), Value::from(1_u128)); + assert_eq!(Value::from(1_u64), Value::from(1_i128)); + assert_eq!(Value::from(1_i64), Value::from(1_u128)); + assert_eq!(Value::from(1_i64), Value::from(1_i128)); + + assert_eq!(Value::from(1_u128), Value::from(1_u128)); + assert_eq!(Value::from(1_u128), Value::from(1_i128)); + assert_eq!(Value::from(1_u128), Value::from(1_u64)); + assert_eq!(Value::from(1_u128), Value::from(1_i64)); + + assert_eq!(Value::from(1_i128), Value::from(1_u128)); + assert_eq!(Value::from(1_i128), Value::from(1_i128)); + assert_eq!(Value::from(1_i128), Value::from(1_u64)); + assert_eq!(Value::from(1_i128), Value::from(1_i64)); + } + + #[test] + fn test_union_cmp() { + let v: Value = ().into(); + assert_eq!(v, ()); + } + #[test] + #[allow(clippy::bool_assert_comparison)] + fn test_bool_cmp() { + let v: Value = true.into(); + assert_eq!(v, true); + let v: Value = false.into(); + assert_eq!(v, false); + } +} diff --git a/src/value/lazy/array.rs b/src/value/lazy/array.rs new file mode 100644 index 00000000..762c2a65 --- /dev/null +++ b/src/value/lazy/array.rs @@ -0,0 +1,142 @@ +use std::borrow::Cow; + +use super::Value; +use crate::{borrowed, tape}; + +#[derive(Clone)] +/// Wrapper around the tape that allows interacting with it via a `Array`-like API. +pub enum Array<'tape, 'value> { + /// Tape variant + Tape(tape::Array<'tape, 'value>), + /// Value variant + Value(&'tape borrowed::Array<'value>), +} + +pub enum ArrayIter<'tape, 'input> { + Tape(tape::array::Iter<'tape, 'input>), + Value(std::slice::Iter<'tape, borrowed::Value<'input>>), +} + +impl<'tape, 'input> Iterator for ArrayIter<'tape, 'input> { + type Item = Value<'tape, 'input>; + + fn next(&mut self) -> Option { + match self { + ArrayIter::Tape(t) => t.next().map(Value::Tape), + ArrayIter::Value(v) => v.next().map(Cow::Borrowed).map(Value::Value), + } + } +} + +// value_trait::Array for +impl<'tape, 'input> Array<'tape, 'input> +where + 'input: 'tape, +{ + /// FIXME: docs + + #[must_use] + pub fn get(&self, idx: usize) -> Option> { + match self { + Array::Tape(t) => t.get(idx).map(Value::Tape), + Array::Value(v) => v.get(idx).map(Cow::Borrowed).map(Value::Value), + } + } + /// FIXME: docs + #[allow(clippy::iter_without_into_iter)] + #[must_use] + pub fn iter<'i>(&'i self) -> ArrayIter<'i, 'input> { + match self { + Array::Tape(t) => ArrayIter::Tape(t.iter()), + Array::Value(v) => ArrayIter::Value(v.iter()), + } + } + + /// FIXME: docs + /// # Panics + /// if the tape is not an array + #[must_use] + pub fn len(&self) -> usize { + match self { + Array::Tape(t) => t.len(), + Array::Value(v) => v.len(), + } + } + /// FIXME: docs + #[must_use] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +// impl<'tape, 'input> IntoIterator for &Array<'tape, 'input> { +// type IntoIter = ArrayIter<'tape, 'input>; +// type Item = Value<'tape, 'input>; +// fn into_iter(self) -> Self::IntoIter { +// self.iter() +// } +// } + +#[cfg(test)] +mod test { + use crate::to_tape; + use value_trait::base::ValueAsScalar; + + #[test] + fn get_ints() -> crate::Result<()> { + let mut input = b"[1,2,3,4]".to_vec(); + let t = to_tape(input.as_mut_slice())?; + let v = t.as_value(); + let a = v.as_array().expect("is an array"); + assert_eq!(a.get(0).and_then(|v| v.as_u64()), Some(1)); + assert_eq!(a.get(1).and_then(|v| v.as_u64()), Some(2)); + assert_eq!(a.get(2).and_then(|v| v.as_u64()), Some(3)); + assert_eq!(a.get(3).and_then(|v| v.as_u64()), Some(4)); + assert_eq!(a.get(4), None); + Ok(()) + } + + #[test] + fn get_nested() -> crate::Result<()> { + let mut input = b"[1,[2,3],4]".to_vec(); + let t = to_tape(input.as_mut_slice())?; + let v = t.as_value(); + let a = v.as_array().expect("is an array"); + assert_eq!(a.get(0).and_then(|v| v.as_u64()), Some(1)); + let a1 = a.get(1).expect("has first element"); + let a2 = a1.as_array().expect("is an array"); + assert_eq!(a2.get(0).and_then(|v| v.as_u64()), Some(2)); + assert_eq!(a2.get(1).and_then(|v| v.as_u64()), Some(3)); + assert_eq!(a.get(2).and_then(|v| v.as_u64()), Some(4)); + assert_eq!(a.get(3), None); + Ok(()) + } + + #[test] + fn iter() -> crate::Result<()> { + let mut input = b"[1,2,3,4]".to_vec(); + let t = to_tape(input.as_mut_slice())?; + let v = t.as_value(); + let a = v.as_array().expect("is an array"); + let v = a + .iter() + .map(|v| v.as_u8().expect("integer")) + .collect::>(); + + assert_eq!(v, vec![1, 2, 3, 4]); + + Ok(()) + } + #[test] + fn iter_container() -> crate::Result<()> { + let mut input = b"[1,[2,3],4]".to_vec(); + let t = to_tape(input.as_mut_slice())?; + let v = t.as_value(); + let a = v.as_array().expect("is an array"); + let v = a.iter().map(|v| v.as_u8()).collect::>(); + + assert_eq!(v, vec![Some(1), None, Some(4)]); + + Ok(()) + } +} diff --git a/src/value/lazy/cmp.rs b/src/value/lazy/cmp.rs new file mode 100644 index 00000000..01f10b62 --- /dev/null +++ b/src/value/lazy/cmp.rs @@ -0,0 +1,172 @@ +use std::borrow::Borrow; +use value_trait::{base::ValueAsScalar, derived::TypedScalarValue}; + +use super::Value; + +impl<'tape, 'input> PartialEq<()> for Value<'tape, 'input> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn eq(&self, _other: &()) -> bool { + self.is_null() + } +} + +impl<'tape, 'input> PartialEq for Value<'tape, 'input> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn eq(&self, other: &bool) -> bool { + self.as_bool().map(|t| t.eq(other)).unwrap_or_default() + } +} + +impl<'tape, 'input> PartialEq for Value<'tape, 'input> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn eq(&self, other: &str) -> bool { + self.as_str().map(|t| t.eq(other)).unwrap_or_default() + } +} + +impl<'tape, 'input> PartialEq<&str> for Value<'tape, 'input> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn eq(&self, other: &&str) -> bool { + self == *other + } +} + +impl<'tape, 'input> PartialEq for Value<'tape, 'input> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn eq(&self, other: &String) -> bool { + self.as_str().map(|t| t.eq(other)).unwrap_or_default() + } +} + +impl<'tape, 'input> PartialEq for Value<'tape, 'input> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn eq(&self, other: &i8) -> bool { + self.as_i8().map(|t| t.eq(other)).unwrap_or_default() + } +} + +impl<'tape, 'input> PartialEq for Value<'tape, 'input> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn eq(&self, other: &i16) -> bool { + self.as_i16().map(|t| t.eq(other)).unwrap_or_default() + } +} + +impl<'tape, 'input> PartialEq for Value<'tape, 'input> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn eq(&self, other: &i32) -> bool { + self.as_i32().map(|t| t.eq(other)).unwrap_or_default() + } +} + +impl<'tape, 'input> PartialEq for Value<'tape, 'input> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn eq(&self, other: &i64) -> bool { + self.as_i64().map(|t| t.eq(other)).unwrap_or_default() + } +} + +impl<'tape, 'input> PartialEq for Value<'tape, 'input> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn eq(&self, other: &i128) -> bool { + self.as_i128().map(|t| t.eq(other)).unwrap_or_default() + } +} + +impl<'tape, 'input> PartialEq for Value<'tape, 'input> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn eq(&self, other: &u8) -> bool { + self.as_u8().map(|t| t.eq(other)).unwrap_or_default() + } +} + +impl<'tape, 'input> PartialEq for Value<'tape, 'input> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn eq(&self, other: &u16) -> bool { + self.as_u16().map(|t| t.eq(other)).unwrap_or_default() + } +} + +impl<'tape, 'input> PartialEq for Value<'tape, 'input> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn eq(&self, other: &u32) -> bool { + self.as_u32().map(|t| t.eq(other)).unwrap_or_default() + } +} + +impl<'tape, 'input> PartialEq for Value<'tape, 'input> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn eq(&self, other: &u64) -> bool { + self.as_u64().map(|t| t.eq(other)).unwrap_or_default() + } +} + +impl<'tape, 'input> PartialEq for Value<'tape, 'input> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn eq(&self, other: &usize) -> bool { + self.as_usize().map(|t| t.eq(other)).unwrap_or_default() + } +} + +impl<'tape, 'input> PartialEq for Value<'tape, 'input> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn eq(&self, other: &u128) -> bool { + self.as_u128().map(|t| t.eq(other)).unwrap_or_default() + } +} + +impl<'tape, 'input> PartialEq for Value<'tape, 'input> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn eq(&self, other: &f32) -> bool { + self.as_f32().map(|t| t.eq(other)).unwrap_or_default() + } +} + +impl<'tape, 'input> PartialEq for Value<'tape, 'input> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn eq(&self, other: &f64) -> bool { + self.as_f64().map(|t| t.eq(other)).unwrap_or_default() + } +} + +impl<'tape, 'input, K, T, S> PartialEq> for Value<'tape, 'input> +where + K: Borrow + std::hash::Hash + Eq, + for<'t, 'i> T: PartialEq>, + S: std::hash::BuildHasher, +{ + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn eq(&self, other: &std::collections::HashMap) -> bool { + let Some(object) = self.as_object() else { + return false; + }; + if object.len() != other.len() { + return false; + } + for (key, value) in object.iter() { + if !other.get(key).map_or(false, |v| v == &value) { + return false; + } + } + true + } +} diff --git a/src/value/lazy/from.rs b/src/value/lazy/from.rs new file mode 100644 index 00000000..96d649c5 --- /dev/null +++ b/src/value/lazy/from.rs @@ -0,0 +1,227 @@ +use super::Value; +use crate::StaticNode; +use crate::{borrowed, cow::Cow}; + +impl<'tape, 'value> From for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: StaticNode) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +impl<'tape, 'value, T> From> for Value<'tape, 'value> +where + borrowed::Value<'value>: From>, +{ + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: Option) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} +/********* str_ **********/ +impl<'tape, 'value> From<&'value str> for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: &'value str) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +#[cfg(feature = "beef")] +impl<'tape, 'value> From> for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: std::borrow::Cow<'value, str>) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +#[cfg(not(feature = "beef"))] +impl<'tape, 'value> From> for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: std::borrow::Cow<'value, str>) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +#[cfg(feature = "beef")] +impl<'tape, 'value> From> for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: beef::lean::Cow<'value, str>) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +impl<'tape, 'value> From for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: String) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +/********* atoms **********/ +impl<'tape, 'value> From for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: bool) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} +impl<'tape, 'value> From<()> for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: ()) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +/********* i_ **********/ +impl<'tape, 'value> From for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: i8) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +impl<'tape, 'value> From for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: i16) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +impl<'tape, 'value> From for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: i32) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +impl<'tape, 'value> From for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: i64) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +#[cfg(feature = "128bit")] +impl<'tape, 'value> From for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: i128) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +/********* u_ **********/ +impl<'tape, 'value> From for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: u8) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +impl<'tape, 'value> From for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: u16) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +impl<'tape, 'value> From for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: u32) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +impl<'tape, 'value> From for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: u64) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +#[cfg(feature = "128bit")] +impl<'tape, 'value> From for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: u128) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +impl<'tape, 'value> From for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: usize) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +/********* f_ **********/ +impl<'tape, 'value> From for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: f32) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +impl<'tape, 'value> From for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: f64) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +impl<'tape, 'value, S> From> for Value<'tape, 'value> +where + borrowed::Value<'value>: From>, +{ + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: Vec) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} + +impl<'tape, 'value, V: Into>> FromIterator for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from_iter>(v: I) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from_iter(v))) + } +} + +impl<'tape, 'value, K: Into>, V: Into>> + FromIterator<(K, V)> for Value<'tape, 'value> +{ + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from_iter>(v: I) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from_iter(v))) + } +} + +impl<'tape, 'value> From> for Value<'tape, 'value> { + #[cfg_attr(not(feature = "no-inline"), inline)] + #[must_use] + fn from(v: crate::borrowed::Object<'value>) -> Self { + Value::Value(Cow::Owned(borrowed::Value::from(v))) + } +} diff --git a/src/value/lazy/object.rs b/src/value/lazy/object.rs new file mode 100644 index 00000000..a228e30c --- /dev/null +++ b/src/value/lazy/object.rs @@ -0,0 +1,276 @@ +use std::{ + borrow::{Borrow, Cow}, + hash::Hash, +}; + +use super::Value; +use crate::{borrowed, tape}; + +/// Wrapper around the tape that allows interacting with it via a `Object`-like API. + +pub enum Object<'tape, 'value> { + /// Tape variant + Tape(tape::Object<'tape, 'value>), + /// Value variant + Value(&'tape borrowed::Object<'value>), +} +pub enum Iter<'tape, 'input> { + /// Tape variant + Tape(tape::object::Iter<'tape, 'input>), + /// Value variant + Value(halfbrown::Iter<'tape, Cow<'input, str>, borrowed::Value<'input>>), +} +pub enum Keys<'tape, 'input> { + /// Tape variant + Tape(tape::object::Keys<'tape, 'input>), + /// Value variant + Value(halfbrown::Keys<'tape, Cow<'input, str>, borrowed::Value<'input>>), +} +pub enum Values<'tape, 'input> { + /// Tape variant + Tape(tape::object::Values<'tape, 'input>), + /// Value variant + Value(halfbrown::Values<'tape, Cow<'input, str>, borrowed::Value<'input>>), +} + +//value_trait::Object for +impl<'tape, 'input> Object<'tape, 'input> { + /// FIXME: docs + + #[must_use] + pub fn get(&self, k: &Q) -> Option> + where + str: Borrow, + std::borrow::Cow<'input, str>: Borrow, + Q: ?Sized + Hash + Eq + Ord, + { + match self { + Object::Tape(t) => t.get(k).map(Value::Tape), + Object::Value(v) => v.get(k).map(Cow::Borrowed).map(Value::Value), + } + } + /// FIXME: docs + #[allow(clippy::iter_without_into_iter)] + #[must_use] + pub fn iter<'i>(&'i self) -> Iter<'i, 'input> { + match self { + Object::Tape(t) => Iter::Tape(t.iter()), + Object::Value(v) => Iter::Value(v.iter()), + } + } + /// FIXME: docs + #[must_use] + pub fn keys<'i>(&'i self) -> Keys<'i, 'input> { + match self { + Object::Tape(t) => Keys::Tape(t.keys()), + Object::Value(v) => Keys::Value(v.keys()), + } + } + /// FIXME: docs + #[must_use] + pub fn values<'i>(&'i self) -> Values<'i, 'input> { + match self { + Object::Tape(t) => Values::Tape(t.values()), + Object::Value(v) => Values::Value(v.values()), + } + } + /// FIXME: docs + #[must_use] + pub fn len(&self) -> usize { + match self { + Object::Tape(t) => t.len(), + Object::Value(v) => v.len(), + } + } + /// FIXME: docs + #[must_use] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +// impl<'tape, 'input> IntoIterator for &Object<'tape, 'input> { +// type IntoIter = Iter<'tape, 'input>; +// type Item = (&'input str, Value<'tape, 'input>); +// fn into_iter(self) -> Self::IntoIter { +// self.iter() +// } +// } + +impl<'tape, 'input> Iterator for Iter<'tape, 'input> { + type Item = (&'tape str, Value<'tape, 'input>); + + fn next(&mut self) -> Option { + match self { + Iter::Tape(t) => t.next().map(|(k, v)| (k, Value::Tape(v))), + Iter::Value(v) => v + .next() + .map(|(k, v)| (k.as_ref(), Value::Value(Cow::Borrowed(v)))), + } + } +} + +impl<'tape, 'input> Iterator for Keys<'tape, 'input> { + type Item = &'tape str; + fn next(&mut self) -> Option { + match self { + Keys::Tape(t) => t.next(), + Keys::Value(v) => v.next().map(std::convert::AsRef::as_ref), + } + } +} + +impl<'tape, 'input> Iterator for Values<'tape, 'input> { + type Item = Value<'tape, 'input>; + fn next(&mut self) -> Option { + match self { + Values::Tape(t) => t.next().map(Value::Tape), + Values::Value(v) => v.next().map(|v| Value::Value(Cow::Borrowed(v))), + } + } +} + +#[cfg(test)] +mod test { + use value_trait::base::ValueAsScalar; + + use crate::to_tape; + + #[test] + fn get_ints() -> crate::Result<()> { + let mut input = br#"{"snot": 1, "badger":2, "cake":3, "cookie":4}"#.to_vec(); + let t = to_tape(input.as_mut_slice())?; + let v = t.as_value(); + let a = v.as_object().expect("is an object"); + assert_eq!(a.get("snot").and_then(|v| v.as_u64()), Some(1)); + assert_eq!(a.get("badger").and_then(|v| v.as_u64()), Some(2)); + assert_eq!(a.get("cake").and_then(|v| v.as_u64()), Some(3)); + assert_eq!(a.get("cookie").and_then(|v| v.as_u64()), Some(4)); + assert_eq!(a.get("monster"), None); + Ok(()) + } + + #[test] + fn get_container() -> crate::Result<()> { + let mut input = + br#"{"snot": 1, "badger":[2, 2.5], "cake":{"frosting": 3}, "cookie":4}"#.to_vec(); + let t = to_tape(input.as_mut_slice())?; + let v = t.as_value(); + let a = v.as_object().expect("is an object"); + assert_eq!(a.get("snot").and_then(|v| v.as_u64()), Some(1)); + let badger = a.get("badger").expect("is an array"); + let badger = badger.as_array().expect("is an array"); + assert_eq!(badger.get(0).and_then(|v| v.as_u64()), Some(2)); + assert_eq!(badger.get(1).and_then(|v| v.as_f64()), Some(2.5)); + let cake = a.get("cake").expect("is an object"); + let cake = cake.as_object().expect("is an object"); + assert_eq!(cake.get("frosting").and_then(|v| v.as_u64()), Some(3)); + assert_eq!(a.get("cookie").and_then(|v| v.as_u64()), Some(4)); + assert_eq!(a.get("monster"), None); + Ok(()) + } + #[test] + fn iter_ints() -> crate::Result<()> { + let mut input = br#"{"snot": 1, "badger":2, "cake":3, "cookie":4}"#.to_vec(); + let t = to_tape(input.as_mut_slice())?; + let v = t.as_value(); + let v = v + .as_object() + .expect("is an object") + .iter() + .map(|(k, v)| (k, v.as_u64().expect("integer"))) + .collect::>(); + assert_eq!( + v, + vec![("snot", 1), ("badger", 2), ("cake", 3), ("cookie", 4)] + ); + + Ok(()) + } + + #[test] + fn keys_ints() -> crate::Result<()> { + let mut input = br#"{"snot": 1, "badger":2, "cake":3, "cookie":4}"#.to_vec(); + let t = to_tape(input.as_mut_slice())?; + let v = t.as_value(); + let v = v + .as_object() + .expect("is an object") + .keys() + .collect::>(); + assert_eq!(v, vec!["snot", "badger", "cake", "cookie"]); + + Ok(()) + } + + #[test] + fn values_ints() -> crate::Result<()> { + let mut input = br#"{"snot": 1, "badger":2, "cake":3, "cookie":4}"#.to_vec(); + let t = to_tape(input.as_mut_slice())?; + let v = t.as_value(); + let v = v + .as_object() + .expect("is an object") + .values() + .map(|v| v.as_u64().expect("integer")) + .collect::>(); + assert_eq!(v, vec![1, 2, 3, 4]); + + Ok(()) + } + #[test] + fn iter_container() -> crate::Result<()> { + let mut input = + br#"{"snot": 1, "badger":[2, 2.5], "cake":{"frosting": 3}, "cookie":4}"#.to_vec(); + let t = to_tape(input.as_mut_slice())?; + let v = t.as_value(); + let v = v + .as_object() + .expect("is an object") + .iter() + .map(|(k, v)| (k, v.as_u64())) + .collect::>(); + assert_eq!( + v, + vec![ + ("snot", Some(1)), + ("badger", None), + ("cake", None), + ("cookie", Some(4)) + ] + ); + Ok(()) + } + #[test] + fn keys_container() -> crate::Result<()> { + let mut input = + br#"{"snot": 1, "badger":[2, 2.5], "cake":{"frosting": 3}, "cookie":4}"#.to_vec(); + let t = to_tape(input.as_mut_slice())?; + let v = t.as_value(); + let v = v + .as_object() + .expect("is an object") + .keys() + .collect::>(); + assert_eq!(v, vec!["snot", "badger", "cake", "cookie"]); + + Ok(()) + } + + #[test] + fn values_container() -> crate::Result<()> { + let mut input = + br#"{"snot": 1, "badger":[2, 2.5], "cake":{"frosting": 3}, "cookie":4}"#.to_vec(); + let t = to_tape(input.as_mut_slice())?; + let v = t.as_value(); + let v = v + .as_object() + .expect("is an object") + .values() + .map(|v| v.as_u64()) + .collect::>(); + assert_eq!(v, vec![Some(1), None, None, Some(4)]); + + Ok(()) + } +} diff --git a/src/value/owned.rs b/src/value/owned.rs index 2ac8e18c..04db1000 100644 --- a/src/value/owned.rs +++ b/src/value/owned.rs @@ -397,7 +397,7 @@ mod test { #[cfg(feature = "128bit")] #[test] fn conversions_i128() { - let v = Value::from(i128::max_value()); + let v = Value::from(i128::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(!v.is_i64()); @@ -411,7 +411,7 @@ mod test { assert!(!v.is_f64()); assert!(!v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(i128::min_value()); + let v = Value::from(i128::MIN); assert!(v.is_i128()); assert!(!v.is_u128()); assert!(!v.is_i64()); @@ -428,7 +428,7 @@ mod test { } #[test] fn conversions_i64() { - let v = Value::from(i64::max_value()); + let v = Value::from(i64::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -442,7 +442,7 @@ mod test { assert!(!v.is_f64()); assert!(!v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(i64::min_value()); + let v = Value::from(i64::MIN); assert!(v.is_i128()); assert!(!v.is_u128()); assert!(v.is_i64()); @@ -460,7 +460,7 @@ mod test { #[test] fn conversions_i32() { - let v = Value::from(i32::max_value()); + let v = Value::from(i32::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -474,7 +474,7 @@ mod test { assert!(!v.is_f64()); assert!(!v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(i32::min_value()); + let v = Value::from(i32::MIN); assert!(v.is_i128()); assert!(!v.is_u128()); assert!(v.is_i64()); @@ -492,7 +492,7 @@ mod test { #[test] fn conversions_i16() { - let v = Value::from(i16::max_value()); + let v = Value::from(i16::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -506,7 +506,7 @@ mod test { assert!(!v.is_f64()); assert!(!v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(i16::min_value()); + let v = Value::from(i16::MIN); assert!(v.is_i128()); assert!(!v.is_u128()); assert!(v.is_i64()); @@ -525,7 +525,7 @@ mod test { #[test] fn conversions_i8() { - let v = Value::from(i8::max_value()); + let v = Value::from(i8::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -539,7 +539,7 @@ mod test { assert!(!v.is_f64()); assert!(!v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(i8::min_value()); + let v = Value::from(i8::MIN); assert!(v.is_i128()); assert!(!v.is_u128()); assert!(v.is_i64()); @@ -557,7 +557,7 @@ mod test { #[test] fn conversions_usize() { - let v = Value::from(usize::min_value() as u64); + let v = Value::from(usize::MIN as u64); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -579,7 +579,7 @@ mod test { #[cfg(feature = "128bit")] #[test] fn conversions_u128() { - let v = Value::from(u128::min_value()); + let v = Value::from(u128::MIN); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -596,7 +596,7 @@ mod test { } #[test] fn conversions_u64() { - let v = Value::from(u64::min_value()); + let v = Value::from(u64::MIN); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -614,7 +614,7 @@ mod test { #[test] fn conversions_u32() { - let v = Value::from(u32::max_value()); + let v = Value::from(u32::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -632,7 +632,7 @@ mod test { #[test] fn conversions_u16() { - let v = Value::from(u16::max_value()); + let v = Value::from(u16::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -650,7 +650,7 @@ mod test { #[test] fn conversions_u8() { - let v = Value::from(u8::max_value()); + let v = Value::from(u8::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -668,13 +668,13 @@ mod test { #[test] fn conversions_f64() { - let v = Value::from(std::f64::MAX); + let v = Value::from(f64::MAX); assert!(!v.is_i64()); assert!(!v.is_u64()); assert!(v.is_f64()); assert!(!v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(std::f64::MIN); + let v = Value::from(f64::MIN); assert!(!v.is_i64()); assert!(!v.is_u64()); assert!(v.is_f64()); @@ -686,13 +686,13 @@ mod test { #[test] fn conversions_f32() { - let v = Value::from(std::f32::MAX); + let v = Value::from(f32::MAX); assert!(!v.is_i64()); assert!(!v.is_u64()); assert!(v.is_f64()); assert!(v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(std::f32::MIN); + let v = Value::from(f32::MIN); assert!(!v.is_i64()); assert!(!v.is_u64()); assert!(v.is_f64()); diff --git a/src/value/tape.rs b/src/value/tape.rs index c3c0b883..f6ab7468 100644 --- a/src/value/tape.rs +++ b/src/value/tape.rs @@ -1,10 +1,10 @@ /// A tape of a parsed json, all values are extracted and validated and /// can be used without further computation. -use value_trait::StaticNode; +use value_trait::{base::TypedValue as _, StaticNode, TryTypeError, ValueType}; -mod array; +pub(super) mod array; mod cmp; -mod object; +pub(super) mod object; mod trait_impls; #[derive(Debug)] /// `Tape` @@ -38,10 +38,19 @@ impl<'input> Tape<'input> { /// Wrapper around the tape that allows interaction via a `Value`-like API. #[derive(Clone, Copy, Debug)] #[repr(transparent)] -pub struct Value<'tape, 'input>(&'tape [Node<'input>]) +pub struct Value<'tape, 'input>(pub(super) &'tape [Node<'input>]) where 'input: 'tape; +impl Value<'static, 'static> { + const NULL: [Node<'static>; 1] = [Node::Static(StaticNode::Null)]; + /// Creates tape value representing a null value + #[must_use] + pub const fn null() -> Self { + Self(&Self::NULL) + } +} + #[allow(clippy::derive_partial_eq_without_eq)] /// Tape `Node` #[derive(Debug, Clone, Copy, PartialEq)] @@ -79,8 +88,66 @@ impl<'input> Node<'input> { None } } + /// Returns the type of the node + #[must_use] + pub fn value_type(&self) -> ValueType { + match self { + Node::String(_) => ValueType::String, + Node::Object { .. } => ValueType::Object, + Node::Array { .. } => ValueType::Array, + Node::Static(v) => v.value_type(), + } + } + + // returns the count of elements in an array + fn array_count(&self) -> Result { + if let Node::Array { count, .. } = self { + Ok(*count) + } else { + Err(TryTypeError { + expected: ValueType::Array, + got: self.value_type(), + }) + } + } + + // // returns the length of an array + // fn array_len(&self) -> Result { + // if let Node::Array { len, .. } = self { + // Ok(*len) + // } else { + // Err(TryTypeError { + // expected: ValueType::Array, + // got: self.value_type(), + // }) + // } + // } + + // returns the count of nodes in an object + fn object_count(&self) -> Result { + if let Node::Object { count, .. } = self { + Ok(*count) + } else { + Err(TryTypeError { + expected: ValueType::Object, + got: self.value_type(), + }) + } + } + + // returns the count of elements in an array + fn object_len(&self) -> Result { + if let Node::Object { len, .. } = self { + Ok(*len) + } else { + Err(TryTypeError { + expected: ValueType::Object, + got: self.value_type(), + }) + } + } - // returns the count of elements in this node (n for nested, 1 for the rest) + // returns the count of elements in this node, including the node itself (n for nested, 1 for the rest) fn count(&self) -> usize { match self { // We add 1 as we need to include the header itself @@ -149,7 +216,7 @@ mod test { #[cfg(feature = "128bit")] #[test] fn conversions_i128() { - let v = Value::from(i128::max_value()); + let v = Value::from(i128::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(!v.is_i64()); @@ -163,7 +230,7 @@ mod test { assert!(!v.is_f64()); assert!(!v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(i128::min_value()); + let v = Value::from(i128::MIN); assert!(v.is_i128()); assert!(!v.is_u128()); assert!(!v.is_i64()); @@ -181,7 +248,7 @@ mod test { #[test] fn conversions_i64() { - let v = Value::from(i64::max_value()); + let v = Value::from(i64::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -195,7 +262,7 @@ mod test { assert!(!v.is_f64()); assert!(!v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(i64::min_value()); + let v = Value::from(i64::MIN); assert!(v.is_i128()); assert!(!v.is_u128()); assert!(v.is_i64()); @@ -213,7 +280,7 @@ mod test { #[test] fn conversions_i32() { - let v = Value::from(i32::max_value()); + let v = Value::from(i32::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -227,7 +294,7 @@ mod test { assert!(!v.is_f64()); assert!(!v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(i32::min_value()); + let v = Value::from(i32::MIN); assert!(v.is_i128()); assert!(!v.is_u128()); assert!(v.is_i64()); @@ -245,7 +312,7 @@ mod test { #[test] fn conversions_i16() { - let v = Value::from(i16::max_value()); + let v = Value::from(i16::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -259,7 +326,7 @@ mod test { assert!(!v.is_f64()); assert!(!v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(i16::min_value()); + let v = Value::from(i16::MIN); assert!(v.is_i128()); assert!(!v.is_u128()); assert!(v.is_i64()); @@ -278,7 +345,7 @@ mod test { #[test] fn conversions_i8() { - let v = Value::from(i8::max_value()); + let v = Value::from(i8::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -292,7 +359,7 @@ mod test { assert!(!v.is_f64()); assert!(!v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(i8::min_value()); + let v = Value::from(i8::MIN); assert!(v.is_i128()); assert!(!v.is_u128()); assert!(v.is_i64()); @@ -310,7 +377,7 @@ mod test { #[test] fn conversions_usize() { - let v = Value::from(usize::min_value() as u64); + let v = Value::from(usize::MIN as u64); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -332,7 +399,7 @@ mod test { #[cfg(feature = "128bit")] #[test] fn conversions_u128() { - let v = Value::from(u128::min_value()); + let v = Value::from(u128::MIN); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -350,7 +417,7 @@ mod test { #[test] fn conversions_u64() { - let v = Value::from(u64::min_value()); + let v = Value::from(u64::MIN); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -368,7 +435,7 @@ mod test { #[test] fn conversions_u32() { - let v = Value::from(u32::max_value()); + let v = Value::from(u32::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -386,7 +453,7 @@ mod test { #[test] fn conversions_u16() { - let v = Value::from(u16::max_value()); + let v = Value::from(u16::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -404,7 +471,7 @@ mod test { #[test] fn conversions_u8() { - let v = Value::from(u8::max_value()); + let v = Value::from(u8::MAX); assert!(v.is_i128()); assert!(v.is_u128()); assert!(v.is_i64()); @@ -422,13 +489,13 @@ mod test { #[test] fn conversions_f64() { - let v = Value::from(std::f64::MAX); + let v = Value::from(f64::MAX); assert!(!v.is_i64()); assert!(!v.is_u64()); assert!(v.is_f64()); assert!(!v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(std::f64::MIN); + let v = Value::from(f64::MIN); assert!(!v.is_i64()); assert!(!v.is_u64()); assert!(v.is_f64()); @@ -440,13 +507,13 @@ mod test { #[test] fn conversions_f32() { - let v = Value::from(std::f32::MAX); + let v = Value::from(f32::MAX); assert!(!v.is_i64()); assert!(!v.is_u64()); assert!(v.is_f64()); assert!(v.is_f32()); assert!(v.is_f64_castable()); - let v = Value::from(std::f32::MIN); + let v = Value::from(f32::MIN); assert!(!v.is_i64()); assert!(!v.is_u64()); assert!(v.is_f64()); diff --git a/src/value/tape/array.rs b/src/value/tape/array.rs index e7c683e1..46b18424 100644 --- a/src/value/tape/array.rs +++ b/src/value/tape/array.rs @@ -5,9 +5,9 @@ use crate::Node; /// Wrapper around the tape that allows interacting with it via a `Array`-like API. pub struct Array<'tape, 'input>(pub(super) &'tape [Node<'input>]); -pub struct ArrayIter<'tape, 'input>(&'tape [Node<'input>]); +pub struct Iter<'tape, 'input>(&'tape [Node<'input>]); -impl<'tape, 'input> Iterator for ArrayIter<'tape, 'input> { +impl<'tape, 'input> Iterator for Iter<'tape, 'input> { type Item = Value<'tape, 'input>; fn next(&mut self) -> Option { @@ -36,8 +36,8 @@ where } /// FIXME: docs #[must_use] - pub fn iter<'i>(&'i self) -> ArrayIter<'tape, 'input> { - ArrayIter(&self.0[1..]) + pub fn iter<'i>(&'i self) -> Iter<'tape, 'input> { + Iter(&self.0[1..]) } /// FIXME: docs @@ -59,7 +59,7 @@ where } impl<'tape, 'input> IntoIterator for &Array<'tape, 'input> { - type IntoIter = ArrayIter<'tape, 'input>; + type IntoIter = Iter<'tape, 'input>; type Item = Value<'tape, 'input>; fn into_iter(self) -> Self::IntoIter { self.iter() diff --git a/src/value/tape/cmp.rs b/src/value/tape/cmp.rs index 475d461b..d79b8e00 100644 --- a/src/value/tape/cmp.rs +++ b/src/value/tape/cmp.rs @@ -171,22 +171,23 @@ impl<'tape, 'input> PartialEq for Value<'tape, 'input> { impl<'tape, 'input, K, T, S> PartialEq> for Value<'tape, 'input> where K: Borrow + std::hash::Hash + Eq, - T: PartialEq>, + for<'i> T: PartialEq>, S: std::hash::BuildHasher, { #[cfg_attr(not(feature = "no-inline"), inline)] #[must_use] fn eq(&self, other: &std::collections::HashMap) -> bool { - self.as_object().map_or(false, |object| { - if object.len() != other.len() { + let Some(object) = self.as_object() else { + return false; + }; + if object.len() != other.len() { + return false; + } + for (key, value) in &object { + if !other.get(key).map_or(false, |v| v == &value) { return false; } - for (key, value) in &object { - if !other.get(key).map_or(false, |v| v == &value) { - return false; - } - } - true - }) + } + true } } diff --git a/src/value/tape/object.rs b/src/value/tape/object.rs index b52caa9c..83d9989c 100644 --- a/src/value/tape/object.rs +++ b/src/value/tape/object.rs @@ -7,9 +7,9 @@ use crate::Node; pub struct Object<'tape, 'input>(pub(super) &'tape [Node<'input>]); -pub struct ObjectIter<'tape, 'input>(&'tape [Node<'input>]); -pub struct ObjectKeys<'tape, 'input>(&'tape [Node<'input>]); -pub struct ObjectValues<'tape, 'input>(&'tape [Node<'input>]); +pub struct Iter<'tape, 'input>(&'tape [Node<'input>]); +pub struct Keys<'tape, 'input>(&'tape [Node<'input>]); +pub struct Values<'tape, 'input>(&'tape [Node<'input>]); //value_trait::Object for impl<'tape, 'input> Object<'tape, 'input> { @@ -40,18 +40,18 @@ impl<'tape, 'input> Object<'tape, 'input> { } /// FIXME: docs #[must_use] - pub fn iter<'i>(&'i self) -> ObjectIter<'tape, 'input> { - ObjectIter(&self.0[1..]) + pub fn iter<'i>(&'i self) -> Iter<'tape, 'input> { + Iter(&self.0[1..]) } /// FIXME: docs #[must_use] - pub fn keys<'i>(&'i self) -> ObjectKeys<'tape, 'input> { - ObjectKeys(&self.0[1..]) + pub fn keys<'i>(&'i self) -> Keys<'tape, 'input> { + Keys(&self.0[1..]) } /// FIXME: docs #[must_use] - pub fn values<'i>(&'i self) -> ObjectValues<'tape, 'input> { - ObjectValues(&self.0[1..]) + pub fn values<'i>(&'i self) -> Values<'tape, 'input> { + Values(&self.0[1..]) } /// FIXME: docs #[must_use] @@ -69,14 +69,14 @@ impl<'tape, 'input> Object<'tape, 'input> { } impl<'tape, 'input> IntoIterator for &Object<'tape, 'input> { - type IntoIter = ObjectIter<'tape, 'input>; + type IntoIter = Iter<'tape, 'input>; type Item = (&'input str, Value<'tape, 'input>); fn into_iter(self) -> Self::IntoIter { self.iter() } } -impl<'tape, 'input> Iterator for ObjectIter<'tape, 'input> { +impl<'tape, 'input> Iterator for Iter<'tape, 'input> { type Item = (&'input str, Value<'tape, 'input>); fn next(&mut self) -> Option { @@ -89,7 +89,7 @@ impl<'tape, 'input> Iterator for ObjectIter<'tape, 'input> { } } -impl<'tape, 'input> Iterator for ObjectKeys<'tape, 'input> { +impl<'tape, 'input> Iterator for Keys<'tape, 'input> { type Item = &'input str; fn next(&mut self) -> Option { let (k, v) = self.0.split_first()?; @@ -101,7 +101,7 @@ impl<'tape, 'input> Iterator for ObjectKeys<'tape, 'input> { } } -impl<'tape, 'input> Iterator for ObjectValues<'tape, 'input> { +impl<'tape, 'input> Iterator for Values<'tape, 'input> { type Item = Value<'tape, 'input>; fn next(&mut self) -> Option { let (_, v) = self.0.split_first()?; @@ -140,16 +140,12 @@ mod test { let v = t.as_value(); let a = v.as_object().expect("is an object"); assert_eq!(a.get("snot").and_then(|v| v.as_u64()), Some(1)); - let badger = a - .get("badger") - .and_then(|v| v.as_array()) - .expect("is an array"); + let badger = a.get("badger").expect("is an array"); + let badger = badger.as_array().expect("is an array"); assert_eq!(badger.get(0).and_then(|v| v.as_u64()), Some(2)); assert_eq!(badger.get(1).and_then(|v| v.as_f64()), Some(2.5)); - let cake = a - .get("cake") - .and_then(|v| v.as_object()) - .expect("is an object"); + let cake = a.get("cake").expect("is an object"); + let cake = cake.as_object().expect("is an object"); assert_eq!(cake.get("frosting").and_then(|v| v.as_u64()), Some(3)); assert_eq!(a.get("cookie").and_then(|v| v.as_u64()), Some(4)); assert_eq!(a.get("monster"), None); diff --git a/src/value/tape/trait_impls.rs b/src/value/tape/trait_impls.rs index f1a72763..d58bab3a 100644 --- a/src/value/tape/trait_impls.rs +++ b/src/value/tape/trait_impls.rs @@ -97,12 +97,24 @@ impl<'tape, 'input> ValueIntoContainer for Value<'tape, 'input> { type Object = Object<'tape, 'input>; #[must_use] fn into_array(self) -> Option { - self.as_array() + if let Some(Node::Array { count, .. }) = self.0.first() { + // we add one element as we want to keep the array header + let count = *count + 1; + Some(Array(&self.0[..count])) + } else { + None + } } #[must_use] fn into_object(self) -> Option { - self.as_object() + if let Some(Node::Object { count, .. }) = self.0.first() { + // we add one element as we want to keep the object header + let count = *count + 1; + Some(Object(&self.0[..count])) + } else { + None + } } } @@ -136,7 +148,7 @@ impl<'tape, 'input> Value<'tape, 'input> { /// current Value isn't an Object, returns `None` if the key isn't in the object /// # Errors /// if the value is not an object - pub fn try_get(&self, k: &Q) -> Result>, TryTypeError> + pub fn try_get(&self, k: &Q) -> Result>, TryTypeError> where str: Borrow + Hash + Eq, Q: ?Sized + Hash + Eq + Ord, @@ -154,7 +166,7 @@ where /// current value isn't an Array, returns `None` if the index is out of bounds /// # Errors /// if the requested type doesn't match the actual type or the value is not an object - pub fn try_get_idx(&self, i: usize) -> Result>, TryTypeError> { + pub fn try_get_idx(&self, i: usize) -> Result>, TryTypeError> { Ok(self.try_as_array()?.get(i)) } } @@ -166,7 +178,7 @@ where { /// Tries to represent the value as an array and returns a reference to it #[must_use] - pub fn as_array(&self) -> Option> { + pub fn as_array(&self) -> Option> { if let Some(Node::Array { count, .. }) = self.0.first() { // we add one element as we want to keep the array header let count = *count + 1; @@ -178,7 +190,7 @@ where /// Tries to represent the value as an array and returns a reference to it #[must_use] - pub fn as_object(&self) -> Option> { + pub fn as_object(&self) -> Option> { if let Some(Node::Object { count, .. }) = self.0.first() { // we add one element as we want to keep the object header let count = *count + 1; @@ -194,7 +206,7 @@ impl<'tape, 'input> Value<'tape, 'input> { /// Tries to represent the value as an array and returns a reference to it /// # Errors /// if the requested type doesn't match the actual type - pub fn try_as_array(&self) -> Result, TryTypeError> { + pub fn try_as_array(&self) -> Result, TryTypeError> { self.as_array().ok_or(TryTypeError { expected: ValueType::Array, got: self.value_type(), @@ -204,7 +216,7 @@ impl<'tape, 'input> Value<'tape, 'input> { /// Tries to represent the value as an object and returns a reference to it /// # Errors /// if the requested type doesn't match the actual type - pub fn try_as_object(&self) -> Result, TryTypeError> { + pub fn try_as_object(&self) -> Result, TryTypeError> { self.as_object().ok_or(TryTypeError { expected: ValueType::Object, got: self.value_type(), @@ -217,7 +229,7 @@ impl<'tape, 'input> Value<'tape, 'input> { /// current Value isn't an Object or doesn't contain the key /// it was asked for. #[must_use] - pub fn get(&self, k: &Q) -> Option> + pub fn get(&self, k: &Q) -> Option> where str: Borrow + Hash + Eq, Q: ?Sized + Hash + Eq + Ord, @@ -239,7 +251,7 @@ impl<'tape, 'input> Value<'tape, 'input> { /// current Value isn't an Array or doesn't contain the index /// it was asked for. #[must_use] - pub fn get_idx(&self, i: usize) -> Option> { + pub fn get_idx(&self, i: usize) -> Option> { self.as_array().and_then(|a| a.get(i)) } } @@ -393,23 +405,54 @@ where impl<'tape, 'input> Value<'tape, 'input> { /// Tries to get an element of an object as a array #[must_use] - pub fn get_array(&self, k: &Q) -> Option> + pub fn get_array(&self, k: &Q) -> Option> where str: Borrow + Hash + Eq, Q: ?Sized + Hash + Eq + Ord, { - let v = self.get(k)?; - v.as_array() + let mut len = self.0[0].object_len().ok()?; + let mut idx = 1; + while len > 0 { + let Some(s) = self.0[idx].as_str() else { + unreachable!() + }; + idx += 1; + len -= 1; + let count = self.0[idx].count(); + let s: &Q = s.borrow(); + if s == k { + let count: usize = self.0[idx].array_count().ok()?; + return Some(Array(&self.0[idx..idx + count])); + } + idx += count; + } + None } /// Tries to get an element of an object as a object #[must_use] - pub fn get_object(&self, k: &Q) -> Option> + pub fn get_object(&self, k: &Q) -> Option> where str: Borrow + Hash + Eq, Q: ?Sized + Hash + Eq + Ord, { - self.get(k).and_then(|v| v.as_object()) + let mut len = self.0[0].object_len().ok()?; + let mut idx = 1; + while len > 0 { + let Some(s) = self.0[idx].as_str() else { + unreachable!() + }; + idx += 1; + len -= 1; + let count = self.0[idx].count(); + let s: &Q = s.borrow(); + if s == k { + let count: usize = self.0[idx].object_count().ok()?; + return Some(Object(&self.0[idx..idx + count])); + } + idx += count; + } + None } } // TryValueObjectContainerAccess @@ -423,10 +466,23 @@ impl<'tape, 'input> Value<'tape, 'input> { str: Borrow + Hash + Eq, Q: ?Sized + Hash + Eq + Ord, { - self.try_as_object()? - .get(k) - .map(|v| v.try_as_array()) - .transpose() + let mut len = self.0[0].object_len()?; + let mut idx = 1; + while len > 0 { + let Some(s) = self.0[idx].as_str() else { + unreachable!() + }; + idx += 1; + len -= 1; + let count = self.0[idx].count(); + let s: &Q = s.borrow(); + if s == k { + let count: usize = self.0[idx].array_count()?; + return Ok(Some(Array(&self.0[idx..idx + count]))); + } + idx += count; + } + Ok(None) } /// Tries to get an element of an object as an object, returns @@ -434,15 +490,28 @@ impl<'tape, 'input> Value<'tape, 'input> { /// /// # Errors /// if the requested type doesn't match the actual type or the value is not an object - pub fn try_get_object(&self, k: &Q) -> Result>, TryTypeError> + pub fn try_get_object(&self, k: &Q) -> Result>, TryTypeError> where str: Borrow + Hash + Eq, Q: ?Sized + Hash + Eq + Ord, { - self.try_as_object()? - .get(k) - .map(|v| v.try_as_object()) - .transpose() + let mut len = self.0[0].object_len()?; + let mut idx = 1; + while len > 0 { + let Some(s) = self.0[idx].as_str() else { + unreachable!() + }; + idx += 1; + len -= 1; + let count = self.0[idx].count(); + let s: &Q = s.borrow(); + if s == k { + let count: usize = self.0[idx].object_count()?; + return Ok(Some(Object(&self.0[idx..idx + count]))); + } + idx += count; + } + Ok(None) } }