use std::ops::Deref;
use std::sync::{Arc, OnceLock};
use lance_core::deepsize::{Context, DeepSizeOf};
use lance_core::{Error, Result};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::pb;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeepSizeOf)]
pub struct ExternalFile {
pub path: String,
pub offset: u64,
pub size: u64,
}
#[derive(Clone)]
pub struct InlineRowIds {
inner: Arc<InlineRowIdsInner>,
}
struct InlineRowIdsInner {
data: Vec<u8>,
digest: OnceLock<[u8; 32]>,
}
impl InlineRowIds {
pub fn digest(&self) -> &[u8; 32] {
self.inner
.digest
.get_or_init(|| blake3::hash(&self.inner.data).into())
}
}
impl From<Vec<u8>> for InlineRowIds {
fn from(data: Vec<u8>) -> Self {
Self {
inner: Arc::new(InlineRowIdsInner {
data,
digest: OnceLock::new(),
}),
}
}
}
impl Deref for InlineRowIds {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.inner.data
}
}
impl std::fmt::Debug for InlineRowIds {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.data.fmt(f)
}
}
impl PartialEq for InlineRowIds {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.inner, &other.inner) || self.inner.data == other.inner.data
}
}
impl Eq for InlineRowIds {}
impl Serialize for InlineRowIds {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
self.inner.data.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for InlineRowIds {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
Vec::<u8>::deserialize(deserializer).map(Self::from)
}
}
impl DeepSizeOf for InlineRowIds {
fn deep_size_of_children(&self, context: &mut Context) -> usize {
self.inner.deep_size_of_children(context)
}
}
impl DeepSizeOf for InlineRowIdsInner {
fn deep_size_of_children(&self, context: &mut Context) -> usize {
self.data.deep_size_of_children(context)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeepSizeOf)]
pub enum RowIdMeta {
Inline(InlineRowIds),
External(ExternalFile),
}
impl TryFrom<pb::data_fragment::RowIdSequence> for RowIdMeta {
type Error = Error;
fn try_from(value: pb::data_fragment::RowIdSequence) -> Result<Self> {
match value {
pb::data_fragment::RowIdSequence::InlineRowIds(data) => Ok(Self::Inline(data.into())),
pb::data_fragment::RowIdSequence::ExternalRowIds(file) => {
Ok(Self::External(ExternalFile {
path: file.path.clone(),
offset: file.offset,
size: file.size,
}))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::format::Fragment;
#[test]
fn inline_row_ids_digest_identifies_contents() {
let first = InlineRowIds::from(vec![1, 2, 3]);
let same = InlineRowIds::from(vec![1, 2, 3]);
let other = InlineRowIds::from(vec![1, 2, 4]);
assert_eq!(first.digest(), same.digest());
assert_ne!(first.digest(), other.digest());
assert_eq!(first, same);
assert_ne!(first, other);
let memoized = *first.digest();
assert_eq!(first.digest(), &memoized);
}
#[test]
fn inline_row_ids_clones_share_bytes_and_memo() {
let first = InlineRowIds::from(vec![1, 2, 3]);
let cloned = first.clone();
assert!(std::ptr::eq(first.as_ptr(), cloned.as_ptr()));
assert!(std::ptr::eq(first.digest(), cloned.digest()));
let separate = InlineRowIds::from(vec![1, 2, 3]);
assert_eq!(first, separate);
assert!(!std::ptr::eq(first.as_ptr(), separate.as_ptr()));
let fresh = InlineRowIds::from(vec![4, 5, 6]);
let fresh_clone = fresh.clone();
let via_clone = *fresh_clone.digest();
assert_eq!(fresh.digest(), &via_clone);
}
#[test]
fn inline_row_ids_serializes_as_bare_bytes() {
let meta = RowIdMeta::Inline(InlineRowIds::from(vec![7, 8, 9]));
let json = serde_json::to_string(&meta).unwrap();
assert_eq!(json, r#"{"Inline":[7,8,9]}"#);
assert_eq!(serde_json::from_str::<RowIdMeta>(&json).unwrap(), meta);
let fragment = Fragment {
row_id_meta: Some(meta.clone()),
..Fragment::new(0)
};
let restored = Fragment::try_from(pb::DataFragment::from(&fragment)).unwrap();
assert_eq!(restored.row_id_meta, Some(meta));
}
}