use bitcode::{Decode, Encode};
use wbase::buf::put_header_payload;
use crate::{
buf::stack_heap_buf,
error::{Error, Result},
tag::{CollectionType, KeyTag},
};
#[inline(always)]
const fn read_be_u64_at(slice: &[u8], offset: usize) -> u64 {
u64::from_be_bytes([
slice[offset],
slice[offset + 1],
slice[offset + 2],
slice[offset + 3],
slice[offset + 4],
slice[offset + 5],
slice[offset + 6],
slice[offset + 7],
])
}
#[inline(always)]
const fn ensure_len(slice: &[u8], need: usize) -> Result<()> {
if slice.len() < need {
return Err(Error::BufferTooShort {
expected: need,
actual: slice.len(),
});
}
Ok(())
}
const TYPE_OFFSET: usize = 8;
const VERSION_OFFSET: usize = 16;
pub const SIZE_OFFSET: usize = 24;
const U64_LEN: usize = 8;
pub const COMPACT_META_VALUE_SIZE: usize = 16;
pub const META_VALUE_SIZE: usize = 32;
pub const SUBKEY_HEADER_SIZE: usize = 17;
pub const SUBKEY_STACK_CAP: usize = 128;
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
pub enum StorageEncoding {
#[default]
Compact = 0,
Flattened = 1,
}
impl StorageEncoding {
#[inline(always)]
pub const fn from_u8(val: u8) -> Self {
match val {
1 => Self::Flattened,
_ => Self::Compact,
}
}
#[inline(always)]
pub const fn as_u8(self) -> u8 {
self as u8
}
}
#[repr(C, align(8))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
pub struct MetaValue {
pub key_id: u64,
pub collection_type: CollectionType,
pub reserved: [u8; 7],
pub version: u64,
pub size: u64,
}
impl MetaValue {
#[inline(always)]
pub const fn new(key_id: u64, collection_type: CollectionType, version: u64, size: u64) -> Self {
Self {
key_id,
collection_type,
reserved: [0u8; 7],
version,
size,
}
}
#[inline(always)]
pub const fn encoding(&self) -> StorageEncoding {
StorageEncoding::from_u8(self.reserved[0])
}
#[inline(always)]
pub const fn set_encoding(&mut self, enc: StorageEncoding) {
self.reserved[0] = enc.as_u8();
}
#[inline(always)]
pub const fn with_encoding(mut self, enc: StorageEncoding) -> Self {
self.reserved[0] = enc.as_u8();
self
}
#[inline(always)]
pub const fn bump_version(&mut self) -> u64 {
self.version = match self.version.checked_add(1) {
Some(v) => v,
None => 1,
};
self.version
}
#[inline(always)]
pub const fn inc_size(&mut self, count: u64) {
self.size = self.size.saturating_add(count);
}
#[inline(always)]
pub const fn dec_size(&mut self, count: u64) {
self.size = self.size.saturating_sub(count);
}
#[inline(always)]
pub const fn to_bytes(&self) -> [u8; META_VALUE_SIZE] {
let k = self.key_id.to_be_bytes();
let r = self.reserved;
let v = self.version.to_be_bytes();
let s = self.size.to_be_bytes();
[
k[0],
k[1],
k[2],
k[3],
k[4],
k[5],
k[6],
k[7],
self.collection_type.as_u8(),
r[0],
r[1],
r[2],
r[3],
r[4],
r[5],
r[6],
v[0],
v[1],
v[2],
v[3],
v[4],
v[5],
v[6],
v[7],
s[0],
s[1],
s[2],
s[3],
s[4],
s[5],
s[6],
s[7],
]
}
#[inline(always)]
pub const fn read_version(slice: &[u8]) -> Result<u64> {
match ensure_len(slice, VERSION_OFFSET + U64_LEN) {
Ok(()) => Ok(read_be_u64_at(slice, VERSION_OFFSET)),
Err(e) => Err(e),
}
}
#[inline(always)]
pub const fn read_size(slice: &[u8]) -> Result<u64> {
match ensure_len(slice, SIZE_OFFSET + U64_LEN) {
Ok(()) => Ok(read_be_u64_at(slice, SIZE_OFFSET)),
Err(e) => Err(e),
}
}
#[inline(always)]
pub const fn read_collection_type(slice: &[u8]) -> Result<CollectionType> {
if let Err(e) = ensure_len(slice, TYPE_OFFSET + 1) {
return Err(e);
}
match CollectionType::from_u8(slice[TYPE_OFFSET]) {
Some(t) => Ok(t),
None => Err(Error::InvalidCollectionType(slice[TYPE_OFFSET])),
}
}
#[inline(always)]
pub const fn from_slice(slice: &[u8]) -> Result<Self> {
if let Err(e) = ensure_len(slice, META_VALUE_SIZE) {
return Err(e);
}
let key_id = u64::from_be_bytes([
slice[0], slice[1], slice[2], slice[3], slice[4], slice[5], slice[6], slice[7],
]);
let collection_type = match CollectionType::from_u8(slice[TYPE_OFFSET]) {
Some(t) => t,
None => return Err(Error::InvalidCollectionType(slice[TYPE_OFFSET])),
};
let reserved = [
slice[9], slice[10], slice[11], slice[12], slice[13], slice[14], slice[15],
];
Ok(Self {
key_id,
collection_type,
reserved,
version: read_be_u64_at(slice, VERSION_OFFSET),
size: read_be_u64_at(slice, SIZE_OFFSET),
})
}
#[inline(always)]
pub const fn from_bytes(bytes: [u8; META_VALUE_SIZE]) -> Result<Self> {
Self::from_slice(&bytes)
}
#[inline]
pub fn write_to_slice(&self, dst: &mut [u8]) -> Result<()> {
if let Some(chunk) = dst.first_chunk_mut::<META_VALUE_SIZE>() {
*chunk = self.to_bytes();
Ok(())
} else {
Err(Error::BufferTooShort {
expected: META_VALUE_SIZE,
actual: dst.len(),
})
}
}
#[inline]
pub fn encode_bitcode(&self) -> Vec<u8> {
bitcode::encode(self)
}
#[inline]
pub fn decode_bitcode(src: &[u8]) -> Result<Self> {
bitcode::decode(src).map_err(Error::from)
}
}
#[repr(C, align(8))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
pub struct CompactMetaValue {
pub collection_type: CollectionType,
pub encoding: StorageEncoding,
pub reserved: [u8; 2],
pub size: u32,
pub expire_at_ms: u64,
}
impl CompactMetaValue {
#[inline(always)]
pub const fn new(
collection_type: CollectionType,
encoding: StorageEncoding,
size: u32,
expire_at_ms: u64,
) -> Self {
Self {
collection_type,
encoding,
reserved: [0u8; 2],
size,
expire_at_ms,
}
}
#[inline(always)]
pub const fn is_expired(&self, now_ms: u64) -> bool {
self.expire_at_ms > 0 && self.expire_at_ms <= now_ms
}
#[inline(always)]
pub const fn inc_size(&mut self, count: u32) {
self.size = self.size.saturating_add(count);
}
#[inline(always)]
pub const fn dec_size(&mut self, count: u32) {
self.size = self.size.saturating_sub(count);
}
#[inline]
pub const fn to_bytes(&self) -> [u8; COMPACT_META_VALUE_SIZE] {
let w0 = ((self.collection_type.as_u8() as u64) << 56)
| ((self.encoding.as_u8() as u64) << 48)
| ((self.reserved[0] as u64) << 40)
| ((self.reserved[1] as u64) << 32)
| (self.size as u64);
let b0 = w0.to_be_bytes();
let b1 = self.expire_at_ms.to_be_bytes();
[
b0[0], b0[1], b0[2], b0[3], b0[4], b0[5], b0[6], b0[7], b1[0], b1[1], b1[2], b1[3], b1[4],
b1[5], b1[6], b1[7],
]
}
#[inline(always)]
pub const fn from_bytes(bytes: [u8; COMPACT_META_VALUE_SIZE]) -> Result<Self> {
Self::from_slice(&bytes)
}
#[inline]
pub const fn from_slice(slice: &[u8]) -> Result<Self> {
match slice {
[
b0,
b1,
b2,
b3,
b4,
b5,
b6,
b7,
b8,
b9,
b10,
b11,
b12,
b13,
b14,
b15,
..,
] => {
let w0 = u64::from_be_bytes([*b0, *b1, *b2, *b3, *b4, *b5, *b6, *b7]);
let expire_at_ms = u64::from_be_bytes([*b8, *b9, *b10, *b11, *b12, *b13, *b14, *b15]);
let type_byte = (w0 >> 56) as u8;
let collection_type = match CollectionType::from_u8(type_byte) {
Some(t) => t,
None => return Err(Error::InvalidCollectionType(type_byte)),
};
let encoding = StorageEncoding::from_u8((w0 >> 48) as u8);
let reserved = [(w0 >> 40) as u8, (w0 >> 32) as u8];
let size = w0 as u32;
Ok(Self {
collection_type,
encoding,
reserved,
size,
expire_at_ms,
})
}
_ => Err(Error::BufferTooShort {
expected: COMPACT_META_VALUE_SIZE,
actual: slice.len(),
}),
}
}
#[inline]
pub const fn read_collection_type(slice: &[u8]) -> Option<CollectionType> {
if slice.len() < COMPACT_META_VALUE_SIZE {
return None;
}
CollectionType::from_u8(slice[0])
}
#[inline]
pub const fn read_encoding(slice: &[u8]) -> Option<StorageEncoding> {
if slice.len() < COMPACT_META_VALUE_SIZE {
return None;
}
Some(StorageEncoding::from_u8(slice[1]))
}
#[inline]
pub const fn read_size(slice: &[u8]) -> Option<u32> {
if slice.len() < COMPACT_META_VALUE_SIZE {
return None;
}
Some(u32::from_be_bytes([slice[4], slice[5], slice[6], slice[7]]))
}
#[inline]
pub const fn read_expire_at_ms(slice: &[u8]) -> Option<u64> {
if slice.len() < COMPACT_META_VALUE_SIZE {
return None;
}
Some(u64::from_be_bytes([
slice[8], slice[9], slice[10], slice[11], slice[12], slice[13], slice[14], slice[15],
]))
}
#[inline]
pub const fn read_is_expired(slice: &[u8], now_ms: u64) -> Option<bool> {
match Self::read_expire_at_ms(slice) {
Some(exp) => Some(exp > 0 && exp <= now_ms),
None => None,
}
}
#[inline]
pub fn write_to_slice(&self, dst: &mut [u8]) -> Result<()> {
if let Some(chunk) = dst.first_chunk_mut::<COMPACT_META_VALUE_SIZE>() {
*chunk = self.to_bytes();
Ok(())
} else {
Err(Error::BufferTooShort {
expected: COMPACT_META_VALUE_SIZE,
actual: dst.len(),
})
}
}
#[inline]
pub fn encode_bitcode(&self) -> Vec<u8> {
bitcode::encode(self)
}
#[inline]
pub fn decode_bitcode(src: &[u8]) -> Result<Self> {
bitcode::decode(src).map_err(Error::from)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SubKeyRef<'a> {
pub tag: KeyTag,
pub key_id: u64,
pub version: u64,
pub payload: &'a [u8],
}
impl<'a> SubKeyRef<'a> {
#[inline]
pub const fn from_slice(slice: &'a [u8]) -> Result<Self> {
let (tag, key_id, version) = match SubKeyCodec::decode_header(slice) {
Ok(h) => h,
Err(e) => return Err(e),
};
let payload = slice.split_at(SUBKEY_HEADER_SIZE).1;
Ok(Self {
tag,
key_id,
version,
payload,
})
}
#[inline(always)]
pub const fn header(&self) -> [u8; SUBKEY_HEADER_SIZE] {
SubKeyCodec::encode_header(self.tag, self.key_id, self.version)
}
#[inline(always)]
pub const fn encoded_len(&self) -> usize {
SUBKEY_HEADER_SIZE + self.payload.len()
}
#[inline]
pub fn write_to_slice(&self, dst: &mut [u8]) -> Result<usize> {
SubKeyCodec::encode_to_slice(self.tag, self.key_id, self.version, self.payload, dst)
}
#[inline]
pub fn to_vec(&self) -> Vec<u8> {
self.try_to_vec().unwrap_or_default()
}
#[inline]
pub fn try_to_vec(&self) -> Result<Vec<u8>> {
SubKeyCodec::try_encode_to_vec(self.tag, self.key_id, self.version, self.payload)
}
}
stack_heap_buf!(SubKeyBuf, SUBKEY_STACK_CAP);
impl SubKeyBuf {
#[inline]
pub fn encode(tag: KeyTag, key_id: u64, version: u64, payload: &[u8]) -> Result<Self> {
SubKeyCodec::encode_to_buf(tag, key_id, version, payload)
}
}
pub struct SubKeyCodec;
#[inline(always)]
pub(crate) const fn kv_prefix(tag: u8, key_id: u64, version: u64) -> [u8; SUBKEY_HEADER_SIZE] {
let k = key_id.to_be_bytes();
let v = version.to_be_bytes();
[
tag, k[0], k[1], k[2], k[3], k[4], k[5], k[6], k[7], v[0], v[1], v[2], v[3], v[4], v[5], v[6],
v[7],
]
}
impl SubKeyCodec {
#[inline(always)]
pub const fn encode_header(tag: KeyTag, key_id: u64, version: u64) -> [u8; SUBKEY_HEADER_SIZE] {
kv_prefix(tag.as_u8(), key_id, version)
}
#[inline(always)]
pub const fn decode_id_version(slice: &[u8]) -> Option<(u64, u64)> {
match slice {
[
k0,
k1,
k2,
k3,
k4,
k5,
k6,
k7,
v0,
v1,
v2,
v3,
v4,
v5,
v6,
v7,
..,
] => {
let key_id = u64::from_be_bytes([*k0, *k1, *k2, *k3, *k4, *k5, *k6, *k7]);
let version = u64::from_be_bytes([*v0, *v1, *v2, *v3, *v4, *v5, *v6, *v7]);
Some((key_id, version))
}
_ => None,
}
}
#[inline(always)]
pub const fn decode_header(slice: &[u8]) -> Result<(KeyTag, u64, u64)> {
match slice {
[tag_byte, rest @ ..] => {
let tag = match KeyTag::from_u8(*tag_byte) {
Some(t) if t.is_subkey() => t,
_ => return Err(Error::InvalidKeyTag(*tag_byte)),
};
match Self::decode_id_version(rest) {
Some((key_id, version)) => Ok((tag, key_id, version)),
None => Err(Error::BufferTooShort {
expected: SUBKEY_HEADER_SIZE,
actual: slice.len(),
}),
}
}
[] => Err(Error::BufferTooShort {
expected: SUBKEY_HEADER_SIZE,
actual: 0,
}),
}
}
#[inline(always)]
const fn ensure_subkey(tag: KeyTag) -> Result<()> {
if tag.is_subkey() {
Ok(())
} else {
Err(Error::InvalidKeyTag(tag.as_u8()))
}
}
#[inline(always)]
pub const fn decode(slice: &[u8]) -> Result<SubKeyRef<'_>> {
SubKeyRef::from_slice(slice)
}
#[inline]
pub fn encode_to_slice(
tag: KeyTag,
key_id: u64,
version: u64,
payload: &[u8],
dst: &mut [u8],
) -> Result<usize> {
Self::ensure_subkey(tag)?;
let total_len = SUBKEY_HEADER_SIZE
.checked_add(payload.len())
.ok_or(Error::RecordSizeOverflow)?;
let header = Self::encode_header(tag, key_id, version);
put_header_payload(dst, &header, payload).ok_or(Error::BufferTooShort {
expected: total_len,
actual: dst.len(),
})
}
#[inline]
pub fn encode_to_buf(
tag: KeyTag,
key_id: u64,
version: u64,
payload: &[u8],
) -> Result<SubKeyBuf> {
Self::ensure_subkey(tag)?;
SUBKEY_HEADER_SIZE
.checked_add(payload.len())
.ok_or(Error::RecordSizeOverflow)?;
Ok(SubKeyBuf::from_header_parts(
&Self::encode_header(tag, key_id, version),
payload,
))
}
#[inline]
pub fn try_encode_to_vec(
tag: KeyTag,
key_id: u64,
version: u64,
payload: &[u8],
) -> Result<Vec<u8>> {
Self::ensure_subkey(tag)?;
let total_len = match SUBKEY_HEADER_SIZE.checked_add(payload.len()) {
Some(l) => l,
None => return Err(Error::RecordSizeOverflow),
};
let mut vec = Vec::with_capacity(total_len);
let header = Self::encode_header(tag, key_id, version);
vec.extend_from_slice(&header);
vec.extend_from_slice(payload);
Ok(vec)
}
}