use alloc::borrow::Cow;
use alloc::boxed::Box;
use alloc::vec;
use alloc::vec::Vec;
use core::borrow::Borrow;
use core::cmp::Ordering;
use core::fmt;
use core::ops::Deref;
use core::ptr::NonNull;
use super::storage::Storage;
use crate::inline_key::{InlineKey, InlineWord};
#[repr(C, align(8))]
pub struct VarBytes {
len: u32,
offset: u32,
}
impl VarBytes {
pub(crate) fn encode(len: u32, offset: u32) -> [u8; 8] {
let mut bytes = [0; 8];
bytes[..4].copy_from_slice(&len.to_ne_bytes());
bytes[4..].copy_from_slice(&offset.to_ne_bytes());
bytes
}
pub(crate) fn decode(bytes: [u8; 8]) -> (u32, u32) {
let [l0, l1, l2, l3, o0, o1, o2, o3] = bytes;
(
u32::from_ne_bytes([l0, l1, l2, l3]),
u32::from_ne_bytes([o0, o1, o2, o3]),
)
}
#[inline]
pub fn bytes(&self) -> &[u8] {
let start = (self as *const Self).cast::<u8>();
unsafe { core::slice::from_raw_parts(start.add(self.offset as usize), self.len as usize) }
}
#[must_use]
pub fn len(&self) -> usize {
self.len as usize
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len == 0
}
}
impl PartialEq for VarBytes {
fn eq(&self, other: &Self) -> bool {
self.bytes() == other.bytes()
}
}
impl Eq for VarBytes {}
impl PartialOrd for VarBytes {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for VarBytes {
fn cmp(&self, other: &Self) -> Ordering {
self.bytes().cmp(other.bytes())
}
}
impl fmt::Debug for VarBytes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.bytes(), f)
}
}
impl Borrow<[u8]> for VarBytes {
fn borrow(&self) -> &[u8] {
self.bytes()
}
}
impl InlineKey for VarBytes {
const HAS_HASH_TAG: bool = true;
const HAS_CMP_FROM: bool = true;
#[inline]
fn inline_key_at(&self, offset: usize) -> InlineWord {
self.bytes().inline_key_at(offset)
}
#[inline]
fn hash_tag(&self) -> u8 {
self.bytes().hash_tag()
}
#[inline]
fn cmp_from(&self, other: &Self, equal_len: usize) -> Ordering {
self.bytes().cmp_from(other.bytes(), equal_len)
}
#[inline]
fn cmp_prefix(&self, prefix: &[u8]) -> Ordering {
self.bytes().cmp_prefix(prefix)
}
}
unsafe impl Storage for VarBytes {
type Input<'a> = Cow<'a, [u8]>;
type InWriter = ArenaBytes;
const VAR_LEN: bool = true;
fn in_writer(input: Cow<'_, [u8]>, arena: &mut ByteArena) -> ArenaBytes {
arena.push(&input)
}
fn tail_bytes(in_writer: &ArenaBytes) -> &[u8] {
in_writer.bytes()
}
}
pub struct VarBytesBuf {
words: QueryWords,
}
enum QueryWords {
InPlace([u64; 1 + Self::IN_PLACE_BYTES / 8]),
Allocated(Vec<u64>),
}
impl QueryWords {
const IN_PLACE_BYTES: usize = 64;
fn words_mut(&mut self, words: usize) -> &mut [u64] {
if words <= 1 + Self::IN_PLACE_BYTES / 8 && !matches!(self, Self::Allocated(_)) {
let Self::InPlace(in_place) = self else {
unreachable!("not allocated");
};
return &mut in_place[..words];
}
if let Self::InPlace(_) = self {
*self = Self::Allocated(Vec::new());
}
let Self::Allocated(allocated) = self else {
unreachable!("allocated just above");
};
allocated.clear();
allocated.resize(words, 0);
allocated
}
fn as_ptr(&self) -> *const u64 {
match self {
Self::InPlace(in_place) => in_place.as_ptr(),
Self::Allocated(allocated) => allocated.as_ptr(),
}
}
}
impl VarBytesBuf {
#[must_use]
pub fn new(bytes: &[u8]) -> Self {
let mut buf = Self::empty();
buf.set(bytes);
buf
}
#[must_use]
pub fn empty() -> Self {
Self {
words: QueryWords::InPlace([0; 1 + QueryWords::IN_PLACE_BYTES / 8]),
}
}
pub fn set(&mut self, bytes: &[u8]) {
let len = u32::try_from(bytes.len()).expect("a key of at most 4 GiB");
let words = self.words.words_mut(1 + bytes.len().div_ceil(8));
let offset = size_of::<VarBytes>() as u32;
words[0] = u64::from_ne_bytes(VarBytes::encode(len, offset));
let key_bytes = unsafe {
core::slice::from_raw_parts_mut(words.as_mut_ptr().add(1).cast::<u8>(), bytes.len())
};
key_bytes.copy_from_slice(bytes);
}
}
impl Default for VarBytesBuf {
fn default() -> Self {
Self::empty()
}
}
impl Deref for VarBytesBuf {
type Target = VarBytes;
fn deref(&self) -> &VarBytes {
unsafe { &*self.words.as_ptr().cast::<VarBytes>() }
}
}
impl fmt::Debug for VarBytesBuf {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("VarBytesBuf").field(&**self).finish()
}
}
#[repr(C, align(8))]
#[derive(Clone, Copy)]
pub struct ArenaBytes {
record: NonNull<u8>,
zeros: [u8; 8 - size_of::<NonNull<u8>>()],
}
unsafe impl Send for ArenaBytes {}
unsafe impl Sync for ArenaBytes {}
impl ArenaBytes {
#[inline]
pub(crate) fn bytes(&self) -> &[u8] {
unsafe {
let len = self.record.cast::<u32>().read_unaligned() as usize;
core::slice::from_raw_parts(self.record.as_ptr().add(size_of::<u32>()), len)
}
}
}
impl PartialEq for ArenaBytes {
fn eq(&self, other: &Self) -> bool {
self.bytes() == other.bytes()
}
}
impl Eq for ArenaBytes {}
impl PartialOrd for ArenaBytes {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ArenaBytes {
fn cmp(&self, other: &Self) -> Ordering {
self.bytes().cmp(other.bytes())
}
}
impl InlineKey for ArenaBytes {
const HAS_HASH_TAG: bool = VarBytes::HAS_HASH_TAG;
const HAS_CMP_FROM: bool = VarBytes::HAS_CMP_FROM;
const WIDTH: crate::InlineWidth = VarBytes::WIDTH;
#[inline]
fn inline_key_at(&self, offset: usize) -> InlineWord {
self.bytes().inline_key_at(offset)
}
#[inline]
fn hash_tag(&self) -> u8 {
self.bytes().hash_tag()
}
#[inline]
fn cmp_from(&self, other: &Self, equal_len: usize) -> Ordering {
self.bytes().cmp_from(other.bytes(), equal_len)
}
#[inline]
fn cmp_prefix(&self, prefix: &[u8]) -> Ordering {
self.bytes().cmp_prefix(prefix)
}
}
#[derive(Default)]
pub struct ByteArena {
chunks: Vec<NonNull<[u8]>>,
used: usize,
}
unsafe impl Send for ByteArena {}
unsafe impl Sync for ByteArena {}
impl ByteArena {
const CHUNK_SIZE: usize = 1 << 20;
pub(crate) fn push(&mut self, bytes: &[u8]) -> ArenaBytes {
let len = u32::try_from(bytes.len()).expect("a key or value of at most 4 GiB");
let size = size_of::<u32>() + bytes.len();
let room = self
.chunks
.last()
.map_or(0, |chunk| chunk.len() - self.used);
if size > room {
let chunk = vec![0; size.max(Self::CHUNK_SIZE)].into_boxed_slice();
self.chunks.push(NonNull::from(Box::leak(chunk)));
self.used = 0;
}
let chunk = *self.chunks.last().expect("a chunk with room");
let record = unsafe {
let record = chunk.cast::<u8>().add(self.used);
record.cast::<[u8; 4]>().write_unaligned(len.to_ne_bytes());
record
.add(size_of::<u32>())
.copy_from_nonoverlapping(NonNull::from(bytes).cast(), bytes.len());
record
};
self.used += size;
ArenaBytes {
record,
zeros: [0; 8 - size_of::<NonNull<u8>>()],
}
}
}
impl Drop for ByteArena {
fn drop(&mut self) {
for chunk in self.chunks.drain(..) {
drop(unsafe { Box::from_raw(chunk.as_ptr()) });
}
}
}