thunderdb/
value.rs

1//! Summary: Zero-copy value types for Phase 2 read dominance.
2//! Copyright (c) YOAB. All rights reserved.
3//!
4//! This module provides distinct types for borrowed vs owned values:
5//! - `BorrowedValue`: A reference tied to a transaction's lifetime
6//! - `OwnedValue`: An owned copy that can outlive the transaction
7//!
8//! # Design Philosophy
9//!
10//! Thunder provides explicit type-level distinction between borrowed and
11//! owned data. This makes the zero-copy contract clear at compile time:
12//!
13//! - `BorrowedValue<'tx>` - Cannot outlive the transaction
14//! - `OwnedValue` - Owns its data, can be stored freely
15//!
16//! # Performance Considerations
17//!
18//! - `BorrowedValue` has zero allocation overhead
19//! - `OwnedValue` allocates when created but can be passed around freely
20//! - Use `BorrowedValue` when processing data within a transaction
21//! - Use `OwnedValue` when data needs to outlive the transaction
22
23use std::borrow::Cow;
24use std::ops::Deref;
25
26/// A borrowed reference to a value, tied to a transaction's lifetime.
27///
28/// This type provides zero-copy access to values stored in the database.
29/// The reference is valid for the lifetime of the transaction that created it.
30///
31/// # Lifetime
32///
33/// The lifetime parameter `'tx` represents the transaction lifetime.
34/// The value cannot outlive the transaction:
35///
36/// ```ignore
37/// let borrowed: BorrowedValue<'_>;
38/// {
39///     let rtx = db.read_tx();
40///     borrowed = rtx.get_borrowed(b"key").unwrap();
41/// } // Error: rtx dropped but borrowed still in use
42/// ```
43///
44/// # Zero-Copy
45///
46/// No data is copied when creating a `BorrowedValue`. The internal slice
47/// points directly to the database's memory.
48#[derive(Debug, Clone, Copy)]
49pub struct BorrowedValue<'tx> {
50    /// The underlying byte slice.
51    data: &'tx [u8],
52}
53
54impl<'tx> BorrowedValue<'tx> {
55    /// Creates a new borrowed value from a slice.
56    #[inline]
57    pub(crate) fn new(data: &'tx [u8]) -> Self {
58        Self { data }
59    }
60
61    /// Returns the value as a byte slice.
62    #[inline]
63    pub fn as_bytes(&self) -> &[u8] {
64        self.data
65    }
66
67    /// Returns the length of the value in bytes.
68    #[inline]
69    pub fn len(&self) -> usize {
70        self.data.len()
71    }
72
73    /// Returns true if the value is empty.
74    #[inline]
75    pub fn is_empty(&self) -> bool {
76        self.data.is_empty()
77    }
78
79    /// Converts to an owned value.
80    ///
81    /// This allocates a new `Vec<u8>` with a copy of the data.
82    #[inline]
83    pub fn to_owned(&self) -> OwnedValue {
84        OwnedValue::new(self.data.to_vec())
85    }
86
87    /// Converts to a `Cow` for flexible ownership.
88    #[inline]
89    pub fn to_cow(&self) -> Cow<'tx, [u8]> {
90        Cow::Borrowed(self.data)
91    }
92}
93
94impl<'tx> Deref for BorrowedValue<'tx> {
95    type Target = [u8];
96
97    #[inline]
98    fn deref(&self) -> &Self::Target {
99        self.data
100    }
101}
102
103impl<'tx> AsRef<[u8]> for BorrowedValue<'tx> {
104    #[inline]
105    fn as_ref(&self) -> &[u8] {
106        self.data
107    }
108}
109
110impl<'tx> PartialEq<[u8]> for BorrowedValue<'tx> {
111    fn eq(&self, other: &[u8]) -> bool {
112        self.data == other
113    }
114}
115
116impl<'tx> PartialEq<&[u8]> for BorrowedValue<'tx> {
117    fn eq(&self, other: &&[u8]) -> bool {
118        self.data == *other
119    }
120}
121
122impl<'tx> PartialEq<Vec<u8>> for BorrowedValue<'tx> {
123    fn eq(&self, other: &Vec<u8>) -> bool {
124        self.data == other.as_slice()
125    }
126}
127
128impl<'tx> From<&'tx [u8]> for BorrowedValue<'tx> {
129    fn from(data: &'tx [u8]) -> Self {
130        Self::new(data)
131    }
132}
133
134/// An owned value that can outlive the transaction.
135///
136/// This type holds an owned copy of the data, allowing it to be stored
137/// and used after the transaction has ended.
138///
139/// # Allocation
140///
141/// Creating an `OwnedValue` allocates memory for the data. Use
142/// `BorrowedValue` when possible to avoid this overhead.
143///
144/// # Example
145///
146/// ```ignore
147/// let owned: OwnedValue;
148/// {
149///     let rtx = db.read_tx();
150///     owned = rtx.get_owned(b"key").unwrap();
151/// } // Transaction dropped
152///
153/// // OwnedValue is still valid
154/// println!("Value: {:?}", owned.as_ref());
155/// ```
156#[derive(Debug, Clone, PartialEq, Eq, Hash)]
157pub struct OwnedValue {
158    /// The owned byte data.
159    data: Vec<u8>,
160}
161
162impl OwnedValue {
163    /// Creates a new owned value.
164    #[inline]
165    pub fn new(data: Vec<u8>) -> Self {
166        Self { data }
167    }
168
169    /// Creates an owned value from a slice (copies the data).
170    #[inline]
171    pub fn from_slice(data: &[u8]) -> Self {
172        Self {
173            data: data.to_vec(),
174        }
175    }
176
177    /// Returns the value as a byte slice.
178    #[inline]
179    pub fn as_bytes(&self) -> &[u8] {
180        &self.data
181    }
182
183    /// Returns the length of the value in bytes.
184    #[inline]
185    pub fn len(&self) -> usize {
186        self.data.len()
187    }
188
189    /// Returns true if the value is empty.
190    #[inline]
191    pub fn is_empty(&self) -> bool {
192        self.data.is_empty()
193    }
194
195    /// Consumes self and returns the inner Vec.
196    #[inline]
197    pub fn into_vec(self) -> Vec<u8> {
198        self.data
199    }
200
201    /// Returns a borrowed view of this value.
202    #[inline]
203    pub fn as_borrowed(&self) -> BorrowedValue<'_> {
204        BorrowedValue::new(&self.data)
205    }
206}
207
208impl Deref for OwnedValue {
209    type Target = [u8];
210
211    #[inline]
212    fn deref(&self) -> &Self::Target {
213        &self.data
214    }
215}
216
217impl AsRef<[u8]> for OwnedValue {
218    #[inline]
219    fn as_ref(&self) -> &[u8] {
220        &self.data
221    }
222}
223
224impl From<Vec<u8>> for OwnedValue {
225    fn from(data: Vec<u8>) -> Self {
226        Self::new(data)
227    }
228}
229
230impl From<&[u8]> for OwnedValue {
231    fn from(data: &[u8]) -> Self {
232        Self::from_slice(data)
233    }
234}
235
236impl<const N: usize> From<&[u8; N]> for OwnedValue {
237    fn from(data: &[u8; N]) -> Self {
238        Self::from_slice(data.as_slice())
239    }
240}
241
242impl PartialEq<[u8]> for OwnedValue {
243    fn eq(&self, other: &[u8]) -> bool {
244        self.data.as_slice() == other
245    }
246}
247
248impl PartialEq<&[u8]> for OwnedValue {
249    fn eq(&self, other: &&[u8]) -> bool {
250        self.data.as_slice() == *other
251    }
252}
253
254impl PartialEq<Vec<u8>> for OwnedValue {
255    fn eq(&self, other: &Vec<u8>) -> bool {
256        &self.data == other
257    }
258}
259
260/// A value that may be either borrowed or owned.
261///
262/// This enum provides flexibility when the ownership semantics
263/// depend on runtime conditions.
264///
265/// # Performance
266///
267/// The borrowed variant has zero allocation overhead.
268/// The owned variant allocates once on creation.
269#[derive(Debug, Clone)]
270pub enum MaybeOwnedValue<'a> {
271    /// A borrowed reference.
272    Borrowed(BorrowedValue<'a>),
273    /// An owned value.
274    Owned(OwnedValue),
275}
276
277impl<'a> MaybeOwnedValue<'a> {
278    /// Returns the value as a byte slice.
279    #[inline]
280    pub fn as_bytes(&self) -> &[u8] {
281        match self {
282            Self::Borrowed(b) => b.as_bytes(),
283            Self::Owned(o) => o.as_bytes(),
284        }
285    }
286
287    /// Returns the length of the value.
288    #[inline]
289    pub fn len(&self) -> usize {
290        match self {
291            Self::Borrowed(b) => b.len(),
292            Self::Owned(o) => o.len(),
293        }
294    }
295
296    /// Returns true if the value is empty.
297    #[inline]
298    pub fn is_empty(&self) -> bool {
299        self.len() == 0
300    }
301
302    /// Converts to an owned value, cloning if necessary.
303    pub fn into_owned(self) -> OwnedValue {
304        match self {
305            Self::Borrowed(b) => b.to_owned(),
306            Self::Owned(o) => o,
307        }
308    }
309
310    /// Returns true if this is a borrowed value.
311    #[inline]
312    pub fn is_borrowed(&self) -> bool {
313        matches!(self, Self::Borrowed(_))
314    }
315
316    /// Returns true if this is an owned value.
317    #[inline]
318    pub fn is_owned(&self) -> bool {
319        matches!(self, Self::Owned(_))
320    }
321}
322
323impl<'a> AsRef<[u8]> for MaybeOwnedValue<'a> {
324    fn as_ref(&self) -> &[u8] {
325        self.as_bytes()
326    }
327}
328
329impl<'a> From<BorrowedValue<'a>> for MaybeOwnedValue<'a> {
330    fn from(value: BorrowedValue<'a>) -> Self {
331        Self::Borrowed(value)
332    }
333}
334
335impl<'a> From<OwnedValue> for MaybeOwnedValue<'a> {
336    fn from(value: OwnedValue) -> Self {
337        Self::Owned(value)
338    }
339}
340
341#[cfg(test)]
342mod tests {
343    use super::*;
344
345    #[test]
346    fn test_borrowed_value() {
347        let data = b"hello world";
348        let borrowed = BorrowedValue::new(data);
349
350        assert_eq!(borrowed.len(), 11);
351        assert!(!borrowed.is_empty());
352        assert_eq!(borrowed.as_bytes(), b"hello world");
353        assert!(borrowed == b"hello world".as_slice());
354    }
355
356    #[test]
357    fn test_borrowed_to_owned() {
358        let data = b"test data";
359        let borrowed = BorrowedValue::new(data);
360        let owned = borrowed.to_owned();
361
362        assert_eq!(owned.as_bytes(), borrowed.as_bytes());
363        assert_eq!(owned.len(), 9);
364    }
365
366    #[test]
367    fn test_owned_value() {
368        let owned = OwnedValue::new(b"test".to_vec());
369
370        assert_eq!(owned.len(), 4);
371        assert!(!owned.is_empty());
372        assert_eq!(owned.as_bytes(), b"test");
373    }
374
375    #[test]
376    fn test_owned_value_conversions() {
377        let owned = OwnedValue::from_slice(b"slice");
378        assert_eq!(owned.as_bytes(), b"slice");
379
380        let owned2: OwnedValue = b"array".into();
381        assert_eq!(owned2.as_bytes(), b"array");
382
383        let vec: Vec<u8> = owned2.into_vec();
384        assert_eq!(vec, b"array");
385    }
386
387    #[test]
388    fn test_maybe_owned_value() {
389        let data = b"borrowed";
390        let borrowed = BorrowedValue::new(data);
391        let maybe_borrowed = MaybeOwnedValue::Borrowed(borrowed);
392
393        assert!(maybe_borrowed.is_borrowed());
394        assert!(!maybe_borrowed.is_owned());
395        assert_eq!(maybe_borrowed.as_bytes(), b"borrowed");
396
397        let owned = OwnedValue::new(b"owned".to_vec());
398        let maybe_owned = MaybeOwnedValue::Owned(owned);
399
400        assert!(!maybe_owned.is_borrowed());
401        assert!(maybe_owned.is_owned());
402        assert_eq!(maybe_owned.as_bytes(), b"owned");
403    }
404
405    #[test]
406    fn test_maybe_owned_into_owned() {
407        let data = b"test";
408        let borrowed = BorrowedValue::new(data);
409        let maybe = MaybeOwnedValue::Borrowed(borrowed);
410        let owned = maybe.into_owned();
411
412        assert_eq!(owned.as_bytes(), b"test");
413    }
414}