#![allow(clippy::manual_map)]
#[cfg(test)]
#[path = "./value_tests.rs"]
mod tests;
pub(crate) mod array;
pub(crate) mod table;
#[cfg(feature = "to-toml")]
mod to_toml;
use crate::arena::Arena;
use crate::error::{Error, ErrorKind};
use crate::item::table::TableIndex;
use crate::{DateTime, Span, Table};
use std::fmt;
use std::mem::ManuallyDrop;
pub use array::Array;
pub(crate) use array::InternalArray;
use table::InnerTable;
pub(crate) const TAG_MASK: u32 = 0x7;
pub(crate) const TAG_SHIFT: u32 = 3;
pub(crate) const TAG_STRING: u32 = 0;
pub(crate) const TAG_INTEGER: u32 = 1;
pub(crate) const TAG_FLOAT: u32 = 2;
pub(crate) const TAG_BOOLEAN: u32 = 3;
pub(crate) const TAG_DATETIME: u32 = 4;
pub(crate) const TAG_TABLE: u32 = 5;
pub(crate) const TAG_ARRAY: u32 = 6;
pub(crate) const TAG_NONE: u32 = 7;
pub(crate) const FLAG_MASK: u32 = 0x7;
pub(crate) const FLAG_SHIFT: u32 = 3;
pub(crate) const FLAG_NONE: u32 = 0;
pub(crate) const FLAG_ARRAY: u32 = 2;
pub(crate) const FLAG_AOT: u32 = 3;
pub(crate) const FLAG_TABLE: u32 = 4;
pub(crate) const FLAG_DOTTED: u32 = 5;
pub(crate) const FLAG_HEADER: u32 = 6;
pub(crate) const FLAG_FROZEN: u32 = 7;
pub(crate) const HINTS_BIT: u32 = 1 << 31;
pub(crate) const AUTO_STYLE_BIT: u32 = 1 << 26;
const NOT_PROJECTED: u32 = !(TAG_MASK);
#[derive(Copy, Clone)]
#[repr(C)]
pub struct ItemMetadata {
pub(crate) start_and_tag: u32,
pub(crate) end_and_flag: u32,
}
impl ItemMetadata {
#[inline]
pub(crate) fn spanned(tag: u32, flag: u32, start: u32, end: u32) -> Self {
Self {
start_and_tag: (start << TAG_SHIFT) | tag,
end_and_flag: (end << FLAG_SHIFT) | flag,
}
}
#[inline]
pub(crate) fn hints(tag: u32, flag: u32) -> Self {
Self {
start_and_tag: NOT_PROJECTED | tag,
end_and_flag: HINTS_BIT | flag,
}
}
#[inline]
pub(crate) fn tag(&self) -> u32 {
self.start_and_tag & TAG_MASK
}
#[inline]
pub(crate) fn flag(&self) -> u32 {
self.end_and_flag & FLAG_MASK
}
#[inline]
pub(crate) fn set_flag(&mut self, flag: u32) {
self.end_and_flag = (self.end_and_flag & !FLAG_MASK) | flag;
}
#[inline]
pub(crate) fn set_auto_style(&mut self) {
self.end_and_flag |= AUTO_STYLE_BIT;
}
#[inline]
#[allow(dead_code)]
pub(crate) fn is_auto_style(&self) -> bool {
self.end_and_flag & (HINTS_BIT | AUTO_STYLE_BIT) == (HINTS_BIT | AUTO_STYLE_BIT)
}
#[inline]
pub(crate) fn clear_auto_style(&mut self) {
self.end_and_flag &= !AUTO_STYLE_BIT;
}
#[inline]
pub(crate) fn is_span_mode(&self) -> bool {
(self.end_and_flag as i32) >= 0
}
#[inline]
pub fn span(&self) -> Span {
if (self.end_and_flag as i32) >= 0 {
self.span_unchecked()
} else {
Span { start: 0, end: 0 }
}
}
#[inline]
pub(crate) fn span_unchecked(&self) -> Span {
debug_assert!(self.is_span_mode());
Span::new(
self.start_and_tag >> TAG_SHIFT,
self.end_and_flag >> FLAG_SHIFT,
)
}
#[inline]
pub(crate) fn span_start(&self) -> u32 {
debug_assert!(self.is_span_mode());
self.start_and_tag >> TAG_SHIFT
}
#[inline]
pub(crate) fn set_span_start(&mut self, v: u32) {
debug_assert!(self.is_span_mode());
self.start_and_tag = (v << TAG_SHIFT) | (self.start_and_tag & TAG_MASK);
}
#[inline]
pub(crate) fn set_span_end(&mut self, v: u32) {
debug_assert!(self.is_span_mode());
self.end_and_flag = (v << FLAG_SHIFT) | (self.end_and_flag & FLAG_MASK);
}
#[inline]
pub(crate) fn extend_span_end(&mut self, new_end: u32) {
debug_assert!(self.is_span_mode());
let old = self.end_and_flag;
let current = old >> FLAG_SHIFT;
self.end_and_flag = (current.max(new_end) << FLAG_SHIFT) | (old & FLAG_MASK);
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TableStyle {
Implicit,
Dotted,
Header,
Inline,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ArrayStyle {
Inline,
Header,
}
#[repr(C, packed)]
#[derive(Clone, Copy)]
struct PackedI128 {
value: i128,
}
#[repr(align(8))]
#[derive(Clone, Copy)]
pub struct Integer {
value: PackedI128,
}
impl Integer {
#[inline]
pub fn as_i128(&self) -> i128 {
let copy = *self;
copy.value.value
}
#[inline]
pub fn as_f64(&self) -> f64 {
let copy = *self;
copy.value.value as f64
}
#[inline]
pub fn as_i64(&self) -> Option<i64> {
i64::try_from(self.as_i128()).ok()
}
#[inline]
pub fn as_u64(&self) -> Option<u64> {
u64::try_from(self.as_i128()).ok()
}
}
impl From<i128> for Integer {
#[inline]
fn from(v: i128) -> Self {
Self {
value: PackedI128 { value: v },
}
}
}
impl From<i64> for Integer {
#[inline]
fn from(v: i64) -> Self {
Self::from(v as i128)
}
}
impl From<u64> for Integer {
#[inline]
fn from(v: u64) -> Self {
Self::from(v as i128)
}
}
impl From<i32> for Integer {
#[inline]
fn from(v: i32) -> Self {
Self::from(v as i128)
}
}
impl From<u32> for Integer {
#[inline]
fn from(v: u32) -> Self {
Self::from(v as i128)
}
}
impl PartialEq for Integer {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_i128() == other.as_i128()
}
}
impl Eq for Integer {}
impl fmt::Debug for Integer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_i128().fmt(f)
}
}
impl fmt::Display for Integer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_i128().fmt(f)
}
}
#[repr(C, align(8))]
union Payload<'de> {
string: &'de str,
integer: Integer,
float: f64,
boolean: bool,
array: ManuallyDrop<InternalArray<'de>>,
table: ManuallyDrop<InnerTable<'de>>,
datetime: DateTime,
}
#[repr(C)]
pub struct Item<'de> {
payload: Payload<'de>,
pub(crate) meta: ItemMetadata,
}
const _: () = assert!(std::mem::size_of::<Item<'_>>() == 24);
const _: () = assert!(std::mem::align_of::<Item<'_>>() == 8);
impl<'de> From<i64> for Item<'de> {
fn from(value: i64) -> Self {
Self::raw_hints(
TAG_INTEGER,
FLAG_NONE,
Payload {
integer: Integer::from(value),
},
)
}
}
impl<'de> From<i128> for Item<'de> {
fn from(value: i128) -> Self {
Self::raw_hints(
TAG_INTEGER,
FLAG_NONE,
Payload {
integer: Integer::from(value),
},
)
}
}
impl<'de> From<i32> for Item<'de> {
fn from(value: i32) -> Self {
Self::from(value as i64)
}
}
impl<'de> From<&'de str> for Item<'de> {
fn from(value: &'de str) -> Self {
Self::raw_hints(TAG_STRING, FLAG_NONE, Payload { string: value })
}
}
impl<'de> From<f64> for Item<'de> {
fn from(value: f64) -> Self {
Self::raw_hints(TAG_FLOAT, FLAG_NONE, Payload { float: value })
}
}
impl<'de> From<bool> for Item<'de> {
fn from(value: bool) -> Self {
Self::raw_hints(TAG_BOOLEAN, FLAG_NONE, Payload { boolean: value })
}
}
impl<'de> From<DateTime> for Item<'de> {
fn from(value: DateTime) -> Self {
Self::raw_hints(TAG_DATETIME, FLAG_NONE, Payload { datetime: value })
}
}
impl<'de> Item<'de> {
#[inline]
fn raw(tag: u32, flag: u32, start: u32, end: u32, payload: Payload<'de>) -> Self {
Self {
meta: ItemMetadata::spanned(tag, flag, start, end),
payload,
}
}
#[inline]
fn raw_hints(tag: u32, flag: u32, payload: Payload<'de>) -> Self {
Self {
meta: ItemMetadata::hints(tag, flag),
payload,
}
}
#[inline]
pub fn string(s: &'de str) -> Self {
Self::raw_hints(TAG_STRING, FLAG_NONE, Payload { string: s })
}
#[inline]
pub(crate) fn string_spanned(s: &'de str, span: Span) -> Self {
Self::raw(
TAG_STRING,
FLAG_NONE,
span.start,
span.end,
Payload { string: s },
)
}
#[inline]
pub(crate) fn integer_spanned(i: i128, span: Span) -> Self {
Self::raw(
TAG_INTEGER,
FLAG_NONE,
span.start,
span.end,
Payload {
integer: Integer::from(i),
},
)
}
#[inline]
pub(crate) fn float_spanned(f: f64, span: Span) -> Self {
Self::raw(
TAG_FLOAT,
FLAG_NONE,
span.start,
span.end,
Payload { float: f },
)
}
#[inline]
pub(crate) fn boolean(b: bool, span: Span) -> Self {
Self::raw(
TAG_BOOLEAN,
FLAG_NONE,
span.start,
span.end,
Payload { boolean: b },
)
}
#[inline]
pub(crate) fn array(a: InternalArray<'de>, span: Span) -> Self {
Self::raw(
TAG_ARRAY,
FLAG_ARRAY,
span.start,
span.end,
Payload {
array: ManuallyDrop::new(a),
},
)
}
#[inline]
pub(crate) fn table(t: InnerTable<'de>, span: Span) -> Self {
Self::raw(
TAG_TABLE,
FLAG_TABLE,
span.start,
span.end,
Payload {
table: ManuallyDrop::new(t),
},
)
}
#[inline]
pub(crate) fn array_aot(a: InternalArray<'de>, span: Span) -> Self {
Self::raw(
TAG_ARRAY,
FLAG_AOT,
span.start,
span.end,
Payload {
array: ManuallyDrop::new(a),
},
)
}
#[inline]
pub(crate) fn table_frozen(t: InnerTable<'de>, span: Span) -> Self {
Self::raw(
TAG_TABLE,
FLAG_FROZEN,
span.start,
span.end,
Payload {
table: ManuallyDrop::new(t),
},
)
}
#[inline]
pub(crate) fn table_header(t: InnerTable<'de>, span: Span) -> Self {
Self::raw(
TAG_TABLE,
FLAG_HEADER,
span.start,
span.end,
Payload {
table: ManuallyDrop::new(t),
},
)
}
#[inline]
pub(crate) fn table_dotted(t: InnerTable<'de>, span: Span) -> Self {
Self::raw(
TAG_TABLE,
FLAG_DOTTED,
span.start,
span.end,
Payload {
table: ManuallyDrop::new(t),
},
)
}
#[inline]
pub(crate) fn moment(m: DateTime, span: Span) -> Self {
Self::raw(
TAG_DATETIME,
FLAG_NONE,
span.start,
span.end,
Payload { datetime: m },
)
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
#[allow(unused)]
pub enum Kind {
String = 0,
Integer = 1,
Float = 2,
Boolean = 3,
DateTime = 4,
Table = 5,
Array = 6,
}
impl std::fmt::Debug for Kind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.as_str().fmt(f)
}
}
impl std::fmt::Display for Kind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.as_str().fmt(f)
}
}
impl Kind {
pub fn as_str(&self) -> &'static str {
match self {
Kind::String => "string",
Kind::Integer => "integer",
Kind::Float => "float",
Kind::Boolean => "boolean",
Kind::Array => "array",
Kind::Table => "table",
Kind::DateTime => "datetime",
}
}
}
impl<'de> Item<'de> {
#[inline]
pub fn kind(&self) -> Kind {
debug_assert!((self.meta.start_and_tag & TAG_MASK) as u8 <= Kind::Array as u8);
unsafe { std::mem::transmute::<u8, Kind>(self.meta.start_and_tag as u8 & 0x7) }
}
#[inline]
pub(crate) fn tag(&self) -> u32 {
self.meta.tag()
}
#[inline]
pub(crate) fn is_scalar(&self) -> bool {
self.tag() < TAG_TABLE
}
#[inline]
pub fn flag(&self) -> u32 {
self.meta.flag()
}
#[inline]
pub(crate) fn span_unchecked(&self) -> Span {
self.meta.span_unchecked()
}
#[inline]
pub fn span(&self) -> Span {
self.meta.span()
}
#[inline]
pub fn type_str(&self) -> &'static &'static str {
match self.kind() {
Kind::String => &"string",
Kind::Integer => &"integer",
Kind::Float => &"float",
Kind::Boolean => &"boolean",
Kind::Array => &"array",
Kind::Table => &"table",
Kind::DateTime => &"datetime",
}
}
#[inline]
pub(crate) fn is_table(&self) -> bool {
self.flag() >= FLAG_TABLE
}
#[inline]
pub(crate) fn is_array(&self) -> bool {
self.flag() & 6 == 2
}
#[inline]
pub(crate) fn is_frozen(&self) -> bool {
self.flag() == FLAG_FROZEN
}
#[inline]
pub(crate) fn is_aot(&self) -> bool {
self.flag() == FLAG_AOT
}
#[inline]
pub(crate) fn has_header_bit(&self) -> bool {
self.flag() == FLAG_HEADER
}
#[inline]
pub(crate) fn has_dotted_bit(&self) -> bool {
self.flag() == FLAG_DOTTED
}
#[inline]
pub(crate) fn is_implicit_table(&self) -> bool {
self.flag() == FLAG_TABLE
}
#[inline]
pub(crate) unsafe fn split_array_end_flag(&mut self) -> (&mut u32, &mut InternalArray<'de>) {
debug_assert!(self.is_array());
let ptr = self as *mut Item<'de>;
unsafe {
let end_flag = &mut *std::ptr::addr_of_mut!((*ptr).meta.end_and_flag);
let array =
&mut *std::ptr::addr_of_mut!((*ptr).payload.array).cast::<InternalArray<'de>>();
(end_flag, array)
}
}
}
#[derive(Debug)]
pub enum Value<'a, 'de> {
String(&'a &'de str),
Integer(&'a Integer),
Float(&'a f64),
Boolean(&'a bool),
DateTime(&'a DateTime),
Table(&'a Table<'de>),
Array(&'a Array<'de>),
}
pub enum ValueMut<'a, 'de> {
String(&'a mut &'de str),
Integer(&'a mut Integer),
Float(&'a mut f64),
Boolean(&'a mut bool),
DateTime(&'a DateTime),
Table(&'a mut Table<'de>),
Array(&'a mut Array<'de>),
}
impl<'de> Item<'de> {
pub fn value(&self) -> Value<'_, 'de> {
unsafe {
match self.kind() {
Kind::String => Value::String(&self.payload.string),
Kind::Integer => Value::Integer(&self.payload.integer),
Kind::Float => Value::Float(&self.payload.float),
Kind::Boolean => Value::Boolean(&self.payload.boolean),
Kind::Array => Value::Array(self.as_array_unchecked()),
Kind::Table => Value::Table(self.as_table_unchecked()),
Kind::DateTime => Value::DateTime(&self.payload.datetime),
}
}
}
pub fn value_mut(&mut self) -> ValueMut<'_, 'de> {
unsafe {
match self.kind() {
Kind::String => ValueMut::String(&mut self.payload.string),
Kind::Integer => ValueMut::Integer(&mut self.payload.integer),
Kind::Float => ValueMut::Float(&mut self.payload.float),
Kind::Boolean => ValueMut::Boolean(&mut self.payload.boolean),
Kind::Array => ValueMut::Array(self.as_array_mut_unchecked()),
Kind::Table => ValueMut::Table(self.as_table_mut_unchecked()),
Kind::DateTime => ValueMut::DateTime(&self.payload.datetime),
}
}
}
}
impl<'de> Item<'de> {
#[inline]
pub fn as_str(&self) -> Option<&str> {
if self.tag() == TAG_STRING {
Some(unsafe { self.payload.string })
} else {
None
}
}
#[doc(hidden)]
pub fn with_style_of_array_or_table(mut self, style: TableStyle) -> Item<'de> {
match self.value_mut() {
ValueMut::Table(table) => table.set_style(style),
ValueMut::Array(array) => match style {
TableStyle::Header => array.set_style(ArrayStyle::Header),
TableStyle::Inline => array.set_style(ArrayStyle::Inline),
_ => (),
},
_ => (),
}
self
}
#[inline]
pub fn as_i128(&self) -> Option<i128> {
if self.tag() == TAG_INTEGER {
Some(unsafe { self.payload.integer.as_i128() })
} else {
None
}
}
#[inline]
pub fn as_i64(&self) -> Option<i64> {
if self.tag() == TAG_INTEGER {
unsafe { self.payload.integer.as_i64() }
} else {
None
}
}
#[inline]
pub fn as_u64(&self) -> Option<u64> {
if self.tag() == TAG_INTEGER {
unsafe { self.payload.integer.as_u64() }
} else {
None
}
}
#[inline]
pub fn as_f64(&self) -> Option<f64> {
match self.value() {
Value::Float(f) => Some(*f),
Value::Integer(i) => Some(i.as_i128() as f64),
_ => None,
}
}
#[inline]
pub fn as_bool(&self) -> Option<bool> {
if self.tag() == TAG_BOOLEAN {
Some(unsafe { self.payload.boolean })
} else {
None
}
}
#[inline]
pub fn as_array(&self) -> Option<&Array<'de>> {
if self.tag() == TAG_ARRAY {
Some(unsafe { self.as_array_unchecked() })
} else {
None
}
}
#[inline]
pub fn as_table(&self) -> Option<&Table<'de>> {
if self.is_table() {
Some(unsafe { self.as_table_unchecked() })
} else {
None
}
}
#[inline]
pub fn as_datetime(&self) -> Option<&DateTime> {
if self.tag() == TAG_DATETIME {
Some(unsafe { &self.payload.datetime })
} else {
None
}
}
#[inline]
pub fn as_array_mut(&mut self) -> Option<&mut Array<'de>> {
if self.tag() == TAG_ARRAY {
Some(unsafe { self.as_array_mut_unchecked() })
} else {
None
}
}
#[inline]
pub fn into_table(self) -> Option<Table<'de>> {
if self.is_table() {
Some(unsafe { std::mem::transmute::<Item<'de>, Table<'de>>(self) })
} else {
None
}
}
#[inline]
pub fn as_table_mut(&mut self) -> Option<&mut Table<'de>> {
if self.is_table() {
Some(unsafe { self.as_table_mut_unchecked() })
} else {
None
}
}
#[inline]
pub(crate) unsafe fn as_array_unchecked(&self) -> &Array<'de> {
debug_assert!(self.tag() == TAG_ARRAY);
unsafe { &*(self as *const Item<'de>).cast::<Array<'de>>() }
}
#[inline]
pub(crate) unsafe fn as_array_mut_unchecked(&mut self) -> &mut Array<'de> {
debug_assert!(self.tag() == TAG_ARRAY);
unsafe { &mut *(self as *mut Item<'de>).cast::<Array<'de>>() }
}
#[inline]
pub(crate) unsafe fn as_inner_table_mut_unchecked(&mut self) -> &mut InnerTable<'de> {
debug_assert!(self.is_table());
unsafe { &mut self.payload.table }
}
#[inline]
pub(crate) unsafe fn as_table_mut_unchecked(&mut self) -> &mut Table<'de> {
debug_assert!(self.is_table());
unsafe { &mut *(self as *mut Item<'de>).cast::<Table<'de>>() }
}
#[inline]
pub(crate) unsafe fn as_table_unchecked(&self) -> &Table<'de> {
debug_assert!(self.is_table());
unsafe { &*(self as *const Item<'de>).cast::<Table<'de>>() }
}
#[inline]
pub fn has_keys(&self) -> bool {
self.as_table().is_some_and(|t| !t.is_empty())
}
#[inline]
pub fn has_key(&self, key: &str) -> bool {
self.as_table().is_some_and(|t| t.contains_key(key))
}
pub fn clone_in(&self, arena: &'de Arena) -> Item<'de> {
if self.is_scalar() {
unsafe { std::ptr::read(self) }
} else if self.tag() == TAG_ARRAY {
let cloned = unsafe { self.payload.array.clone_in(arena) };
Item {
payload: Payload {
array: ManuallyDrop::new(cloned),
},
meta: self.meta,
}
} else {
let cloned = unsafe { self.payload.table.clone_in(arena) };
Item {
payload: Payload {
table: ManuallyDrop::new(cloned),
},
meta: self.meta,
}
}
}
}
impl<'de> Item<'de> {
#[inline]
pub fn expected(&self, expected: &'static &'static str) -> Error {
Error::new(
ErrorKind::Wanted {
expected,
found: self.type_str(),
},
self.span_unchecked(),
)
}
#[inline]
pub fn parse<T>(&self) -> Result<T, Error>
where
T: std::str::FromStr,
<T as std::str::FromStr>::Err: std::fmt::Display,
{
let Some(s) = self.as_str() else {
return Err(self.expected(&"a string"));
};
match s.parse() {
Ok(v) => Ok(v),
Err(err) => Err(Error::custom(
format!("failed to parse string: {err}"),
self.span_unchecked(),
)),
}
}
}
impl fmt::Debug for Item<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.value() {
Value::String(s) => s.fmt(f),
Value::Integer(i) => i.fmt(f),
Value::Float(v) => v.fmt(f),
Value::Boolean(b) => b.fmt(f),
Value::Array(a) => a.fmt(f),
Value::Table(t) => t.fmt(f),
Value::DateTime(m) => {
let mut buf = std::mem::MaybeUninit::uninit();
f.write_str(m.format(&mut buf))
}
}
}
}
#[derive(Copy, Clone)]
pub struct Key<'de> {
pub name: &'de str,
pub span: Span,
}
impl<'de> Key<'de> {
pub fn new(value: &'de str) -> Self {
Self {
name: value,
span: Span::default(),
}
}
pub fn as_str(&self) -> &'de str {
self.name
}
}
impl<'de> From<&'de str> for Key<'de> {
fn from(value: &'de str) -> Self {
Self::new(value)
}
}
#[cfg(target_pointer_width = "64")]
const _: () = assert!(std::mem::size_of::<Key<'_>>() == 24);
impl std::borrow::Borrow<str> for Key<'_> {
fn borrow(&self) -> &str {
self.name
}
}
impl fmt::Debug for Key<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name)
}
}
impl fmt::Display for Key<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name)
}
}
impl Ord for Key<'_> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.name.cmp(other.name)
}
}
impl PartialOrd for Key<'_> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for Key<'_> {
fn eq(&self, other: &Self) -> bool {
self.name.eq(other.name)
}
}
impl Eq for Key<'_> {}
impl std::hash::Hash for Key<'_> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
}
}
pub(crate) fn equal_items(a: &Item<'_>, b: &Item<'_>, index: Option<&TableIndex<'_>>) -> bool {
if a.kind() != b.kind() {
return false;
}
unsafe {
match a.kind() {
Kind::String => a.payload.string == b.payload.string,
Kind::Integer => a.payload.integer == b.payload.integer,
Kind::Float => {
let af = a.payload.float;
let bf = b.payload.float;
if af.is_nan() && bf.is_nan() {
af.is_sign_negative() == bf.is_sign_negative()
} else {
af.to_bits() == bf.to_bits()
}
}
Kind::Boolean => a.payload.boolean == b.payload.boolean,
Kind::DateTime => a.payload.datetime == b.payload.datetime,
Kind::Array => {
let a = a.payload.array.as_slice();
let b = b.payload.array.as_slice();
if a.len() != b.len() {
return false;
}
for i in 0..a.len() {
if !equal_items(&*a.as_ptr().add(i), &*b.as_ptr().add(i), index) {
return false;
}
}
true
}
Kind::Table => {
let tab_a = a.as_table_unchecked();
let tab_b = b.as_table_unchecked();
if tab_a.len() != tab_b.len() {
return false;
}
for (key, val_a) in tab_a {
let Some((_, val_b)) = tab_b.value.get_entry_with_maybe_index(key.name, index)
else {
return false;
};
if !equal_items(val_a, val_b, index) {
return false;
}
}
true
}
}
}
}
impl<'de> PartialEq for Item<'de> {
fn eq(&self, other: &Self) -> bool {
equal_items(self, other, None)
}
}
impl<'de> std::ops::Index<&str> for Item<'de> {
type Output = MaybeItem<'de>;
#[inline]
fn index(&self, index: &str) -> &Self::Output {
if let Some(table) = self.as_table()
&& let Some(item) = table.get(index)
{
return MaybeItem::from_ref(item);
}
&NONE
}
}
impl<'de> std::ops::Index<usize> for Item<'de> {
type Output = MaybeItem<'de>;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
if let Some(arr) = self.as_array()
&& let Some(item) = arr.get(index)
{
return MaybeItem::from_ref(item);
}
&NONE
}
}
impl<'de> std::ops::Index<&str> for MaybeItem<'de> {
type Output = MaybeItem<'de>;
#[inline]
fn index(&self, index: &str) -> &Self::Output {
if let Some(table) = self.as_table()
&& let Some(item) = table.get(index)
{
return MaybeItem::from_ref(item);
}
&NONE
}
}
impl<'de> std::ops::Index<usize> for MaybeItem<'de> {
type Output = MaybeItem<'de>;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
if let Some(arr) = self.as_array()
&& let Some(item) = arr.get(index)
{
return MaybeItem::from_ref(item);
}
&NONE
}
}
impl fmt::Debug for MaybeItem<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.item() {
Some(item) => item.fmt(f),
None => f.write_str("None"),
}
}
}
#[repr(C)]
pub struct MaybeItem<'de> {
payload: Payload<'de>,
meta: ItemMetadata,
}
unsafe impl Sync for MaybeItem<'_> {}
pub(crate) static NONE: MaybeItem<'static> = MaybeItem {
payload: Payload {
integer: Integer {
value: PackedI128 { value: 0 },
},
},
meta: ItemMetadata {
start_and_tag: TAG_NONE,
end_and_flag: FLAG_NONE,
},
};
impl<'de> MaybeItem<'de> {
pub fn from_ref<'a>(item: &'a Item<'de>) -> &'a Self {
unsafe { &*(item as *const Item<'de>).cast::<MaybeItem<'de>>() }
}
#[inline]
pub(crate) fn tag(&self) -> u32 {
self.meta.tag()
}
pub fn item(&self) -> Option<&Item<'de>> {
if self.tag() != TAG_NONE {
Some(unsafe { &*(self as *const MaybeItem<'de>).cast::<Item<'de>>() })
} else {
None
}
}
#[inline]
pub fn as_str(&self) -> Option<&str> {
if self.tag() == TAG_STRING {
Some(unsafe { self.payload.string })
} else {
None
}
}
#[inline]
pub fn as_i128(&self) -> Option<i128> {
if self.tag() == TAG_INTEGER {
Some(unsafe { self.payload.integer.as_i128() })
} else {
None
}
}
#[inline]
pub fn as_i64(&self) -> Option<i64> {
if self.tag() == TAG_INTEGER {
unsafe { self.payload.integer.as_i64() }
} else {
None
}
}
#[inline]
pub fn as_u64(&self) -> Option<u64> {
if self.tag() == TAG_INTEGER {
unsafe { self.payload.integer.as_u64() }
} else {
None
}
}
#[inline]
pub fn as_f64(&self) -> Option<f64> {
self.item()?.as_f64()
}
#[inline]
pub fn as_bool(&self) -> Option<bool> {
if self.tag() == TAG_BOOLEAN {
Some(unsafe { self.payload.boolean })
} else {
None
}
}
#[inline]
pub fn as_array(&self) -> Option<&Array<'de>> {
if self.tag() == TAG_ARRAY {
Some(unsafe { &*(self as *const Self).cast::<Array<'de>>() })
} else {
None
}
}
#[inline]
pub fn as_table(&self) -> Option<&Table<'de>> {
if self.tag() == TAG_TABLE {
Some(unsafe { &*(self as *const Self).cast::<Table<'de>>() })
} else {
None
}
}
#[inline]
pub fn as_datetime(&self) -> Option<&DateTime> {
if self.tag() == TAG_DATETIME {
Some(unsafe { &self.payload.datetime })
} else {
None
}
}
pub fn span(&self) -> Span {
if let Some(item) = self.item() {
item.span_unchecked()
} else {
Span::default()
}
}
pub fn value(&self) -> Option<Value<'_, 'de>> {
if let Some(item) = self.item() {
Some(item.value())
} else {
None
}
}
}