use crate::{
key_composer::KeyTag,
meta::{KeyMeta, MetaOps, RedisType},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, bitcode::Encode, bitcode::Decode)]
#[repr(u8)]
pub enum JsonStorageFormat {
#[default]
Json = 0,
Cbor = 1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, bitcode::Encode, bitcode::Decode)]
pub struct JsonMeta {
pub base: KeyMeta,
pub format: JsonStorageFormat,
}
impl JsonMeta {
pub const ENCODED_SIZE: usize = KeyMeta::ENCODED_SIZE + 1; pub const KVROCKS_ENCODED_SIZE: usize = KeyMeta::KVROCKS_SINGLE_KV_ENCODED_SIZE + 1;
#[inline]
pub fn new(expire_at: u64, version: u64, size: u64) -> Self {
Self {
base: KeyMeta::new(RedisType::Json, expire_at, version, size),
format: JsonStorageFormat::Json,
}
}
#[inline]
pub fn new_with_version(expire_at: u64, size: u64) -> Self {
Self {
base: KeyMeta::new_with_version(RedisType::Json, expire_at, size),
format: JsonStorageFormat::Json,
}
}
#[inline]
pub fn with_format(format: JsonStorageFormat, expire_at: u64, version: u64, size: u64) -> Self {
Self {
base: KeyMeta::new(RedisType::Json, expire_at, version, size),
format,
}
}
#[inline]
pub fn is_expired(&self, now_ms: u64) -> bool {
self.base.is_expired(now_ms)
}
#[inline]
pub fn encode(&self) -> [u8; Self::ENCODED_SIZE] {
let mut buf = [0u8; Self::ENCODED_SIZE];
let base_enc = self.base.encode();
buf[..KeyMeta::ENCODED_SIZE].copy_from_slice(&base_enc);
buf[KeyMeta::ENCODED_SIZE] = self.format as u8;
buf
}
#[inline]
pub fn encode_kvrocks(&self) -> [u8; Self::KVROCKS_ENCODED_SIZE] {
let mut buf = [0u8; Self::KVROCKS_ENCODED_SIZE];
let flags =
KeyMeta::META_64BIT_ENCODING_MASK | (self.base.rtype as u8 & KeyMeta::META_TYPE_MASK);
buf[0] = flags;
buf[1..9].copy_from_slice(&self.base.expire_at.to_be_bytes());
buf[9] = self.format as u8;
buf
}
#[inline]
pub fn decode(bytes: &[u8]) -> Option<(Self, &[u8])> {
if bytes.is_empty() {
return None;
}
if bytes.len() >= Self::ENCODED_SIZE
&& bytes[0] == RedisType::Json as u8
&& let Some(base) = KeyMeta::decode(&bytes[..KeyMeta::ENCODED_SIZE])
{
let format = match bytes[KeyMeta::ENCODED_SIZE] {
1 => JsonStorageFormat::Cbor,
_ => JsonStorageFormat::Json,
};
let payload = &bytes[Self::ENCODED_SIZE..];
return Some((Self { base, format }, payload));
}
if bytes.len() >= Self::KVROCKS_ENCODED_SIZE
&& (bytes[0] & KeyMeta::META_TYPE_MASK == RedisType::Json as u8)
&& (bytes[0] & KeyMeta::META_64BIT_ENCODING_MASK != 0)
&& let Some(base) = KeyMeta::decode(&bytes[..KeyMeta::KVROCKS_SINGLE_KV_ENCODED_SIZE])
{
let format = match bytes[KeyMeta::KVROCKS_SINGLE_KV_ENCODED_SIZE] {
1 => JsonStorageFormat::Cbor,
_ => JsonStorageFormat::Json,
};
let payload = &bytes[Self::KVROCKS_ENCODED_SIZE..];
return Some((Self { base, format }, payload));
}
if let Some(&first_non_ws) = bytes.iter().find(|&&b| !b.is_ascii_whitespace())
&& matches!(
first_non_ws,
b'{' | b'[' | b'"' | b't' | b'f' | b'n' | b'0'..=b'9' | b'-'
)
{
return Some((Self::new(0, 0, bytes.len() as u64), bytes));
}
None
}
}
pub fn encode_json_value(meta: &JsonMeta, payload: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(JsonMeta::ENCODED_SIZE + payload.len());
out.extend_from_slice(&meta.encode());
out.extend_from_slice(payload);
out
}
impl MetaOps for JsonMeta {
const TAG: &[u8] = KeyTag::JsonMeta.as_slice();
type EncodedBytes = [u8; Self::ENCODED_SIZE];
#[inline]
fn decode(bytes: &[u8]) -> Option<Self> {
Self::decode(bytes).map(|(m, _)| m)
}
#[inline]
fn is_expired(&self, now_ms: u64) -> bool {
self.base.is_expired(now_ms)
}
#[inline]
fn encode_bytes(&self) -> Self::EncodedBytes {
self.encode()
}
#[inline]
fn base(&self) -> &KeyMeta {
&self.base
}
#[inline]
fn base_mut(&mut self) -> &mut KeyMeta {
&mut self.base
}
}