use bitcode::{Decode, Encode};
use crate::{
buf::stack_heap_buf,
error::{Error, Result},
tag::{CollectionType, KeyTag},
};
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]
pub const fn to_bytes(&self) -> [u8; META_VALUE_SIZE] {
let k = self.key_id.to_be_bytes();
let v = self.version.to_be_bytes();
let s = self.size.to_be_bytes();
let r = self.reserved;
[
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]
pub const fn read_version(slice: &[u8]) -> Result<u64> {
if slice.len() < VERSION_OFFSET + U64_LEN {
return Err(Error::BufferTooShort {
expected: VERSION_OFFSET + U64_LEN,
actual: slice.len(),
});
}
Ok(u64::from_be_bytes([
slice[16], slice[17], slice[18], slice[19], slice[20], slice[21], slice[22], slice[23],
]))
}
#[inline]
pub const fn read_size(slice: &[u8]) -> Result<u64> {
if slice.len() < SIZE_OFFSET + U64_LEN {
return Err(Error::BufferTooShort {
expected: SIZE_OFFSET + U64_LEN,
actual: slice.len(),
});
}
Ok(u64::from_be_bytes([
slice[24], slice[25], slice[26], slice[27], slice[28], slice[29], slice[30], slice[31],
]))
}
#[inline]
pub const fn read_collection_type(slice: &[u8]) -> Result<CollectionType> {
if slice.len() < TYPE_OFFSET + 1 {
return Err(Error::BufferTooShort {
expected: TYPE_OFFSET + 1,
actual: slice.len(),
});
}
match CollectionType::from_u8(slice[8]) {
Some(t) => Ok(t),
None => Err(Error::InvalidCollectionType(slice[8])),
}
}
#[inline]
pub const fn from_slice(slice: &[u8]) -> Result<Self> {
if slice.len() < META_VALUE_SIZE {
return Err(Error::BufferTooShort {
expected: META_VALUE_SIZE,
actual: slice.len(),
});
}
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[8]) {
Some(t) => t,
None => return Err(Error::InvalidCollectionType(slice[8])),
};
let reserved = [
slice[9], slice[10], slice[11], slice[12], slice[13], slice[14], slice[15],
];
let version = u64::from_be_bytes([
slice[16], slice[17], slice[18], slice[19], slice[20], slice[21], slice[22], slice[23],
]);
let size = u64::from_be_bytes([
slice[24], slice[25], slice[26], slice[27], slice[28], slice[29], slice[30], slice[31],
]);
Ok(Self {
key_id,
collection_type,
reserved,
version,
size,
})
}
#[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 s = self.size.to_be_bytes();
let exp = self.expire_at_ms.to_be_bytes();
[
self.collection_type.as_u8(),
self.encoding.as_u8(),
self.reserved[0],
self.reserved[1],
s[0],
s[1],
s[2],
s[3],
exp[0],
exp[1],
exp[2],
exp[3],
exp[4],
exp[5],
exp[6],
exp[7],
]
}
#[inline]
pub const fn from_bytes(bytes: [u8; COMPACT_META_VALUE_SIZE]) -> Result<Self> {
let collection_type = match CollectionType::from_u8(bytes[0]) {
Some(t) => t,
None => return Err(Error::InvalidCollectionType(bytes[0])),
};
let encoding = StorageEncoding::from_u8(bytes[1]);
let reserved = [bytes[2], bytes[3]];
let size = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
let expire_at_ms = u64::from_be_bytes([
bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15],
]);
Ok(Self {
collection_type,
encoding,
reserved,
size,
expire_at_ms,
})
}
#[inline]
pub const fn from_slice(slice: &[u8]) -> Result<Self> {
if slice.len() < COMPACT_META_VALUE_SIZE {
return Err(Error::BufferTooShort {
expected: COMPACT_META_VALUE_SIZE,
actual: slice.len(),
});
}
let collection_type = match CollectionType::from_u8(slice[0]) {
Some(t) => t,
None => return Err(Error::InvalidCollectionType(slice[0])),
};
let encoding = StorageEncoding::from_u8(slice[1]);
let reserved = [slice[2], slice[3]];
let size = u32::from_be_bytes([slice[4], slice[5], slice[6], slice[7]]);
let expire_at_ms = u64::from_be_bytes([
slice[8], slice[9], slice[10], slice[11], slice[12], slice[13], slice[14], slice[15],
]);
Ok(Self {
collection_type,
encoding,
reserved,
size,
expire_at_ms,
})
}
#[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(v) => v,
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_buf(&self) -> Result<SubKeyBuf> {
SubKeyCodec::encode_to_buf(self.tag, self.key_id, self.version, self.payload)
}
#[inline]
pub fn to_vec(&self) -> Vec<u8> {
SubKeyCodec::encode_to_vec(self.tag, self.key_id, self.version, self.payload)
}
#[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]
pub const fn decode_header(slice: &[u8]) -> Result<(KeyTag, u64, u64)> {
if slice.len() < SUBKEY_HEADER_SIZE {
return Err(Error::BufferTooShort {
expected: SUBKEY_HEADER_SIZE,
actual: slice.len(),
});
}
let tag = match KeyTag::from_u8(slice[0]) {
Some(t) => t,
None => return Err(Error::InvalidKeyTag(slice[0])),
};
let key_id = u64::from_be_bytes([
slice[1], slice[2], slice[3], slice[4], slice[5], slice[6], slice[7], slice[8],
]);
let version = u64::from_be_bytes([
slice[9], slice[10], slice[11], slice[12], slice[13], slice[14], slice[15], slice[16],
]);
Ok((tag, key_id, version))
}
#[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> {
let total_len = match SUBKEY_HEADER_SIZE.checked_add(payload.len()) {
Some(l) => l,
None => return Err(Error::RecordSizeOverflow),
};
if dst.len() < total_len {
return Err(Error::BufferTooShort {
expected: total_len,
actual: dst.len(),
});
}
let header = Self::encode_header(tag, key_id, version);
dst[..SUBKEY_HEADER_SIZE].copy_from_slice(&header);
dst[SUBKEY_HEADER_SIZE..total_len].copy_from_slice(payload);
Ok(total_len)
}
#[inline]
pub fn encode_to_buf(
tag: KeyTag,
key_id: u64,
version: u64,
payload: &[u8],
) -> Result<SubKeyBuf> {
let total_len = match SUBKEY_HEADER_SIZE.checked_add(payload.len()) {
Some(l) => l,
None => return Err(Error::RecordSizeOverflow),
};
if total_len <= SUBKEY_STACK_CAP {
let mut buf = [0u8; SUBKEY_STACK_CAP];
let header = Self::encode_header(tag, key_id, version);
buf[..SUBKEY_HEADER_SIZE].copy_from_slice(&header);
buf[SUBKEY_HEADER_SIZE..total_len].copy_from_slice(payload);
Ok(SubKeyBuf::Stack(buf, total_len as u8))
} else {
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(SubKeyBuf::Heap(vec))
}
}
#[inline]
pub fn try_encode_to_vec(
tag: KeyTag,
key_id: u64,
version: u64,
payload: &[u8],
) -> Result<Vec<u8>> {
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)
}
#[inline]
pub fn encode_to_vec(tag: KeyTag, key_id: u64, version: u64, payload: &[u8]) -> Vec<u8> {
Self::try_encode_to_vec(tag, key_id, version, payload).unwrap_or_default()
}
}