#[cfg(test)]
#[path = "./table_tests.rs"]
mod tests;
use crate::Span;
use crate::item::{
FLAG_DOTTED, FLAG_FROZEN, FLAG_HEADER, FLAG_TABLE, Item, ItemMetadata, Key, MaybeItem, NONE,
TAG_TABLE, TableStyle,
};
use crate::parser::KeyRef;
use std::mem::size_of;
use std::ptr::NonNull;
use crate::arena::Arena;
pub(crate) type TableIndex<'de> = foldhash::HashMap<KeyRef<'de>, usize>;
type TableEntry<'de> = (Key<'de>, Item<'de>);
const MIN_CAP: u32 = 2;
#[repr(C, align(8))]
pub(crate) struct InnerTable<'de> {
pub(super) len: u32,
pub(super) cap: u32,
pub(super) ptr: NonNull<TableEntry<'de>>,
}
impl<'de> InnerTable<'de> {
#[inline]
pub fn new() -> Self {
Self {
len: 0,
cap: 0,
ptr: NonNull::dangling(),
}
}
pub(crate) fn with_capacity(cap: u32, arena: &'de Arena) -> Self {
let mut table = Self::new();
if cap > 0 {
table.grow_to(cap, arena);
}
table
}
pub fn insert_unique(
&mut self,
key: Key<'de>,
item: Item<'de>,
arena: &'de Arena,
) -> &mut TableEntry<'de> {
let len = self.len;
if self.len == self.cap {
self.grow(arena);
}
unsafe {
let ptr = self.ptr.as_ptr().add(len as usize);
ptr.write((key, item));
self.len = len + 1;
&mut (*ptr)
}
}
#[inline]
pub fn len(&self) -> usize {
self.len as usize
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[cfg(feature = "to-toml")]
pub(crate) fn get_entry_with_index(
&self,
key: &str,
index: &TableIndex<'_>,
) -> Option<&TableEntry<'de>> {
if self.len() > crate::parser::INDEXED_TABLE_THRESHOLD {
let first_key_span = unsafe { self.first_key_span_start_unchecked() };
let i = *index.get(&KeyRef::new(key, first_key_span))?;
self.entries().get(i)
} else {
for entry in self.entries() {
if entry.0.name == key {
return Some(entry);
}
}
None
}
}
pub(crate) fn get_entry_with_maybe_index(
&self,
key: &str,
index: Option<&TableIndex<'_>>,
) -> Option<&TableEntry<'de>> {
if self.len() > crate::parser::INDEXED_TABLE_THRESHOLD {
if let Some(index) = index {
let first_key_span = unsafe { self.first_key_span_start_unchecked() };
let i = *index.get(&KeyRef::new(key, first_key_span))?;
return self.entries().get(i);
}
}
for entry in self.entries() {
if entry.0.name == key {
return Some(entry);
}
}
None
}
pub fn get_entry(&self, name: &str) -> Option<(&Key<'de>, &Item<'de>)> {
for (key, item) in self.entries() {
if key.name == name {
return Some((key, item));
}
}
None
}
pub fn get(&self, name: &str) -> Option<&Item<'de>> {
for (key, item) in self.entries() {
if key.name == name {
return Some(item);
}
}
None
}
pub fn get_mut(&mut self, name: &str) -> Option<&mut Item<'de>> {
for (key, item) in self.entries_mut() {
if key.name == name {
return Some(item);
}
}
None
}
#[inline]
pub fn contains_key(&self, name: &str) -> bool {
self.get(name).is_some()
}
pub fn remove_entry(&mut self, name: &str) -> Option<(Key<'de>, Item<'de>)> {
let idx = self.find_index(name)?;
Some(self.remove_at(idx))
}
#[inline]
pub(crate) unsafe fn first_key_span_start_unchecked(&self) -> u32 {
debug_assert!(self.len > 0);
unsafe { (*self.ptr.as_ptr()).0.span.start }
}
#[inline]
pub fn entries(&self) -> &[TableEntry<'de>] {
unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len as usize) }
}
#[inline]
pub fn entries_mut(&mut self) -> &mut [TableEntry<'de>] {
unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len as usize) }
}
pub(crate) fn find_index(&self, name: &str) -> Option<usize> {
for (i, (key, _)) in self.entries().iter().enumerate() {
if key.name == name {
return Some(i);
}
}
None
}
fn remove_at(&mut self, idx: usize) -> (Key<'de>, Item<'de>) {
let last = self.len as usize - 1;
let ptr = unsafe { self.ptr.as_ptr().add(idx) };
let entry = unsafe { ptr.read() };
if idx != last {
unsafe {
ptr.write(self.ptr.as_ptr().add(last).read());
}
}
self.len -= 1;
entry
}
#[cold]
fn grow(&mut self, arena: &'de Arena) {
let new_cap = if self.cap == 0 {
MIN_CAP
} else {
self.cap.checked_mul(2).expect("capacity overflow")
};
self.grow_to(new_cap, arena);
}
fn grow_to(&mut self, new_cap: u32, arena: &'de Arena) {
#[cfg(target_pointer_width = "32")]
let new_size = (new_cap as usize)
.checked_mul(size_of::<TableEntry<'_>>())
.expect("capacity overflow");
#[cfg(not(target_pointer_width = "32"))]
let new_size = new_cap as usize * size_of::<TableEntry<'_>>();
if self.cap > 0 {
let old_size = self.cap as usize * size_of::<TableEntry<'_>>();
self.ptr = unsafe { arena.realloc(self.ptr.cast(), old_size, new_size).cast() };
} else {
self.ptr = arena.alloc(new_size).cast();
}
self.cap = new_cap;
}
pub(crate) fn clone_in(&self, arena: &'de Arena) -> Self {
let len = self.len as usize;
if len == 0 {
return Self::new();
}
let size = len * size_of::<TableEntry<'de>>();
let dst: NonNull<TableEntry<'de>> = arena.alloc(size).cast();
let src = self.ptr.as_ptr();
let dst_ptr = dst.as_ptr();
let mut run_start = 0;
for i in 0..len {
if unsafe { !(*src.add(i)).1.is_scalar() } {
if run_start < i {
unsafe {
std::ptr::copy_nonoverlapping(
src.add(run_start),
dst_ptr.add(run_start),
i - run_start,
);
}
}
unsafe {
let (key, item) = &*src.add(i);
dst_ptr.add(i).write((*key, item.clone_in(arena)));
}
run_start = i + 1;
}
}
if run_start < len {
unsafe {
std::ptr::copy_nonoverlapping(
src.add(run_start),
dst_ptr.add(run_start),
len - run_start,
);
}
}
Self {
len: self.len,
cap: self.len,
ptr: dst,
}
}
pub(crate) unsafe fn emplace_in(
&self,
target: &mut crate::item::owned::ItemCopyTarget,
) -> InnerTable<'static> {
let len = self.len as usize;
if len == 0 {
return InnerTable::new();
}
let byte_size = len * size_of::<TableEntry<'static>>();
let dst_ptr = unsafe { target.alloc_aligned(byte_size) }
.as_ptr()
.cast::<TableEntry<'static>>();
for (i, (key, item)) in self.entries().iter().enumerate() {
let new_key = Key {
name: unsafe { target.copy_str(key.name) },
span: key.span,
};
let new_item = unsafe { item.emplace_in(target) };
unsafe { dst_ptr.add(i).write((new_key, new_item)) };
}
InnerTable {
len: self.len,
cap: self.len,
ptr: unsafe { NonNull::new_unchecked(dst_ptr) },
}
}
}
impl std::fmt::Debug for InnerTable<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut map = f.debug_map();
for (k, v) in self.entries() {
map.entry(k, v);
}
map.finish()
}
}
pub struct IntoIter<'de> {
table: InnerTable<'de>,
index: u32,
}
impl<'de> Iterator for IntoIter<'de> {
type Item = (Key<'de>, Item<'de>);
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.table.len {
let entry = unsafe { self.table.ptr.as_ptr().add(self.index as usize).read() };
self.index += 1;
Some(entry)
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = (self.table.len - self.index) as usize;
(remaining, Some(remaining))
}
}
impl ExactSizeIterator for IntoIter<'_> {}
#[repr(C)]
pub struct Table<'de> {
pub(crate) value: InnerTable<'de>,
pub(crate) meta: ItemMetadata,
}
impl<'de> Table<'de> {
pub fn new() -> Table<'de> {
let mut meta = ItemMetadata::hints(TAG_TABLE, FLAG_TABLE);
meta.set_auto_style();
Table {
meta,
value: InnerTable::new(),
}
}
pub fn try_with_capacity(cap: usize, arena: &'de Arena) -> Option<Table<'de>> {
let cap: u32 = cap.try_into().ok()?;
let mut meta = ItemMetadata::hints(TAG_TABLE, FLAG_TABLE);
meta.set_auto_style();
Some(Table {
meta,
value: InnerTable::with_capacity(cap, arena),
})
}
pub(crate) fn new_spanned(span: Span) -> Table<'de> {
Table {
meta: ItemMetadata::spanned(TAG_TABLE, FLAG_TABLE, span.start, span.end),
value: InnerTable::new(),
}
}
pub fn span(&self) -> Span {
self.meta.span()
}
}
impl<'de> Default for Table<'de> {
fn default() -> Self {
Self::new()
}
}
impl PartialEq for Table<'_> {
fn eq(&self, other: &Self) -> bool {
super::equal_tables(self, other, None)
}
}
impl std::fmt::Debug for Table<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.value.fmt(f)
}
}
impl<'de> Table<'de> {
pub fn insert(&mut self, key: Key<'de>, value: Item<'de>, arena: &'de Arena) {
if let Some(existing) = self.get_mut(key.name) {
*existing = value;
return;
}
self.value.insert_unique(key, value, arena);
}
pub fn insert_unique(&mut self, key: Key<'de>, value: Item<'de>, arena: &'de Arena) {
self.value.insert_unique(key, value, arena);
}
#[inline]
pub fn len(&self) -> usize {
self.value.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.value.is_empty()
}
pub fn get_key_value(&self, name: &str) -> Option<(&Key<'de>, &Item<'de>)> {
self.value.get_entry(name)
}
pub fn get(&self, name: &str) -> Option<&Item<'de>> {
self.value.get(name)
}
pub fn get_mut(&mut self, name: &str) -> Option<&mut Item<'de>> {
self.value.get_mut(name)
}
#[inline]
pub fn contains_key(&self, name: &str) -> bool {
self.value.contains_key(name)
}
pub fn remove_entry(&mut self, name: &str) -> Option<(Key<'de>, Item<'de>)> {
self.value.remove_entry(name)
}
#[inline]
pub fn entries(&self) -> &[TableEntry<'de>] {
self.value.entries()
}
#[inline]
pub fn entries_mut(&mut self) -> &mut [TableEntry<'de>] {
self.value.entries_mut()
}
#[inline]
pub fn iter(&self) -> std::slice::Iter<'_, TableEntry<'de>> {
self.entries().iter()
}
pub fn as_item(&self) -> &Item<'de> {
unsafe { &*(self as *const Table<'de>).cast::<Item<'de>>() }
}
pub fn into_item(self) -> Item<'de> {
unsafe { std::mem::transmute(self) }
}
}
impl<'a, 'de> IntoIterator for &'a mut Table<'de> {
type Item = &'a mut (Key<'de>, Item<'de>);
type IntoIter = std::slice::IterMut<'a, TableEntry<'de>>;
fn into_iter(self) -> Self::IntoIter {
self.value.entries_mut().iter_mut()
}
}
impl<'a, 'de> IntoIterator for &'a Table<'de> {
type Item = &'a (Key<'de>, Item<'de>);
type IntoIter = std::slice::Iter<'a, TableEntry<'de>>;
fn into_iter(self) -> Self::IntoIter {
self.value.entries().iter()
}
}
impl<'de> IntoIterator for Table<'de> {
type Item = (Key<'de>, Item<'de>);
type IntoIter = IntoIter<'de>;
fn into_iter(self) -> Self::IntoIter {
IntoIter {
table: self.value,
index: 0,
}
}
}
const _: () = assert!(std::mem::size_of::<Table<'_>>() == std::mem::size_of::<Item<'_>>());
const _: () = assert!(std::mem::align_of::<Table<'_>>() == std::mem::align_of::<Item<'_>>());
unsafe impl Send for Table<'_> {}
unsafe impl Sync for Table<'_> {}
unsafe impl Send for IntoIter<'_> {}
unsafe impl Sync for IntoIter<'_> {}
impl<'de> Table<'de> {
#[inline]
pub(crate) fn span_start(&self) -> u32 {
self.meta.span_start()
}
#[inline]
pub(crate) fn set_span_start(&mut self, v: u32) {
self.meta.set_span_start(v);
}
#[inline]
pub(crate) fn set_span_end(&mut self, v: u32) {
self.meta.set_span_end(v);
}
#[inline]
pub(crate) fn extend_span_end(&mut self, new_end: u32) {
self.meta.extend_span_end(new_end);
}
#[inline]
pub(crate) fn set_header_flag(&mut self) {
self.meta.set_flag(FLAG_HEADER);
}
#[inline]
pub(crate) fn set_dotted_flag(&mut self) {
self.meta.set_flag(FLAG_DOTTED);
}
#[inline]
pub fn style(&self) -> TableStyle {
match self.meta.flag() {
FLAG_DOTTED => TableStyle::Dotted,
FLAG_HEADER => TableStyle::Header,
FLAG_FROZEN => TableStyle::Inline,
_ => TableStyle::Implicit,
}
}
#[inline]
pub fn set_style(&mut self, kind: TableStyle) {
let flag = match kind {
TableStyle::Implicit => FLAG_TABLE,
TableStyle::Dotted => FLAG_DOTTED,
TableStyle::Header => FLAG_HEADER,
TableStyle::Inline => FLAG_FROZEN,
};
self.meta.set_flag(flag);
self.meta.clear_auto_style();
}
pub fn clone_in(&self, arena: &'de Arena) -> Table<'de> {
Table {
value: self.value.clone_in(arena),
meta: self.meta,
}
}
}
impl<'de> std::ops::Index<&str> for Table<'de> {
type Output = MaybeItem<'de>;
#[inline]
fn index(&self, index: &str) -> &Self::Output {
if let Some(item) = self.get(index) {
return MaybeItem::from_ref(item);
}
&NONE
}
}