lance_core/cache/codec.rs
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright The Lance Authors
3
4//! Serialization codecs for cache entries.
5//!
6//! Implement [`CacheCodecImpl`] on concrete types, then use
7//! [`CacheCodec::from_impl`] to produce a type-erased codec for the cache.
8
9use std::sync::Arc;
10
11use bytes::Bytes;
12
13use crate::Result;
14
15// ---------------------------------------------------------------------------
16// CacheCodecImpl — trait for serializable cache entry types
17// ---------------------------------------------------------------------------
18
19/// Serialization trait for cache entries.
20///
21/// **Experimental**: the serialized format is not stable and may change
22/// between releases without notice.
23///
24/// Implement this on concrete types that need to survive serialization
25/// through a persistent cache backend. Then wire it into a [`CacheKey`](super::CacheKey)
26/// via [`CacheCodec::from_impl`]:
27///
28/// ```ignore
29/// impl CacheCodecImpl for MyData {
30/// fn serialize(&self, w: &mut dyn Write) -> Result<()> { /* ... */ }
31/// fn deserialize(data: &Bytes) -> Result<Self> { /* ... */ }
32/// }
33///
34/// impl CacheKey for MyDataKey {
35/// type ValueType = MyData;
36/// fn codec() -> Option<CacheCodec> {
37/// Some(CacheCodec::from_impl::<MyData>())
38/// }
39/// // ...
40/// }
41/// ```
42pub trait CacheCodecImpl: Send + Sync {
43 fn serialize(&self, writer: &mut dyn std::io::Write) -> Result<()>;
44
45 fn deserialize(data: &Bytes) -> Result<Self>
46 where
47 Self: Sized;
48}
49
50// ---------------------------------------------------------------------------
51// CacheCodec — type-erased codec passed to backends
52// ---------------------------------------------------------------------------
53
54pub(crate) type ArcAny = Arc<dyn std::any::Any + Send + Sync>;
55
56/// Type-erased codec for serializing and deserializing cache entries.
57///
58/// `CacheCodec` is two plain function pointers — it is `Copy` and has no
59/// heap allocation. Construct one via [`CacheCodec::from_impl`] for types
60/// that implement [`CacheCodecImpl`], or [`CacheCodec::new`] for custom
61/// cases (e.g. when the orphan rule prevents a direct impl).
62#[derive(Copy, Clone)]
63pub struct CacheCodec {
64 pub(crate) serialize: fn(&ArcAny, &mut dyn std::io::Write) -> Result<()>,
65 pub(crate) deserialize: fn(&Bytes) -> Result<ArcAny>,
66}
67
68impl std::fmt::Debug for CacheCodec {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 f.debug_struct("CacheCodec").finish_non_exhaustive()
71 }
72}
73
74fn serialize_via_impl<T: CacheCodecImpl + 'static>(
75 any: &ArcAny,
76 writer: &mut dyn std::io::Write,
77) -> Result<()> {
78 let val = any
79 .downcast_ref::<T>()
80 .expect("CacheCodec::serialize called with wrong type (this is a bug in the cache layer)");
81 val.serialize(writer)
82}
83
84fn deserialize_via_impl<T: CacheCodecImpl + 'static>(data: &Bytes) -> Result<ArcAny> {
85 let val = T::deserialize(data)?;
86 Ok(Arc::new(val) as ArcAny)
87}
88
89impl CacheCodec {
90 /// Create a `CacheCodec` from plain function pointers.
91 ///
92 /// Prefer [`from_impl`](Self::from_impl) when the value type implements
93 /// [`CacheCodecImpl`]. Use this for types where a direct impl isn't
94 /// possible (e.g. orphan rule prevents it).
95 pub fn new(
96 serialize: fn(&ArcAny, &mut dyn std::io::Write) -> Result<()>,
97 deserialize: fn(&Bytes) -> Result<ArcAny>,
98 ) -> Self {
99 Self {
100 serialize,
101 deserialize,
102 }
103 }
104
105 /// Create a `CacheCodec` from a [`CacheCodecImpl`] implementation.
106 ///
107 /// For **sized** types stored directly in the cache. The codec
108 /// downcasts `&dyn Any` to `&T` for serialization and returns `Arc<T>`
109 /// from deserialization.
110 pub fn from_impl<T: CacheCodecImpl + 'static>() -> Self {
111 Self {
112 serialize: serialize_via_impl::<T>,
113 deserialize: deserialize_via_impl::<T>,
114 }
115 }
116
117 pub fn serialize(&self, value: &ArcAny, writer: &mut dyn std::io::Write) -> Result<()> {
118 (self.serialize)(value, writer)
119 }
120
121 pub fn deserialize(&self, data: &Bytes) -> Result<ArcAny> {
122 (self.deserialize)(data)
123 }
124}