wedb_embed 0.1.0

Embedded Kvrocks-compatible storage engine for WeDb
Documentation
use crate::meta::{KeyMeta, RedisType, generate_version};
use serde::{Deserialize, Serialize};

pub use super::sortable::{decode_sortable_f64, encode_sortable_f64};

/// 有序集合结构元数据(对标 Apache Kvrocks ZSetMetadata 26字节 / 紧凑25字节)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct ZSetMeta {
    pub base: KeyMeta,
}

impl ZSetMeta {
    pub const ENCODED_SIZE: usize = KeyMeta::ENCODED_SIZE;
    pub const KVROCKS_ENCODED_SIZE: usize = KeyMeta::KVROCKS_COMPLEX_ENCODED_SIZE;

    #[inline]
    pub fn new(expire_at: u64, version: u64, size: u64) -> Self {
        Self {
            base: KeyMeta::new(RedisType::ZSet, expire_at, version, size),
        }
    }

    #[inline]
    pub fn new_with_version(expire_at: u64, size: u64) -> Self {
        Self {
            base: KeyMeta::new(RedisType::ZSet, expire_at, generate_version(), size),
        }
    }

    #[inline]
    pub const fn size(&self) -> u64 {
        self.base.size
    }

    #[inline]
    pub const fn is_empty(&self) -> bool {
        self.base.size == 0
    }

    #[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] {
        self.base.encode()
    }

    #[inline]
    pub fn encode_kvrocks(&self) -> Vec<u8> {
        self.base.encode_kvrocks()
    }

    #[inline]
    pub fn decode(bytes: &[u8]) -> Option<Self> {
        let base = KeyMeta::decode(bytes)?;
        if base.rtype == RedisType::ZSet {
            Some(Self { base })
        } else {
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_zset_meta_roundtrip() {
        let meta = ZSetMeta::new(1700000000000, 202, 88);
        let enc = meta.encode();
        assert_eq!(enc.len(), ZSetMeta::ENCODED_SIZE);

        let dec = ZSetMeta::decode(&enc).expect("decode failed");
        assert_eq!(dec.base.rtype, RedisType::ZSet);
        assert_eq!(dec.base.expire_at, 1700000000000);
        assert_eq!(dec.base.version, 202);
        assert_eq!(dec.base.size, 88);

        let kv_enc = meta.encode_kvrocks();
        assert_eq!(kv_enc.len(), ZSetMeta::KVROCKS_ENCODED_SIZE);
        let kv_dec = ZSetMeta::decode(&kv_enc).expect("decode kvrocks failed");
        assert_eq!(kv_dec.base.rtype, RedisType::ZSet);
        assert_eq!(kv_dec.base.expire_at, 1700000000000);
        assert_eq!(kv_dec.base.version, 202);
        assert_eq!(kv_dec.base.size, 88);
    }

    #[test]
    fn test_score_sortable_f64_ordering() {
        let test_values = vec![
            f64::NEG_INFINITY,
            -1000.5,
            -1.0,
            -0.0001,
            -0.0,
            0.0,
            0.0001,
            1.0,
            1000.5,
            f64::INFINITY,
        ];

        for i in 0..test_values.len() - 1 {
            let a = test_values[i];
            let b = test_values[i + 1];
            let enc_a = encode_sortable_f64(a);
            let enc_b = encode_sortable_f64(b);
            assert!(
                enc_a <= enc_b,
                "Ordering failed for {} vs {}: {:?} vs {:?}",
                a,
                b,
                enc_a,
                enc_b
            );

            let dec_a = decode_sortable_f64(enc_a);
            let dec_b = decode_sortable_f64(enc_b);
            if a.is_nan() {
                assert!(dec_a.is_nan());
            } else if a == 0.0 && dec_a == 0.0 {
                // ok
            } else {
                assert_eq!(a, dec_a);
            }

            if b.is_nan() {
                assert!(dec_b.is_nan());
            } else if b == 0.0 && dec_b == 0.0 {
                // ok
            } else {
                assert_eq!(b, dec_b);
            }
        }
    }
}