#![allow(clippy::float_cmp)]
use std::cmp::Ordering;
use std::collections::{BTreeMap, HashMap};
use std::convert::{TryFrom, TryInto};
use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::iter::FromIterator;
use std::mem;
use std::ops::{Deref, Index, IndexMut};
use std::ptr::NonNull;
#[cfg(feature = "indexmap")]
use indexmap::IndexMap;
pub(crate) mod array;
mod bigint;
#[cfg(feature = "arbitrary_precision")]
pub(crate) mod decimal;
pub(crate) mod inline;
pub(crate) mod interned;
mod numeric;
pub(crate) mod object;
pub(crate) mod scalar;
#[cfg(feature = "arbitrary_precision")]
pub(crate) use numeric::canonicalise;
pub(crate) use numeric::{decimal_to_f64_exact, decimal_to_f64_lossy, NumVal};
pub(crate) use numeric::{DECIMAL_MAX_MAGNITUDE, DECIMAL_MIN_EXP};
use inline::InlineNumber;
use crate::array::IArray;
use crate::number::INumber;
use crate::object::IObject;
use crate::string::IString;
#[repr(transparent)]
pub struct IValue {
ptr: NonNull<u8>,
}
const _: () = assert!(
mem::size_of::<IValue>() == mem::size_of::<usize>()
&& mem::align_of::<IValue>() == mem::align_of::<usize>(),
"IValue must be exactly one pointer-sized, pointer-aligned word",
);
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Destructured {
Null,
Bool(bool),
Number(INumber),
String(IString),
Array(IArray),
Object(IObject),
}
impl Destructured {
#[must_use]
pub fn as_ref<'a>(&'a self) -> DestructuredRef<'a> {
use DestructuredRef::{Array, Bool, Null, Number, Object, String};
match self {
Self::Null => Null,
Self::Bool(b) => Bool(*b),
Self::Number(v) => Number(v),
Self::String(v) => String(v),
Self::Array(v) => Array(v),
Self::Object(v) => Object(v),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum DestructuredRef<'a> {
Null,
Bool(bool),
Number(&'a INumber),
String(&'a IString),
Array(&'a IArray),
Object(&'a IObject),
}
#[derive(Debug)]
pub enum DestructuredMut<'a> {
Null,
Bool(BoolMut<'a>),
Number(&'a mut INumber),
String(&'a mut IString),
Array(&'a mut IArray),
Object(&'a mut IObject),
}
#[derive(Debug)]
pub struct BoolMut<'a>(&'a mut IValue);
impl BoolMut<'_> {
pub fn set(&mut self, value: bool) {
*self.0 = value.into();
}
#[must_use]
pub fn get(&self) -> bool {
self.0.is_true()
}
}
impl Deref for BoolMut<'_> {
type Target = bool;
fn deref(&self) -> &bool {
if self.get() {
&true
} else {
&false
}
}
}
const ALIGNMENT: usize = 8;
#[repr(usize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum ReprTag {
Inline = 0,
NumberI64 = 1,
NumberU64 = 2,
NumberF64 = 3,
#[cfg_attr(not(feature = "arbitrary_precision"), allow(dead_code))]
NumberDecimal = 4,
String = 5,
Array = 6,
Object = 7,
}
impl From<usize> for ReprTag {
fn from(other: usize) -> Self {
unsafe { mem::transmute(other % ALIGNMENT) }
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ValueType {
Null,
Bool,
Number,
String,
Array,
Object,
}
unsafe impl Send for IValue {}
unsafe impl Sync for IValue {}
unsafe trait TransparentIValue {}
unsafe impl TransparentIValue for INumber {}
unsafe impl TransparentIValue for IString {}
unsafe impl TransparentIValue for IArray {}
unsafe impl TransparentIValue for IObject {}
impl IValue {
const unsafe fn new_usize(tag: ReprTag, payload: usize) -> Self {
Self {
ptr: NonNull::new_unchecked(std::ptr::without_provenance_mut(tag as usize | payload)),
}
}
unsafe fn new_ptr(tag: ReprTag, p: NonNull<u8>) -> Self {
Self {
ptr: p.add(tag as usize),
}
}
pub const NULL: Self = unsafe { Self::new_usize(ReprTag::Inline, inline::NULL) };
pub const FALSE: Self = unsafe { Self::new_usize(ReprTag::Inline, inline::FALSE) };
pub const TRUE: Self = unsafe { Self::new_usize(ReprTag::Inline, inline::TRUE) };
fn usize_(&self) -> usize {
self.ptr.as_ptr().addr() & !(ALIGNMENT - 1)
}
unsafe fn ptr(&self) -> NonNull<u8> {
self.ptr.offset(-(self.repr_tag() as usize as isize))
}
unsafe fn set_ptr(&mut self, ptr: NonNull<u8>) {
let tag = self.repr_tag();
self.ptr = ptr.add(tag as usize);
}
unsafe fn set_usize(&mut self, word: usize) {
let word = word | self.repr_tag() as usize;
self.ptr = NonNull::new_unchecked(std::ptr::without_provenance_mut(word));
}
unsafe fn raw_copy(&self) -> Self {
Self { ptr: self.ptr }
}
pub(crate) fn raw_eq(&self, other: &Self) -> bool {
self.ptr == other.ptr
}
pub(crate) fn raw_hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.ptr.hash(state);
}
pub(crate) fn repr_tag(&self) -> ReprTag {
self.ptr.as_ptr().addr().into()
}
#[cfg(test)]
pub(crate) fn is_inline(&self) -> bool {
self.repr_tag() == ReprTag::Inline
}
#[must_use]
pub fn type_(&self) -> ValueType {
self.repr_tag().with(|r| r.value_type(self))
}
#[must_use]
pub fn destructure(self) -> Destructured {
self.repr_tag().with(move |r| r.destructure(self))
}
#[must_use]
pub fn destructure_ref<'a>(&'a self) -> DestructuredRef<'a> {
self.repr_tag().with(|r| unsafe { r.destructure_ref(self) })
}
pub fn destructure_mut<'a>(&'a mut self) -> DestructuredMut<'a> {
self.repr_tag()
.with(move |r| unsafe { r.destructure_mut(self) })
}
pub fn get(&self, index: impl ValueIndex) -> Option<&IValue> {
index.index_into(self)
}
pub fn get_mut(&mut self, index: impl ValueIndex) -> Option<&mut IValue> {
index.index_into_mut(self)
}
pub fn remove(&mut self, index: impl ValueIndex) -> Option<IValue> {
index.remove(self)
}
pub fn take(&mut self) -> IValue {
mem::replace(self, IValue::NULL)
}
#[must_use]
pub fn len(&self) -> Option<usize> {
self.repr_tag().with(|r| unsafe { r.len(self) })
}
#[must_use]
pub fn is_empty(&self) -> Option<bool> {
self.len().map(|len| len == 0)
}
#[must_use]
pub fn is_null(&self) -> bool {
self.type_() == ValueType::Null
}
#[must_use]
pub fn is_bool(&self) -> bool {
self.type_() == ValueType::Bool
}
#[must_use]
pub fn is_true(&self) -> bool {
self.raw_eq(&Self::TRUE)
}
#[must_use]
pub fn is_false(&self) -> bool {
self.raw_eq(&Self::FALSE)
}
#[must_use]
pub fn to_bool(&self) -> Option<bool> {
self.is_bool().then(|| self.is_true())
}
#[must_use]
pub fn is_number(&self) -> bool {
self.type_() == ValueType::Number
}
unsafe fn unchecked_cast_ref<T: TransparentIValue>(&self) -> &T {
&*(self as *const Self).cast::<T>()
}
unsafe fn unchecked_cast_mut<T: TransparentIValue>(&mut self) -> &mut T {
&mut *(self as *mut Self).cast::<T>()
}
unsafe fn as_number_unchecked(&self) -> &INumber {
self.unchecked_cast_ref()
}
unsafe fn as_number_unchecked_mut(&mut self) -> &mut INumber {
self.unchecked_cast_mut()
}
#[must_use]
pub fn as_number(&self) -> Option<&INumber> {
if self.is_number() {
Some(unsafe { self.as_number_unchecked() })
} else {
None
}
}
pub fn as_number_mut(&mut self) -> Option<&mut INumber> {
if self.is_number() {
Some(unsafe { self.as_number_unchecked_mut() })
} else {
None
}
}
pub fn into_number(self) -> Result<INumber, IValue> {
if self.is_number() {
Ok(INumber(self))
} else {
Err(self)
}
}
#[must_use]
pub fn to_i64(&self) -> Option<i64> {
self.repr_tag().with(|r| unsafe { r.to_i64(self) })
}
#[must_use]
pub fn to_u64(&self) -> Option<u64> {
self.repr_tag().with(|r| unsafe { r.to_u64(self) })
}
#[must_use]
pub fn to_f64(&self) -> Option<f64> {
self.repr_tag().with(|r| unsafe { r.to_f64(self) })
}
#[must_use]
pub fn to_f32(&self) -> Option<f32> {
self.to_f64().and_then(|x| {
let u = x as f32;
(f64::from(u) == x).then_some(u)
})
}
#[must_use]
pub fn to_i32(&self) -> Option<i32> {
self.to_i64().and_then(|x| x.try_into().ok())
}
#[must_use]
pub fn to_u32(&self) -> Option<u32> {
self.to_u64().and_then(|x| x.try_into().ok())
}
#[must_use]
pub fn to_isize(&self) -> Option<isize> {
self.to_i64().and_then(|x| x.try_into().ok())
}
#[must_use]
pub fn to_usize(&self) -> Option<usize> {
self.to_u64().and_then(|x| x.try_into().ok())
}
#[must_use]
pub fn to_f64_lossy(&self) -> Option<f64> {
self.repr_tag().with(|r| unsafe { r.to_f64_lossy(self) })
}
#[must_use]
pub fn to_f32_lossy(&self) -> Option<f32> {
self.to_f64_lossy().map(|x| x as f32)
}
#[must_use]
pub fn is_string(&self) -> bool {
self.type_() == ValueType::String
}
unsafe fn as_string_unchecked(&self) -> &IString {
self.unchecked_cast_ref()
}
unsafe fn as_string_unchecked_mut(&mut self) -> &mut IString {
self.unchecked_cast_mut()
}
#[must_use]
pub fn as_string(&self) -> Option<&IString> {
if self.is_string() {
Some(unsafe { self.as_string_unchecked() })
} else {
None
}
}
pub fn as_string_mut(&mut self) -> Option<&mut IString> {
if self.is_string() {
Some(unsafe { self.as_string_unchecked_mut() })
} else {
None
}
}
pub fn into_string(self) -> Result<IString, IValue> {
if self.is_string() {
Ok(IString(self))
} else {
Err(self)
}
}
#[must_use]
pub fn is_array(&self) -> bool {
self.type_() == ValueType::Array
}
unsafe fn as_array_unchecked(&self) -> &IArray {
self.unchecked_cast_ref()
}
unsafe fn as_array_unchecked_mut(&mut self) -> &mut IArray {
self.unchecked_cast_mut()
}
#[must_use]
pub fn as_array(&self) -> Option<&IArray> {
if self.is_array() {
Some(unsafe { self.as_array_unchecked() })
} else {
None
}
}
pub fn as_array_mut(&mut self) -> Option<&mut IArray> {
if self.is_array() {
Some(unsafe { self.as_array_unchecked_mut() })
} else {
None
}
}
pub fn into_array(self) -> Result<IArray, IValue> {
if self.is_array() {
Ok(IArray(self))
} else {
Err(self)
}
}
#[must_use]
pub fn is_object(&self) -> bool {
self.type_() == ValueType::Object
}
unsafe fn as_object_unchecked(&self) -> &IObject {
self.unchecked_cast_ref()
}
unsafe fn as_object_unchecked_mut(&mut self) -> &mut IObject {
self.unchecked_cast_mut()
}
#[must_use]
pub fn as_object(&self) -> Option<&IObject> {
if self.is_object() {
Some(unsafe { self.as_object_unchecked() })
} else {
None
}
}
pub fn as_object_mut(&mut self) -> Option<&mut IObject> {
if self.is_object() {
Some(unsafe { self.as_object_unchecked_mut() })
} else {
None
}
}
pub fn into_object(self) -> Result<IObject, IValue> {
if self.is_object() {
Ok(IObject(self))
} else {
Err(self)
}
}
}
pub(crate) fn number_cmp(a: NumVal<'_>, b: &IValue) -> Option<Ordering> {
b.num_val().map(|b| a.cmp(b))
}
pub(crate) fn string_cmp(a: &IValue, b: &IValue) -> Ordering {
debug_assert!(
a.type_() == ValueType::String && b.type_() == ValueType::String,
"string_cmp requires two strings",
);
if a.raw_eq(b) {
Ordering::Equal
} else {
a.as_str()
.expect("a string")
.cmp(b.as_str().expect("a string"))
}
}
pub(crate) fn string_debug(v: &IValue, f: &mut Formatter<'_>) -> fmt::Result {
Debug::fmt(v.as_str().expect("a string"), f)
}
impl IValue {
pub(crate) fn new_i64(value: i64) -> Self {
inline::InlineNumberRepr::from_i64(value).unwrap_or_else(|| scalar::I64Repr::store(value))
}
pub(crate) fn new_u64(value: u64) -> Self {
inline::InlineNumberRepr::from_u64(value).unwrap_or_else(|| match i64::try_from(value) {
Ok(v) => scalar::I64Repr::store(v),
Err(_) => scalar::U64Repr::store(value),
})
}
pub(crate) fn new_f64(value: f64) -> Option<Self> {
value.is_finite().then(|| {
inline::InlineNumberRepr::from_f64(value)
.unwrap_or_else(|| scalar::F64Repr::store(value))
})
}
#[cfg(feature = "arbitrary_precision")]
pub(crate) fn new_decimal(
negative: bool,
digits: &[u8],
exp: i32,
has_decimal_point: bool,
) -> Self {
let c = canonicalise(negative, digits, exp);
if let Some(nv) = c.small {
if has_decimal_point {
if let Some(f) = nv.to_f64() {
return Self::new_f64(f).expect("an exact `f64` is finite");
}
} else {
if let Some(i) = nv.to_i64() {
return Self::new_i64(i);
}
if let Some(u) = nv.to_u64() {
return Self::new_u64(u);
}
}
}
decimal::DecimalRepr::store(c.negative, &c.magnitude, c.exp, has_decimal_point)
}
pub(crate) unsafe fn new_inline_number(bits: usize) -> Self {
Self::new_usize(ReprTag::Inline, bits)
}
pub(crate) fn has_decimal_point(&self) -> bool {
self.repr_tag().with(|r| r.has_decimal_point(self))
}
pub(crate) fn num_val(&self) -> Option<NumVal<'_>> {
self.repr_tag().with(|r| unsafe { r.num_val(self) })
}
#[cfg(feature = "arbitrary_precision")]
pub(crate) fn exact_json(&self) -> Option<String> {
self.num_val()
.and_then(|n| n.exact_json(self.has_decimal_point()))
}
}
impl IValue {
pub(crate) fn new_string(s: &str) -> Self {
match inline::string::InlineStringRepr::try_encode(s) {
Some(bits) => unsafe { Self::new_usize(ReprTag::Inline, bits) },
None => unsafe { Self::new_ptr(ReprTag::String, interned::InternedRepr::intern(s)) },
}
}
}
#[cfg(test)]
impl IValue {
pub(crate) fn number_repr_key(&self) -> (u8, u64) {
let tag = self.repr_tag() as u8;
if self.is_inline() {
(tag, self.usize_() as u64)
} else {
(tag, unsafe { scalar::read::<u64>(self.ptr()) })
}
}
}
pub(crate) trait ValueRepr {
fn value_type(&self, v: &IValue) -> ValueType;
unsafe fn clone(&self, v: &IValue) -> IValue;
unsafe fn drop(&self, v: &mut IValue);
unsafe fn hash(&self, v: &IValue, state: &mut dyn Hasher) {
state.write_usize(v.usize_());
}
unsafe fn eq(&self, a: &IValue, b: &IValue) -> bool {
a.raw_eq(b)
}
unsafe fn partial_cmp(&self, _a: &IValue, _b: &IValue) -> Option<Ordering> {
None
}
unsafe fn debug(&self, v: &IValue, f: &mut Formatter<'_>) -> fmt::Result;
fn destructure(&self, v: IValue) -> Destructured;
unsafe fn destructure_ref<'a>(&self, v: &'a IValue) -> DestructuredRef<'a>;
unsafe fn destructure_mut<'a>(&self, v: &'a mut IValue) -> DestructuredMut<'a>;
unsafe fn len(&self, _v: &IValue) -> Option<usize> {
None
}
unsafe fn num_val<'a>(&self, _v: &'a IValue) -> Option<NumVal<'a>> {
None
}
fn has_decimal_point(&self, _v: &IValue) -> bool {
false
}
unsafe fn to_i64(&self, v: &IValue) -> Option<i64> {
self.num_val(v).and_then(|n| n.to_i64())
}
unsafe fn to_u64(&self, v: &IValue) -> Option<u64> {
self.num_val(v).and_then(|n| n.to_u64())
}
unsafe fn to_f64(&self, v: &IValue) -> Option<f64> {
self.num_val(v).and_then(|n| n.to_f64())
}
unsafe fn to_f64_lossy(&self, v: &IValue) -> Option<f64> {
self.num_val(v).map(|n| n.to_f64_lossy())
}
unsafe fn as_bytes<'a>(&self, _v: &'a IValue) -> Option<&'a [u8]> {
None
}
unsafe fn as_str<'a>(&self, v: &'a IValue) -> Option<&'a str> {
self.as_bytes(v)
.map(|b| unsafe { std::str::from_utf8_unchecked(b) })
}
}
impl ReprTag {
#[inline]
fn with<R>(self, f: impl FnOnce(&'static dyn ValueRepr) -> R) -> R {
match self {
ReprTag::Inline => f(&inline::InlineRepr),
ReprTag::NumberI64 => f(&scalar::I64Repr),
ReprTag::NumberU64 => f(&scalar::U64Repr),
#[cfg(feature = "arbitrary_precision")]
ReprTag::NumberDecimal => f(&decimal::DecimalRepr),
#[cfg(not(feature = "arbitrary_precision"))]
ReprTag::NumberDecimal => {
debug_assert!(false, "the decimal tag requires `arbitrary_precision`");
unsafe { std::hint::unreachable_unchecked() }
}
ReprTag::NumberF64 => f(&scalar::F64Repr),
ReprTag::String => f(&interned::InternedRepr),
ReprTag::Array => f(&array::ArrayRepr),
ReprTag::Object => f(&object::ObjectRepr),
}
}
}
impl IValue {
#[inline]
pub(crate) fn as_str(&self) -> Option<&str> {
self.repr_tag().with(|r| unsafe { r.as_str(self) })
}
}
impl Clone for IValue {
fn clone(&self) -> Self {
self.repr_tag().with(|r| unsafe { r.clone(self) })
}
}
impl Drop for IValue {
fn drop(&mut self) {
self.repr_tag().with(|r| unsafe { r.drop(self) })
}
}
impl Hash for IValue {
fn hash<H: Hasher>(&self, state: &mut H) {
self.repr_tag().with(|r| unsafe { r.hash(self, state) })
}
}
impl PartialEq for IValue {
fn eq(&self, other: &Self) -> bool {
self.type_() == other.type_() && self.repr_tag().with(|r| unsafe { r.eq(self, other) })
}
}
impl Eq for IValue {}
impl PartialOrd for IValue {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let (t1, t2) = (self.type_(), other.type_());
if t1 == t2 {
self.repr_tag()
.with(|r| unsafe { r.partial_cmp(self, other) })
} else {
t1.partial_cmp(&t2)
}
}
}
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 ValueIndex: private::Sealed + Copy {
#[doc(hidden)]
fn index_into(self, v: &IValue) -> Option<&IValue>;
#[doc(hidden)]
fn index_into_mut(self, v: &mut IValue) -> Option<&mut IValue>;
#[doc(hidden)]
fn index_or_insert(self, v: &mut IValue) -> &mut IValue;
#[doc(hidden)]
fn remove(self, v: &mut IValue) -> Option<IValue>;
}
impl ValueIndex for usize {
fn index_into(self, v: &IValue) -> Option<&IValue> {
v.as_array().unwrap().get(self)
}
fn index_into_mut(self, v: &mut IValue) -> Option<&mut IValue> {
v.as_array_mut().unwrap().get_mut(self)
}
fn index_or_insert(self, v: &mut IValue) -> &mut IValue {
self.index_into_mut(v).unwrap()
}
fn remove(self, v: &mut IValue) -> Option<IValue> {
v.as_array_mut().unwrap().remove(self)
}
}
impl ValueIndex for &str {
fn index_into(self, v: &IValue) -> Option<&IValue> {
v.as_object().unwrap().get(&IString::intern(self))
}
fn index_into_mut(self, v: &mut IValue) -> Option<&mut IValue> {
v.as_object_mut().unwrap().get_mut(&IString::intern(self))
}
fn index_or_insert(self, v: &mut IValue) -> &mut IValue {
&mut v.as_object_mut().unwrap()[self]
}
fn remove(self, v: &mut IValue) -> Option<IValue> {
v.as_object_mut().unwrap().remove(self)
}
}
impl ValueIndex for &IString {
fn index_into(self, v: &IValue) -> Option<&IValue> {
v.as_object().unwrap().get(self)
}
fn index_into_mut(self, v: &mut IValue) -> Option<&mut IValue> {
v.as_object_mut().unwrap().get_mut(self)
}
fn index_or_insert(self, v: &mut IValue) -> &mut IValue {
&mut v.as_object_mut().unwrap()[self]
}
fn remove(self, v: &mut IValue) -> Option<IValue> {
v.as_object_mut().unwrap().remove(self)
}
}
impl<T: ValueIndex> ValueIndex for &T {
fn index_into(self, v: &IValue) -> Option<&IValue> {
(*self).index_into(v)
}
fn index_into_mut(self, v: &mut IValue) -> Option<&mut IValue> {
(*self).index_into_mut(v)
}
fn index_or_insert(self, v: &mut IValue) -> &mut IValue {
(*self).index_or_insert(v)
}
fn remove(self, v: &mut IValue) -> Option<IValue> {
(*self).remove(v)
}
}
impl<I: ValueIndex> Index<I> for IValue {
type Output = IValue;
#[inline]
fn index(&self, index: I) -> &IValue {
index.index_into(self).unwrap()
}
}
impl<I: ValueIndex> IndexMut<I> for IValue {
#[inline]
fn index_mut(&mut self, index: I) -> &mut IValue {
index.index_or_insert(self)
}
}
impl Debug for IValue {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.repr_tag().with(|r| unsafe { r.debug(self, f) })
}
}
impl<T: Into<IValue>> From<Option<T>> for IValue {
fn from(other: Option<T>) -> Self {
if let Some(v) = other {
v.into()
} else {
Self::NULL
}
}
}
impl From<bool> for IValue {
fn from(other: bool) -> Self {
if other {
Self::TRUE
} else {
Self::FALSE
}
}
}
typed_conversions! {
INumber: i8, u8, i16, u16, i32, u32, i64, u64, isize, usize;
IString: String, &String, &mut String, &str, &mut str;
IArray:
Vec<T> where (T: Into<IValue>),
&[T] where (T: Into<IValue> + Clone);
IObject:
HashMap<K, V> where (K: Into<IString>, V: Into<IValue>),
BTreeMap<K, V> where (K: Into<IString>, V: Into<IValue>);
}
#[cfg(feature = "indexmap")]
typed_conversions! {
IObject:
IndexMap<K, V> where (K: Into<IString>, V: Into<IValue>);
}
impl From<f32> for IValue {
fn from(v: f32) -> Self {
INumber::try_from(v)
.map(Into::into)
.unwrap_or_else(|()| IValue::NULL)
}
}
impl From<f64> for IValue {
fn from(v: f64) -> Self {
INumber::try_from(v)
.map(Into::into)
.unwrap_or_else(|()| IValue::NULL)
}
}
impl<T: Into<IValue>> FromIterator<T> for IValue {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
IArray::from_iter(iter).into()
}
}
impl<K: Into<IString>, V: Into<IValue>> FromIterator<(K, V)> for IValue {
fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
IObject::from_iter(iter).into()
}
}
impl From<serde_json::Value> for IValue {
fn from(other: serde_json::Value) -> Self {
match other {
serde_json::Value::Null => IValue::NULL,
serde_json::Value::Bool(b) => b.into(),
serde_json::Value::Number(n) => INumber::from(n).into(),
serde_json::Value::String(s) => s.into(),
serde_json::Value::Array(a) => a.into_iter().collect(),
serde_json::Value::Object(o) => IObject::from(o).into(),
}
}
}
impl From<IValue> for serde_json::Value {
fn from(other: IValue) -> Self {
match other.destructure() {
Destructured::Null => serde_json::Value::Null,
Destructured::Bool(b) => serde_json::Value::Bool(b),
Destructured::Number(n) => serde_json::Value::Number(n.into()),
Destructured::String(s) => serde_json::Value::String(s.as_str().to_owned()),
Destructured::Array(a) => {
serde_json::Value::Array(a.into_iter().map(Into::into).collect())
}
Destructured::Object(o) => serde_json::Value::Object(o.into()),
}
}
}
impl Default for IValue {
fn default() -> Self {
Self::NULL
}
}
#[cfg(test)]
mod tests {
use super::*;
#[mockalloc::test]
fn can_use_literal() {
let x: IValue = ijson!({
"foo": "bar",
"x": [],
"y": ["hi", "there", 1, 2, null, false, true, 63.5],
"z": [false, {
"a": null
}, {}]
});
let y: IValue = serde_json::from_str(
r#"{
"foo": "bar",
"x": [],
"y": ["hi", "there", 1, 2, null, false, true, 63.5],
"z": [false, {
"a": null
}, {}]
}"#,
)
.unwrap();
assert_eq!(x, y);
}
#[test]
#[allow(clippy::redundant_clone)]
fn test_null() {
let x: IValue = IValue::NULL;
assert!(x.is_null());
assert_eq!(x.type_(), ValueType::Null);
assert!(matches!(x.clone().destructure(), Destructured::Null));
assert!(matches!(x.clone().destructure_ref(), DestructuredRef::Null));
assert!(matches!(x.clone().destructure_mut(), DestructuredMut::Null));
}
#[test]
fn test_bool() {
for v in [true, false].iter().copied() {
let mut x = IValue::from(v);
assert!(x.is_bool());
assert_eq!(x.type_(), ValueType::Bool);
assert_eq!(x.to_bool(), Some(v));
assert!(matches!(x.clone().destructure(), Destructured::Bool(u) if u == v));
assert!(matches!(x.clone().destructure_ref(), DestructuredRef::Bool(u) if u == v));
assert!(
matches!(x.clone().destructure_mut(), DestructuredMut::Bool(u) if u.get() == v)
);
if let DestructuredMut::Bool(mut b) = x.destructure_mut() {
b.set(!v);
}
assert_eq!(x.to_bool(), Some(!v));
}
}
#[test]
fn test_number() {
for v in 300..400 {
let mut x = IValue::from(v);
assert!(x.is_number());
assert_eq!(x.type_(), ValueType::Number);
assert_eq!(x.to_i32(), Some(v));
assert_eq!(x.to_u32(), Some(v as u32));
assert_eq!(x.to_i64(), Some(i64::from(v)));
assert_eq!(x.to_u64(), Some(v as u64));
assert_eq!(x.to_isize(), Some(v as isize));
assert_eq!(x.to_usize(), Some(v as usize));
assert_eq!(x.as_number(), Some(&v.into()));
assert_eq!(x.as_number_mut(), Some(&mut v.into()));
assert!(matches!(x.clone().destructure(), Destructured::Number(u) if u == v.into()));
assert!(
matches!(x.clone().destructure_ref(), DestructuredRef::Number(u) if *u == v.into())
);
assert!(
matches!(x.clone().destructure_mut(), DestructuredMut::Number(u) if *u == v.into())
);
}
}
#[mockalloc::test]
fn test_string() {
for v in 0..10 {
let s = v.to_string();
let mut x = IValue::from(&s);
assert!(x.is_string());
assert_eq!(x.type_(), ValueType::String);
assert_eq!(x.as_string(), Some(&IString::intern(&s)));
assert_eq!(x.as_string_mut(), Some(&mut IString::intern(&s)));
assert!(matches!(x.clone().destructure(), Destructured::String(u) if u == s));
assert!(matches!(x.clone().destructure_ref(), DestructuredRef::String(u) if *u == s));
assert!(matches!(x.clone().destructure_mut(), DestructuredMut::String(u) if *u == s));
}
}
#[mockalloc::test]
fn test_array() {
for v in 0..10 {
let mut a: IArray = (0..v).collect();
let mut x = IValue::from(a.clone());
assert!(x.is_array());
assert_eq!(x.type_(), ValueType::Array);
assert_eq!(x.as_array(), Some(&a));
assert_eq!(x.as_array_mut(), Some(&mut a));
assert!(matches!(x.clone().destructure(), Destructured::Array(u) if u == a));
assert!(matches!(x.clone().destructure_ref(), DestructuredRef::Array(u) if *u == a));
assert!(matches!(x.clone().destructure_mut(), DestructuredMut::Array(u) if *u == a));
}
}
#[mockalloc::test]
fn test_object() {
for v in 0..10 {
let mut o: IObject = (0..v).map(|i| (i.to_string(), i)).collect();
let mut x = IValue::from(o.clone());
assert!(x.is_object());
assert_eq!(x.type_(), ValueType::Object);
assert_eq!(x.as_object(), Some(&o));
assert_eq!(x.as_object_mut(), Some(&mut o));
assert!(matches!(x.clone().destructure(), Destructured::Object(u) if u == o));
assert!(matches!(x.clone().destructure_ref(), DestructuredRef::Object(u) if *u == o));
assert!(matches!(x.clone().destructure_mut(), DestructuredMut::Object(u) if *u == o));
}
}
#[mockalloc::test]
fn test_into_object_for_object() {
let o: IObject = (0..10).map(|i| (i.to_string(), i)).collect();
let x = IValue::from(o.clone());
assert_eq!(x.into_object(), Ok(o));
}
#[mockalloc::test]
fn test_from_iter_array() {
let x: IValue = (0..5).collect();
let y: IValue = ijson!([0, 1, 2, 3, 4]);
assert_eq!(x, y);
let empty: IValue = std::iter::empty::<i32>().collect();
assert_eq!(empty, ijson!([]));
}
#[mockalloc::test]
fn test_from_iter_object() {
let x: IValue = (0..3).map(|i| (i.to_string(), i)).collect();
let y: IValue = ijson!({"0": 0, "1": 1, "2": 2});
assert_eq!(x, y);
let empty: IValue = std::iter::empty::<(String, i32)>().collect();
assert_eq!(empty, ijson!({}));
}
#[mockalloc::test]
fn test_serde_json_roundtrip() {
let json = serde_json::json!({
"null": null,
"bool": true,
"int": 42,
"neg": -17,
"big": 18446744073709551615u64,
"float": 63.5,
"string": "hello",
"array": [1, 2, 3, "four", false, null],
"object": {"nested": [1.5, {"deep": true}]}
});
let ivalue: IValue = json.clone().into();
let back: serde_json::Value = ivalue.clone().into();
assert_eq!(json, back);
let via_serde: IValue = crate::to_value(&json).unwrap();
assert_eq!(ivalue, via_serde);
}
#[test]
fn compares_across_types_without_panicking() {
let vals: Vec<IValue> = vec![
IValue::NULL,
true.into(),
5_i64.into(),
(u64::MAX).into(), 5.0_f64.into(), 10_000_000_000_i64.into(), "hello".into(),
vec![IValue::from(1)].into(),
];
for a in &vals {
for b in &vals {
let _ = a == b;
let _ = a.partial_cmp(b);
}
}
assert_eq!(IValue::from(5_i64), IValue::from(5.0_f64));
assert_eq!(
IValue::from(5_i64).partial_cmp(&IValue::from(5.0_f64)),
Some(Ordering::Equal)
);
}
#[test]
fn compares_numbers_across_representations() {
use crate::INumber;
let ints: &[i64] = &[
0,
5,
-5,
1,
-1,
i64::MIN,
i64::MAX,
10_000_000_000,
-10_000_000_000,
];
let mut nums: Vec<INumber> = ints.iter().map(|&x| x.into()).collect();
nums.extend([u64::MAX.into(), (i64::MAX as u64 + 1).into()]);
for &f in &[
0.0_f64,
5.0,
5.5,
-5.5,
0.1,
-0.1,
1e18,
9.2e18,
f64::MIN_POSITIVE,
f64::MAX,
] {
nums.push(f.try_into().unwrap());
}
for a in &nums {
for b in &nums {
assert_eq!(a.cmp(b), b.cmp(a).reverse(), "{:?} vs {:?}", a, b);
}
}
}
}
#[cfg(codegen_probes)]
pub mod codegen_probes {
use super::{inline, ReprTag};
use crate::number::INumber;
#[inline(always)]
pub unsafe fn assume_inline(n: &INumber) {
unsafe {
std::hint::assert_unchecked(n.0.repr_tag() == ReprTag::Inline);
std::hint::assert_unchecked(inline::is_inline_number(&n.0));
}
}
#[inline(always)]
pub unsafe fn assume_inline_integer(n: &INumber) {
unsafe {
assume_inline(n);
inline::assume_inline_integer(&n.0);
}
}
}