pub(crate) mod constant;
pub(crate) mod number;
pub(crate) mod number_binary;
pub(crate) mod number_decimal;
pub(crate) mod string;
pub(crate) use constant::{FALSE, NULL, TRUE};
#[cfg(feature = "arbitrary_precision")]
pub(crate) use number::parse_json_number;
pub(crate) use number::{InlineNumber, InlineNumberError, NumberShape};
#[cfg(not(feature = "arbitrary_precision"))]
pub(crate) use number_binary::BinaryNumberRepr as InlineNumberRepr;
#[cfg(feature = "arbitrary_precision")]
pub(crate) use number_decimal::DecimalNumberRepr as InlineNumberRepr;
use std::cmp::Ordering;
use std::fmt::{self, Formatter};
use std::hash::Hasher;
use crate::value::{
Destructured, DestructuredMut, DestructuredRef, IValue, NumVal, ValueRepr, ValueType,
};
const IS_NUMBER: usize = 1 << 3;
const IS_STRING: usize = 1 << 4;
const PAYLOAD_SHIFT: u32 = 5;
const TAG_MASK: usize = super::ALIGNMENT - 1;
#[cfg(codegen_probes)]
pub(crate) fn is_inline_number(v: &IValue) -> bool {
v.usize_() & IS_NUMBER != 0
}
#[cfg(codegen_probes)]
pub(crate) unsafe fn assume_inline_integer(v: &IValue) {
unsafe { InlineNumberRepr::assume_integer(v.usize_()) };
}
enum InlineKind {
Number,
String,
Constant,
}
pub(crate) trait InlineValue {
fn value_type(&self, v: &IValue) -> ValueType;
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 num_val<'a>(&self, _v: &'a IValue) -> Option<NumVal<'a>> {
None
}
fn has_decimal_point(&self, _v: &IValue) -> bool {
false
}
unsafe fn to_f64(&self, _v: &IValue) -> Option<f64> {
None
}
unsafe fn to_f64_lossy(&self, _v: &IValue) -> Option<f64> {
None
}
unsafe fn as_bytes<'a>(&self, _v: &'a IValue) -> Option<&'a [u8]> {
None
}
}
pub(crate) struct InlineRepr;
impl InlineRepr {
fn kind(v: &IValue) -> InlineKind {
let bits = v.usize_();
if bits & IS_NUMBER != 0 {
InlineKind::Number
} else if bits & IS_STRING != 0 {
InlineKind::String
} else {
InlineKind::Constant
}
}
}
impl InlineKind {
#[inline]
fn with<R>(self, f: impl FnOnce(&'static dyn InlineValue) -> R) -> R {
match self {
InlineKind::Number => f(&InlineNumberRepr),
InlineKind::String => f(&string::InlineStringRepr),
InlineKind::Constant => f(&constant::ConstantRepr),
}
}
}
impl ValueRepr for InlineRepr {
fn value_type(&self, v: &IValue) -> ValueType {
Self::kind(v).with(|i| i.value_type(v))
}
unsafe fn clone(&self, v: &IValue) -> IValue {
v.raw_copy()
}
unsafe fn drop(&self, _v: &mut IValue) {}
unsafe fn hash(&self, v: &IValue, state: &mut dyn Hasher) {
Self::kind(v).with(|i| unsafe { i.hash(v, state) });
}
unsafe fn eq(&self, a: &IValue, b: &IValue) -> bool {
Self::kind(a).with(|i| unsafe { i.eq(a, b) })
}
unsafe fn partial_cmp(&self, a: &IValue, b: &IValue) -> Option<Ordering> {
Self::kind(a).with(|i| unsafe { i.partial_cmp(a, b) })
}
unsafe fn debug(&self, v: &IValue, f: &mut Formatter<'_>) -> fmt::Result {
Self::kind(v).with(|i| unsafe { i.debug(v, f) })
}
fn destructure(&self, v: IValue) -> Destructured {
Self::kind(&v).with(move |i| i.destructure(v))
}
unsafe fn destructure_ref<'a>(&self, v: &'a IValue) -> DestructuredRef<'a> {
Self::kind(v).with(|i| unsafe { i.destructure_ref(v) })
}
unsafe fn destructure_mut<'a>(&self, v: &'a mut IValue) -> DestructuredMut<'a> {
Self::kind(v).with(move |i| unsafe { i.destructure_mut(v) })
}
unsafe fn num_val<'a>(&self, v: &'a IValue) -> Option<NumVal<'a>> {
Self::kind(v).with(|i| unsafe { i.num_val(v) })
}
fn has_decimal_point(&self, v: &IValue) -> bool {
Self::kind(v).with(|i| i.has_decimal_point(v))
}
unsafe fn to_f64(&self, v: &IValue) -> Option<f64> {
Self::kind(v).with(|i| unsafe { i.to_f64(v) })
}
unsafe fn to_f64_lossy(&self, v: &IValue) -> Option<f64> {
Self::kind(v).with(|i| unsafe { i.to_f64_lossy(v) })
}
unsafe fn as_bytes<'a>(&self, v: &'a IValue) -> Option<&'a [u8]> {
Self::kind(v).with(|i| unsafe { i.as_bytes(v) })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn kinds_classify_and_avoid_the_niche() {
let numbers = [
IValue::new_i64(0),
IValue::new_i64(1),
IValue::new_i64(-1),
IValue::new_f64(0.5).unwrap(),
IValue::new_f64(0.0).unwrap(),
];
let longest_inline = &"abcdefg"[..string::CAPACITY];
let strings = [IValue::new_string(""), IValue::new_string(longest_inline)];
let constants = [IValue::NULL, IValue::TRUE, IValue::FALSE];
for v in &numbers {
assert!(matches!(InlineRepr::kind(v), InlineKind::Number), "{:?}", v);
}
for v in &strings {
assert!(matches!(InlineRepr::kind(v), InlineKind::String), "{:?}", v);
}
for v in &constants {
assert!(
matches!(InlineRepr::kind(v), InlineKind::Constant),
"{:?}",
v
);
}
for v in numbers.iter().chain(&strings).chain(&constants) {
assert_ne!(v.usize_(), 0, "{:?} is the reserved all-zero niche", v);
}
}
}