wcol 0.1.1

Collection objects (list/set/hash/zset/geo) and BfTree range-index operator layer for wedb
//! 集合对象二进制载荷编解码与输入构造辅助(AOF 回放与会话共用)
//!
//! 成员级过期队列单源在 [`crate::types::expiration_queue`](Hash/SortedSet 共用)。
//! 对象信封记录挂 `KeyTag::ObjectEnvelope` 物理键(带外类型通道,对标 C#
//! LogRecord.DataHeader.ValueIsObject 位),值为 `[1B GarnetObjectType 标签]
//! [bitcode 载荷]`——内层标签区分子类型,用户字符串值内容任意、互不干扰。

use wresp::{ArgSlice, SessionParseState};
use wval::GarnetObjectType;

use super::{
  hash::hash_object::HashObject, list::list_object::ListObject, set::set_object::SetObject,
  zset::sorted_set_object::SortedSetObject,
};
use crate::types::{ObjectInput, RespInputFlags, RespInputHeader};

/// 构造零拷贝 ObjectInput(借用 args 的字节指针,无需克隆任何参数与解析状态)
pub fn make_object_input<T: AsRef<[u8]>>(
  obj_type: GarnetObjectType,
  sub_id: u8,
  args: &[T],
  arg1: i32,
  arg2: i32,
) -> ObjectInput {
  let slices: Vec<ArgSlice> = args
    .iter()
    .map(|a| {
      let b = a.as_ref();
      ArgSlice::new(b.as_ptr(), b.len())
    })
    .collect();
  let mut parse_state = SessionParseState::new();
  parse_state.initialize_with_args(&slices);

  let mut header = RespInputHeader::new_with_type(obj_type, RespInputFlags::empty());
  header.set_sub_id(sub_id);
  ObjectInput {
    header,
    arg1,
    arg2,
    parse_state,
  }
}

/// 对象存载荷通用行为契约(对标 Garnet IGarnetObject 与 ObjectStoreRMW 抽象)
///
/// 在 garnet 中的相对路径:libs/server/Objects/Types/IGarnetObject.cs:IGarnetObject
/// 在 garnet 中的相对路径:libs/server/Objects/Types/GarnetObjectSerializer.cs:DeserializeInternal
pub trait GarnetObjectPayload: Sized + Default {
  /// 权威对象类型标识(对标 GarnetObjectType)
  const OBJECT_TAG: GarnetObjectType;

  /// 从对象信封载荷切片反序列化对象(解析失败回退 Default 空对象)
  ///
  /// 在 garnet 中的相对路径:libs/server/Objects/Types/GarnetObjectSerializer.cs:DeserializeInternal
  fn from_blob(raw: &[u8]) -> Self;

  /// 序列化对象为信封载荷字节向量
  ///
  /// 在 garnet 中的相对路径:libs/server/Objects/Types/GarnetObjectSerializer.cs:Serialize
  fn to_blob(&self) -> Vec<u8>;

  /// 检查集合对象是否为空
  fn is_empty(&self) -> bool;
}

/// 对象值信封编码:[类型标签][bitcode 载荷](信封记录挂 KeyTag::ObjectEnvelope 物理键)
///
/// C# 侧对象存值由 Tsavorite 经对象序列化器落盘(GarnetObjectSerializer.cs);
/// Rust 单库 wkv 模型下以值内信封承载,此处为信封编解码单点
#[inline]
pub fn obj_encode(tag: GarnetObjectType, payload: &[u8]) -> Vec<u8> {
  obj_encode_custom(tag as u8, payload)
}

/// 自定义对象值信封编码(支持任意 u8 扩展标签)
#[inline]
pub fn obj_encode_custom(tag: u8, payload: &[u8]) -> Vec<u8> {
  let mut out = Vec::with_capacity(payload.len() + 1);
  out.push(tag);
  out.extend_from_slice(payload);
  out
}

/// 对象值信封解码:校验类型标签后返回载荷切片(信封记录挂 KeyTag::ObjectEnvelope 物理键)
#[inline]
pub fn obj_decode(raw: &[u8], want: GarnetObjectType) -> Option<&[u8]> {
  obj_decode_custom(raw, want as u8)
}

/// 自定义对象值信封解码:校验任意 u8 类型标签后返回载荷切片
#[inline]
pub fn obj_decode_custom(raw: &[u8], want: u8) -> Option<&[u8]> {
  raw
    .split_first()
    .filter(|(t, _)| **t == want)
    .map(|(_, p)| p)
}

