#[cfg(test)]
#[path = "./owned_tests.rs"]
mod tests;
use crate::item::{TAG_ARRAY, TAG_STRING, TAG_TABLE};
use crate::{Array, DateTime, Item, Key, Kind, Span, Table, TableStyle, Value};
use std::mem::size_of;
use std::ptr::NonNull;
pub(crate) struct ItemCopyTarget {
pub(crate) aligned: *mut u8,
#[cfg(debug_assertions)]
pub(crate) aligned_end: *mut u8,
pub(crate) string: *mut u8,
#[cfg(debug_assertions)]
pub(crate) string_end: *mut u8,
}
impl ItemCopyTarget {
pub(crate) unsafe fn alloc_aligned(&mut self, size: usize) -> NonNull<u8> {
#[cfg(debug_assertions)]
unsafe {
let remaining = self.aligned_end.offset_from(self.aligned) as usize;
assert!(size <= remaining);
};
let ptr = self.aligned;
unsafe {
self.aligned = self.aligned.add(size);
NonNull::new_unchecked(ptr)
}
}
pub(crate) unsafe fn copy_str(&mut self, s: &str) -> &'static str {
if s.is_empty() {
return "";
}
let len = s.len();
#[cfg(debug_assertions)]
unsafe {
let remaining = self.string_end.offset_from(self.string) as usize;
assert!(len <= remaining);
};
unsafe {
std::ptr::copy_nonoverlapping(s.as_ptr(), self.string, len);
let result =
std::str::from_utf8_unchecked(std::slice::from_raw_parts(self.string, len));
self.string = self.string.add(len);
result
}
}
}
fn compute_size(item: &Item<'_>, aligned: &mut usize, strings: &mut usize) {
match item.tag() {
TAG_STRING => {
if let Some(s) = item.as_str() {
*strings += s.len();
}
}
TAG_TABLE => {
let table = unsafe { item.as_table_unchecked() };
*aligned += table.len() * size_of::<(Key<'_>, Item<'_>)>();
for (key, child) in table {
*strings += key.name.len();
compute_size(child, aligned, strings);
}
}
TAG_ARRAY => {
let array = unsafe { item.as_array_unchecked() };
*aligned += array.len() * size_of::<Item<'_>>();
for child in array {
compute_size(child, aligned, strings);
}
}
_ => {}
}
}
pub struct OwnedTable {
inner: OwnedItem,
}
impl OwnedTable {
#[inline(always)]
pub fn table<'a>(&'a self) -> &'a Table<'a> {
unsafe { self.inner.item().as_table_unchecked() }
}
#[inline]
pub fn span(&self) -> Span {
self.table().span()
}
#[inline]
pub fn len(&self) -> usize {
self.table().len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.table().is_empty()
}
pub fn get_key_value<'a>(&'a self, name: &str) -> Option<(&'a Key<'a>, &'a Item<'a>)> {
self.table().get_key_value(name)
}
pub fn get<'a>(&'a self, name: &str) -> Option<&'a Item<'a>> {
self.table().get(name)
}
#[inline]
pub fn contains_key(&self, name: &str) -> bool {
self.table().contains_key(name)
}
#[inline]
pub fn entries<'a>(&'a self) -> &'a [(Key<'a>, Item<'a>)] {
self.table().entries()
}
#[inline]
pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, (Key<'a>, Item<'a>)> {
self.table().iter()
}
#[inline]
pub fn as_item<'a>(&'a self) -> &'a Item<'a> {
self.inner.item()
}
#[inline]
pub fn style(&self) -> TableStyle {
self.table().style()
}
}
impl std::fmt::Debug for OwnedTable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.table().fmt(f)
}
}
impl Clone for OwnedTable {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl PartialEq for OwnedTable {
fn eq(&self, other: &Self) -> bool {
self.table() == other.table()
}
}
impl<'a> IntoIterator for &'a OwnedTable {
type Item = &'a (Key<'a>, Item<'a>);
type IntoIter = std::slice::Iter<'a, (Key<'a>, Item<'a>)>;
fn into_iter(self) -> Self::IntoIter {
self.table().iter()
}
}
impl From<&Table<'_>> for OwnedTable {
fn from(value: &Table<'_>) -> Self {
let owned_item = OwnedItem::from(value.as_item());
debug_assert_eq!(owned_item.item().kind(), Kind::Table);
Self { inner: owned_item }
}
}
pub struct OwnedItem {
item: Item<'static>,
ptr: NonNull<u8>,
capacity: usize,
}
unsafe impl Send for OwnedItem {}
unsafe impl Sync for OwnedItem {}
unsafe impl Send for OwnedTable {}
unsafe impl Sync for OwnedTable {}
impl std::fmt::Debug for OwnedItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.item.fmt(f)
}
}
impl Clone for OwnedItem {
fn clone(&self) -> Self {
OwnedItem::from(self.item())
}
}
impl Drop for OwnedItem {
fn drop(&mut self) {
if self.capacity > 0 {
unsafe {
let layout = std::alloc::Layout::from_size_align_unchecked(self.capacity, 8);
std::alloc::dealloc(self.ptr.as_ptr(), layout);
}
}
}
}
impl<'a> From<&Item<'a>> for OwnedItem {
fn from(item: &Item<'a>) -> Self {
let mut aligned = 0usize;
let mut strings = 0usize;
compute_size(item, &mut aligned, &mut strings);
let total = aligned + strings;
if total == 0 {
return Self {
item: unsafe { std::mem::transmute_copy(item) },
ptr: NonNull::dangling(),
capacity: 0,
};
}
let layout = std::alloc::Layout::from_size_align(total, 8).expect("layout overflow");
let raw = unsafe { std::alloc::alloc(layout) };
let Some(base) = NonNull::new(raw) else {
std::alloc::handle_alloc_error(layout);
};
let mut target = unsafe {
ItemCopyTarget {
aligned: base.as_ptr(),
#[cfg(debug_assertions)]
aligned_end: base.as_ptr().add(aligned),
string: base.as_ptr().add(aligned),
#[cfg(debug_assertions)]
string_end: base.as_ptr().add(total),
}
};
let new_item = unsafe { item.emplace_in(&mut target) };
#[cfg(debug_assertions)]
{
assert_eq!(target.aligned as usize, base.as_ptr() as usize + aligned);
assert_eq!(target.string as usize, base.as_ptr() as usize + total);
}
Self {
item: new_item,
ptr: base,
capacity: total,
}
}
}
impl<'a> From<Item<'a>> for OwnedItem {
fn from(item: Item<'a>) -> Self {
OwnedItem::from(&item)
}
}
impl OwnedItem {
#[inline(always)]
pub fn item<'a>(&'a self) -> &'a Item<'a> {
&self.item
}
#[inline]
pub fn kind(&self) -> Kind {
self.item().kind()
}
#[inline]
pub fn span(&self) -> Span {
self.item().span()
}
#[inline]
pub fn as_str(&self) -> Option<&str> {
self.item().as_str()
}
#[inline]
pub fn as_i128(&self) -> Option<i128> {
self.item().as_i128()
}
#[inline]
pub fn as_i64(&self) -> Option<i64> {
self.item().as_i64()
}
#[inline]
pub fn as_u64(&self) -> Option<u64> {
self.item().as_u64()
}
#[inline]
pub fn as_f64(&self) -> Option<f64> {
self.item().as_f64()
}
#[inline]
pub fn as_bool(&self) -> Option<bool> {
self.item().as_bool()
}
#[inline]
pub fn as_array<'a>(&'a self) -> Option<&'a Array<'a>> {
self.item().as_array()
}
#[inline]
pub fn as_table<'a>(&'a self) -> Option<&'a Table<'a>> {
self.item().as_table()
}
#[inline]
pub fn as_datetime(&self) -> Option<&DateTime> {
self.item().as_datetime()
}
#[inline]
pub fn value<'a>(&'a self) -> Value<'a, 'a> {
self.item().value()
}
#[inline]
pub fn has_keys(&self) -> bool {
self.item().has_keys()
}
#[inline]
pub fn has_key(&self, key: &str) -> bool {
self.item().has_key(key)
}
}
impl PartialEq for OwnedItem {
fn eq(&self, other: &Self) -> bool {
self.item() == other.item()
}
}
#[cfg(feature = "from-toml")]
impl<'a> crate::FromToml<'a> for OwnedItem {
fn from_toml(_: &mut crate::Context<'a>, item: &Item<'a>) -> Result<Self, crate::Failed> {
Ok(OwnedItem::from(item))
}
}
#[cfg(feature = "to-toml")]
impl crate::ToToml for OwnedItem {
fn to_toml<'a>(&'a self, arena: &'a crate::Arena) -> Result<Item<'a>, crate::ToTomlError> {
Ok(self.item().clone_in(arena))
}
}
#[cfg(feature = "to-toml")]
impl crate::ToFlattened for OwnedItem {
fn to_flattened<'a>(
&'a self,
arena: &'a crate::Arena,
table: &mut crate::Table<'a>,
) -> Result<(), crate::ToTomlError> {
self.item().to_flattened(arena, table)
}
}
#[cfg(feature = "from-toml")]
impl<'a> crate::FromToml<'a> for OwnedTable {
fn from_toml(ctx: &mut crate::Context<'a>, item: &Item<'a>) -> Result<Self, crate::Failed> {
let Ok(table) = item.require_table(ctx) else {
return Err(crate::Failed);
};
Ok(OwnedTable::from(table))
}
}
#[cfg(feature = "to-toml")]
impl crate::ToToml for OwnedTable {
fn to_toml<'a>(&'a self, arena: &'a crate::Arena) -> Result<Item<'a>, crate::ToTomlError> {
Ok(self.table().as_item().clone_in(arena))
}
}
#[cfg(feature = "to-toml")]
impl crate::ToFlattened for OwnedTable {
fn to_flattened<'a>(
&'a self,
arena: &'a crate::Arena,
table: &mut crate::Table<'a>,
) -> Result<(), crate::ToTomlError> {
self.table().to_flattened(arena, table)
}
}