use std::borrow::Cow;
use std::collections::HashMap;
use std::hash::{BuildHasher, BuildHasherDefault, Hasher, RandomState};
use deser_core::ext::Datetime;
const INDEX_THRESHOLD: usize = 16;
#[derive(Debug, Clone, Copy, Default)]
pub(crate) struct Span {
pub start: usize,
pub end: usize,
}
impl Span {
pub(crate) fn new(start: usize, end: usize) -> Span {
Span { start, end }
}
}
#[derive(Debug, Clone)]
pub(crate) enum Value<'a> {
Str(Cow<'a, str>),
Int(i64),
UInt(u64),
Float(f64),
Float32(f32),
FloatText(Cow<'a, str>),
Bool(bool),
Datetime(Datetime),
Table(usize),
Array(usize),
}
#[derive(Debug)]
pub(crate) struct Item<'a> {
pub value: Value<'a>,
pub span: Span,
}
#[derive(Debug)]
pub(crate) struct Entry<'a> {
pub key: Cow<'a, str>,
pub key_span: Span,
pub item: Item<'a>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum TableKind {
Implicit,
Header,
Dotted(u32),
Inline,
}
#[derive(Debug)]
pub(crate) struct Table<'a> {
pub entries: Vec<Entry<'a>>,
pub kind: TableKind,
pub span: Span,
index: Option<Box<KeyIndex>>,
}
#[derive(Debug)]
struct KeyIndex {
state: RandomState,
heads: HashMap<u64, usize, BuildHasherDefault<HashIsKey>>,
chain: Vec<usize>,
}
const NO_ENTRY: usize = usize::MAX;
#[derive(Debug, Default)]
struct HashIsKey(u64);
impl Hasher for HashIsKey {
fn finish(&self) -> u64 {
self.0
}
fn write(&mut self, _bytes: &[u8]) {
unreachable!("only hashes are hashed")
}
fn write_u64(&mut self, value: u64) {
self.0 = value;
}
}
impl KeyIndex {
fn new(entries: &[Entry<'_>]) -> KeyIndex {
let mut index = KeyIndex {
state: RandomState::new(),
heads: HashMap::with_capacity_and_hasher(entries.len() * 2, Default::default()),
chain: Vec::with_capacity(entries.len() * 2),
};
for (idx, entry) in entries.iter().enumerate() {
index.insert(&entry.key, idx);
}
index
}
fn insert(&mut self, key: &str, idx: usize) {
let hash = self.state.hash_one(key);
self.insert_hashed(hash, idx);
}
fn insert_hashed(&mut self, hash: u64, idx: usize) {
debug_assert_eq!(idx, self.chain.len());
let prev = self.heads.insert(hash, idx).unwrap_or(NO_ENTRY);
self.chain.push(prev);
}
fn find(&self, entries: &[Entry<'_>], key: &str) -> Option<usize> {
self.find_hashed(entries, key, self.state.hash_one(key))
}
fn find_hashed(&self, entries: &[Entry<'_>], key: &str, hash: u64) -> Option<usize> {
let mut idx = *self.heads.get(&hash)?;
while idx != NO_ENTRY {
if entries[idx].key == key {
return Some(idx);
}
idx = self.chain[idx];
}
None
}
}
#[derive(Debug)]
pub(crate) struct Array<'a> {
pub items: Vec<Item<'a>>,
pub of_tables: bool,
pub span: Span,
}
#[derive(Debug, Default)]
pub(crate) struct Document<'a> {
pub tables: Vec<Table<'a>>,
pub arrays: Vec<Array<'a>>,
}
impl<'a> Document<'a> {
pub(crate) fn new_table(&mut self, kind: TableKind, span: Span) -> usize {
self.tables.push(Table {
entries: Vec::new(),
kind,
span,
index: None,
});
self.tables.len() - 1
}
pub(crate) fn new_array(&mut self, of_tables: bool, span: Span) -> usize {
self.arrays.push(Array {
items: Vec::new(),
of_tables,
span,
});
self.arrays.len() - 1
}
pub(crate) fn find(&self, table: usize, key: &str) -> Option<&Entry<'a>> {
let table = &self.tables[table];
match table.index {
Some(ref index) => index
.find(&table.entries, key)
.map(|idx| &table.entries[idx]),
None => table.entries.iter().find(|x| x.key == key),
}
}
pub(crate) fn insert_new(&mut self, table: usize, entry: Entry<'a>) -> Result<(), Entry<'a>> {
let table_ref = &mut self.tables[table];
match table_ref.index {
Some(ref mut index) => {
let hash = index.state.hash_one(&*entry.key);
if index
.find_hashed(&table_ref.entries, &entry.key, hash)
.is_some()
{
return Err(entry);
}
index.insert_hashed(hash, table_ref.entries.len());
table_ref.entries.push(entry);
}
None => {
if table_ref.entries.iter().any(|x| x.key == entry.key) {
return Err(entry);
}
self.insert(table, entry);
}
}
Ok(())
}
pub(crate) fn insert(&mut self, table: usize, entry: Entry<'a>) {
let table = &mut self.tables[table];
table.entries.push(entry);
let len = table.entries.len();
if let Some(ref mut index) = table.index {
index.insert(&table.entries[len - 1].key, len - 1);
} else if len > INDEX_THRESHOLD {
table.index = Some(Box::new(KeyIndex::new(&table.entries)));
}
}
}