use compact_str::CompactString;
use fnv::FnvHashMap;
use crate::error::{TraverserError, TraverserResult, ValueError};
use crate::value::ElementProperty;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) struct SackRef(u32);
impl SackRef {
pub(crate) const NONE: SackRef = SackRef(u32::MAX);
#[inline]
pub(crate) fn is_none(self) -> bool {
self == Self::NONE
}
}
impl Default for SackRef {
fn default() -> Self {
Self::NONE
}
}
#[derive(Clone, Debug)]
pub(crate) struct SackSpec {
pub(crate) initial: ElementProperty,
pub(crate) merge: Option<crate::value::Operator>,
}
#[derive(Clone, PartialEq, Eq, Hash)]
enum ScalarKey {
Null,
Bool(bool),
Int(i64),
FloatBits(u64),
Str(CompactString),
Uuid(u128),
}
impl ScalarKey {
fn of(value: &ElementProperty) -> Option<ScalarKey> {
Some(match value {
ElementProperty::Null => ScalarKey::Null,
ElementProperty::Boolean(b) => ScalarKey::Bool(*b),
ElementProperty::Number(n) => match n.as_i64() {
Some(i) => ScalarKey::Int(i),
None => {
let f = n.as_f64().unwrap_or(f64::NAN);
ScalarKey::FloatBits(if f.is_nan() {
f64::NAN.to_bits()
} else {
f.to_bits()
})
}
},
ElementProperty::String(s) => ScalarKey::Str(s.clone()),
ElementProperty::Compressed(text) => ScalarKey::Str(text.text()),
ElementProperty::Uuid(u) => ScalarKey::Uuid(*u),
ElementProperty::Array(_)
| ElementProperty::Object(_)
| ElementProperty::Map(_)
| ElementProperty::Vertex(_)
| ElementProperty::Edge(_) => return None,
})
}
}
static NULL: ElementProperty = ElementProperty::Null;
pub(crate) struct SackArena {
values: Vec<ElementProperty>,
intern: FnvHashMap<ScalarKey, u32>,
capacity: u32,
}
impl Default for SackArena {
fn default() -> Self {
Self::new()
}
}
impl SackArena {
pub(crate) fn new() -> Self {
Self {
values: Vec::new(),
intern: FnvHashMap::default(),
capacity: u32::MAX - 1,
}
}
#[cfg(test)]
pub(crate) fn with_capacity_limit(capacity: u32) -> Self {
Self {
capacity,
..Self::new()
}
}
pub(crate) fn intern(&mut self, value: ElementProperty) -> TraverserResult<SackRef> {
match &value {
ElementProperty::Vertex(_) | ElementProperty::Edge(_) => {
let actual = if matches!(value, ElementProperty::Vertex(_)) {
"vertex"
} else {
"edge"
};
return Err(ValueError::CastException {
expected: "scalar, array or object".to_string(),
actual: actual.to_string(),
value: None,
origin: None,
}
.into());
}
_ => {}
}
let key = ScalarKey::of(&value);
if let Some(key) = &key
&& let Some(&index) = self.intern.get(key)
{
return Ok(SackRef(index));
}
let index = self.values.len();
if index >= self.capacity as usize {
return Err(Box::new(TraverserError::SackArenaExhausted {
capacity: u64::from(self.capacity),
}));
}
self.values.push(value);
if let Some(key) = key {
self.intern.insert(key, index as u32);
}
Ok(SackRef(index as u32))
}
#[inline]
pub(crate) fn get(&self, handle: SackRef) -> &ElementProperty {
self.values.get(handle.0 as usize).unwrap_or(&NULL)
}
#[allow(dead_code)]
pub(crate) fn len(&self) -> usize {
self.values.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::value::Number;
fn int(i: i64) -> ElementProperty {
ElementProperty::Number(Number::from_i64(i))
}
fn float(f: f64) -> ElementProperty {
ElementProperty::Number(Number::from_f64(f))
}
#[test]
fn equal_scalars_share_a_handle() {
let mut arena = SackArena::new();
let a = arena.intern(int(5)).unwrap();
let b = arena.intern(int(5)).unwrap();
assert_eq!(a, b);
assert_eq!(arena.len(), 1);
let s1 = arena.intern(ElementProperty::String("x".into())).unwrap();
let s2 = arena.intern(ElementProperty::String("x".into())).unwrap();
assert_eq!(s1, s2);
}
#[test]
fn int_and_float_differ_and_signed_zero_is_canonical() {
let mut arena = SackArena::new();
assert_ne!(
arena.intern(int(1)).unwrap(),
arena.intern(float(1.0)).unwrap()
);
assert_eq!(
arena.intern(float(0.0)).unwrap(),
arena.intern(float(-0.0)).unwrap()
);
assert_eq!(
arena.intern(float(f64::NAN)).unwrap(),
arena.intern(float(-f64::NAN)).unwrap()
);
}
#[test]
fn containers_never_share_a_handle() {
let mut arena = SackArena::new();
let a = arena.intern(ElementProperty::Array(vec![int(1)])).unwrap();
let b = arena.intern(ElementProperty::Array(vec![int(1)])).unwrap();
assert_ne!(a, b);
}
#[test]
fn none_reads_null() {
let arena = SackArena::new();
assert!(matches!(arena.get(SackRef::NONE), ElementProperty::Null));
assert!(SackRef::NONE.is_none());
}
#[test]
fn exhaustion_is_an_error() {
let mut arena = SackArena::with_capacity_limit(2);
arena.intern(int(1)).unwrap();
arena.intern(int(2)).unwrap();
assert!(
arena.intern(int(1)).is_ok(),
"an existing scalar needs no slot"
);
let err = arena.intern(int(3)).unwrap_err();
assert!(matches!(
*err,
TraverserError::SackArenaExhausted { capacity: 2 }
));
}
}