/// 对象信封堆内存估算(信封 raw = `[1B 类型标签][bitcode 载荷]`)
///
/// MEMORY USAGE 统计内层对象用:反序列化后取各对象 heap_memory_size
/// 增量记账(对标 C# ReadMethods 的 `ValueObject.HeapMemorySize` 对象堆估算段,
/// 记账单点为 [`crate::types::GarnetObjectBase::heap_memory_size`] 契约与各对象字段)。
/// 格式损坏/未知标签(含自定义类型段与保留段)按 0 计——信封记录物理尺寸
/// 已含序列化载荷本体,不重复计
pub fn object_heap_estimate(raw: &[u8]) -> i64 {
  let Some((&tag, payload)) = raw.split_first() else {
    return 0;
  };
  // C# 侧 MEMORY USAGE 持有已反序列化的 IGarnetObject 直接取 HeapMemorySize;
  // Rust 信封存存储,须先按类型标签剥壳反序列化(from_u8 单点分派)
  match GarnetObjectType::from_u8(tag) {
    Some(GarnetObjectType::SortedSet) => {
      SortedSetObject::deserialize_from_slice(payload).map_or(0, |o| o.heap_memory_size)
    }
    Some(GarnetObjectType::List) => {
      ListObject::deserialize_from_slice(payload).map_or(0, |o| o.heap_memory_size)
    }
    Some(GarnetObjectType::Hash) => {
      HashObject::deserialize_from_slice(payload).map_or(0, |o| o.heap_memory_size)
    }
    Some(GarnetObjectType::Set) => {
      SetObject::deserialize_from_slice(payload).map_or(0, |o| o.heap_memory_size)
    }
    _ => 0,
  }
}

/// 对象键装载/读写结果状态四态(跨 storage 与 resp 全链路统一收敛)
///
/// 对标 C# 全链单枚举 GarnetStatus(OK/NOTFOUND/WRONGTYPE),结合 Rust 异步引擎必要之磁盘候选降级(Degrade)。
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ObjLoad<T> {
  /// 磁盘候选:命令须降级异步重放(未写任何输出)
  Degrade,
  /// 键存在但信封类型不符(WRONGTYPE)
  WrongType,
  /// 键缺失(未找到/无候选/放弃写入)
  Missing,
  /// 命中/执行完成并产出载荷或结果
  Present(T),
}

impl<T> ObjLoad<T> {
  /// 结果提取(非 Present 分支返回 `fallback`)
  #[inline]
  pub fn unwrap_or(self, fallback: T) -> T {
    match self {
      Self::Present(v) => v,
      _ => fallback,
    }
  }

  /// 映射内部载荷
  #[inline]
  pub fn map<U>(self, f: impl FnOnce(T) -> U) -> ObjLoad<U> {
    match self {
      Self::Present(v) => ObjLoad::Present(f(v)),
      Self::Degrade => ObjLoad::Degrade,
      Self::WrongType => ObjLoad::WrongType,
      Self::Missing => ObjLoad::Missing,
    }
  }

  /// 转换为 Option
  #[inline]
  pub fn ok(self) -> Option<T> {
    match self {
      Self::Present(v) => Some(v),
      _ => None,
    }
  }

  /// 借用转换
  #[inline]
  pub fn as_ref(&self) -> ObjLoad<&T> {
    match self {
      Self::Present(v) => ObjLoad::Present(v),
      Self::Degrade => ObjLoad::Degrade,
      Self::WrongType => ObjLoad::WrongType,
      Self::Missing => ObjLoad::Missing,
    }
  }

  /// 可变借用转换
  #[inline]
  pub fn as_mut(&mut self) -> ObjLoad<&mut T> {
    match self {
      Self::Present(v) => ObjLoad::Present(v),
      Self::Degrade => ObjLoad::Degrade,
      Self::WrongType => ObjLoad::WrongType,
      Self::Missing => ObjLoad::Missing,
    }
  }

  /// 提取内部值或默认值
  #[inline]
  pub fn unwrap_or_default(self) -> T
  where
    T: Default,
  {
    self.unwrap_or_else(T::default)
  }

  /// 延迟求值提取内部值
  #[inline]
  pub fn unwrap_or_else(self, f: impl FnOnce() -> T) -> T {
    match self {
      Self::Present(v) => v,
      _ => f(),
    }
  }

  /// 是否为 Present
  #[inline]
  pub const fn is_present(&self) -> bool {
    matches!(self, Self::Present(_))
  }

  /// 是否为 Degrade
  #[inline]
  pub const fn is_degrade(&self) -> bool {
    matches!(self, Self::Degrade)
  }

  /// 是否为 WrongType
  #[inline]
  pub const fn is_wrong_type(&self) -> bool {
    matches!(self, Self::WrongType)
  }

  /// 是否为 Missing
  #[inline]
  pub const fn is_missing(&self) -> bool {
    matches!(self, Self::Missing)
  }
}