Skip to main content

noxu_db/
database_entry.rs

1//! Database entry for keys and data.
2//!
3
4use bytes::Bytes;
5
6/// Encodes database key and data items as byte arrays.
7///
8/// Both key and data items are represented by DatabaseEntry objects.
9/// Key and data byte arrays may refer to arrays of zero length up to
10/// arrays of essentially unlimited length.
11///
12/// Internally uses `bytes::Bytes` so that `clone()` is O(1) (reference-count
13/// increment) and `from_vec` / `set_data_vec` are zero-copy.
14///
15///
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct DatabaseEntry {
18    /// The data bytes.  `Bytes::clone()` is O(1).
19    data: Option<Bytes>,
20    /// Offset into the data array.
21    offset: usize,
22    /// Size of the data.
23    size: usize,
24    /// Whether this is a partial entry.
25    partial: bool,
26    /// Offset for partial operations.
27    partial_offset: usize,
28    /// Length for partial operations.
29    partial_length: usize,
30}
31
32impl DatabaseEntry {
33    /// Creates an empty DatabaseEntry.
34    pub fn new() -> Self {
35        Self {
36            data: None,
37            offset: 0,
38            size: 0,
39            partial: false,
40            partial_offset: 0,
41            partial_length: 0,
42        }
43    }
44
45    /// Creates a DatabaseEntry from a byte slice (copies the slice).
46    ///
47    /// Alias: `from_data` is also available.
48    pub fn from_bytes(data: &[u8]) -> Self {
49        Self {
50            data: Some(Bytes::copy_from_slice(data)),
51            offset: 0,
52            size: data.len(),
53            partial: false,
54            partial_offset: 0,
55            partial_length: 0,
56        }
57    }
58
59    /// Creates a DatabaseEntry from an owned `Vec<u8>` — zero-copy.
60    pub fn from_vec(data: Vec<u8>) -> Self {
61        let size = data.len();
62        Self {
63            data: Some(Bytes::from(data)),
64            offset: 0,
65            size,
66            partial: false,
67            partial_offset: 0,
68            partial_length: 0,
69        }
70    }
71
72    /// Creates a DatabaseEntry from an existing `Bytes` — zero-copy.
73    pub fn from_bytes_ref(data: Bytes) -> Self {
74        let size = data.len();
75        Self {
76            data: Some(data),
77            offset: 0,
78            size,
79            partial: false,
80            partial_offset: 0,
81            partial_length: 0,
82        }
83    }
84
85    /// Returns a reference to the data, or `None` if the entry is unset.
86    ///
87    /// Returns `None` if the entry has no data array at all, otherwise
88    /// returns a slice from offset to offset+size (which may be empty for
89    /// an explicit empty key).  Use [`data`](Self::data) for the common
90    /// case where an empty slice is acceptable for an unset entry.
91    pub fn data_opt(&self) -> Option<&[u8]> {
92        self.data.as_ref().map(|d| {
93            let start = self.offset.min(d.len());
94            let end = (self.offset + self.size).min(d.len());
95            &d[start..end]
96        })
97    }
98
99    /// Sets the data from a byte slice (copies the slice).
100    pub fn set_data(&mut self, data: &[u8]) {
101        self.data = Some(Bytes::copy_from_slice(data));
102        self.offset = 0;
103        self.size = data.len();
104    }
105
106    /// Sets the data from an owned `Vec<u8>` — zero-copy.
107    pub fn set_data_vec(&mut self, data: Vec<u8>) {
108        self.size = data.len();
109        self.data = Some(Bytes::from(data));
110        self.offset = 0;
111    }
112
113    /// Sets the data from an existing `Bytes` — zero-copy.
114    pub fn set_data_bytes(&mut self, data: Bytes) {
115        self.size = data.len();
116        self.data = Some(data);
117        self.offset = 0;
118    }
119
120    /// Creates a DatabaseEntry from a byte slice.
121    ///
122    /// Alias for `from_bytes`.
123    pub fn from_data(data: &[u8]) -> Self {
124        Self::from_bytes(data)
125    }
126
127    /// Gets the data as a byte slice, returning an empty slice if no data.
128    ///
129    /// Convenience method that unwraps the Option from `data_opt()`.
130    pub fn data(&self) -> &[u8] {
131        self.data_opt().unwrap_or(&[])
132    }
133
134    /// Returns the size of the data, in bytes.
135    pub fn len(&self) -> usize {
136        self.size
137    }
138
139    /// Sets the offset within the data array.
140    pub fn set_offset(&mut self, offset: usize) {
141        self.offset = offset;
142    }
143
144    /// Returns the offset within the data array.
145    pub fn offset(&self) -> usize {
146        self.offset
147    }
148
149    /// Sets the size of the data.
150    pub fn set_size(&mut self, size: usize) {
151        self.size = size;
152    }
153
154    /// Configures this entry as a partial entry.
155    ///
156    /// Partial entries are used to read or write only a portion of a record.
157    pub fn set_partial(&mut self, offset: usize, length: usize, partial: bool) {
158        self.partial = partial;
159        self.partial_offset = offset;
160        self.partial_length = length;
161    }
162
163    /// Returns whether this is a partial entry.
164    pub fn is_partial(&self) -> bool {
165        self.partial
166    }
167
168    /// Returns the partial offset.
169    pub fn partial_offset(&self) -> usize {
170        self.partial_offset
171    }
172
173    /// Returns the partial length.
174    pub fn partial_length(&self) -> usize {
175        self.partial_length
176    }
177
178    /// Clears the entry, removing all data.
179    pub fn clear(&mut self) {
180        self.data = None;
181        self.offset = 0;
182        self.size = 0;
183        self.partial = false;
184        self.partial_offset = 0;
185        self.partial_length = 0;
186    }
187
188    /// Returns true if the entry is empty (has no data).
189    pub fn is_empty(&self) -> bool {
190        self.data.is_none() || self.size == 0
191    }
192}
193
194impl Default for DatabaseEntry {
195    fn default() -> Self {
196        Self::new()
197    }
198}
199
200impl From<Vec<u8>> for DatabaseEntry {
201    fn from(data: Vec<u8>) -> Self {
202        Self::from_vec(data)
203    }
204}
205
206impl From<Bytes> for DatabaseEntry {
207    fn from(data: Bytes) -> Self {
208        Self::from_bytes_ref(data)
209    }
210}
211
212impl From<&[u8]> for DatabaseEntry {
213    fn from(data: &[u8]) -> Self {
214        Self::from_bytes(data)
215    }
216}
217
218impl From<String> for DatabaseEntry {
219    fn from(s: String) -> Self {
220        Self::from_vec(s.into_bytes())
221    }
222}
223
224impl From<&str> for DatabaseEntry {
225    fn from(s: &str) -> Self {
226        Self::from_bytes(s.as_bytes())
227    }
228}
229
230/// Lets a `DatabaseEntry` be passed directly to the byte-oriented
231/// `Database`/`Cursor` API (review P1-3: `impl AsRef<[u8]>` is the common
232/// key/value type; `DatabaseEntry` interoperates with it).
233impl AsRef<[u8]> for DatabaseEntry {
234    fn as_ref(&self) -> &[u8] {
235        self.data()
236    }
237}
238
239#[cfg(test)]
240mod tests {
241    use super::*;
242
243    #[test]
244    fn test_new_empty() {
245        let entry = DatabaseEntry::new();
246        assert!(entry.is_empty());
247        assert_eq!(entry.len(), 0);
248        assert_eq!(entry.data_opt(), None);
249    }
250
251    #[test]
252    fn test_from_bytes() {
253        let data = b"hello";
254        let entry = DatabaseEntry::from_bytes(data);
255        assert!(!entry.is_empty());
256        assert_eq!(entry.len(), 5);
257        assert_eq!(entry.data_opt(), Some(&data[..]));
258    }
259
260    #[test]
261    fn test_from_vec() {
262        let data = vec![1, 2, 3, 4, 5];
263        let entry = DatabaseEntry::from_vec(data.clone());
264        assert_eq!(entry.len(), 5);
265        assert_eq!(entry.data_opt(), Some(&data[..]));
266    }
267
268    #[test]
269    fn test_set_data() {
270        let mut entry = DatabaseEntry::new();
271        entry.set_data(b"test");
272        assert_eq!(entry.len(), 4);
273        assert_eq!(entry.data_opt(), Some(b"test".as_ref()));
274    }
275
276    #[test]
277    fn test_set_data_vec() {
278        let mut entry = DatabaseEntry::new();
279        let data = vec![10, 20, 30];
280        entry.set_data_vec(data.clone());
281        assert_eq!(entry.len(), 3);
282        assert_eq!(entry.data_opt(), Some(&data[..]));
283    }
284
285    #[test]
286    fn test_offset_and_size() {
287        let mut entry = DatabaseEntry::from_bytes(b"hello world");
288        entry.set_offset(6);
289        entry.set_size(5);
290        assert_eq!(entry.data_opt(), Some(b"world".as_ref()));
291    }
292
293    #[test]
294    fn test_partial_entry() {
295        let mut entry = DatabaseEntry::new();
296        assert!(!entry.is_partial());
297
298        entry.set_partial(10, 20, true);
299        assert!(entry.is_partial());
300        assert_eq!(entry.partial_offset(), 10);
301        assert_eq!(entry.partial_length(), 20);
302
303        entry.set_partial(0, 0, false);
304        assert!(!entry.is_partial());
305    }
306
307    #[test]
308    fn test_clear() {
309        let mut entry = DatabaseEntry::from_bytes(b"data");
310        assert!(!entry.is_empty());
311
312        entry.clear();
313        assert!(entry.is_empty());
314        assert_eq!(entry.len(), 0);
315        assert_eq!(entry.data_opt(), None);
316    }
317
318    #[test]
319    fn test_default() {
320        let entry = DatabaseEntry::default();
321        assert!(entry.is_empty());
322        assert_eq!(entry.len(), 0);
323    }
324
325    #[test]
326    fn test_from_string() {
327        let entry = DatabaseEntry::from(String::from("test"));
328        assert_eq!(entry.data_opt(), Some(b"test".as_ref()));
329    }
330
331    #[test]
332    fn test_from_str() {
333        let entry = DatabaseEntry::from("hello");
334        assert_eq!(entry.data_opt(), Some(b"hello".as_ref()));
335    }
336
337    #[test]
338    fn test_clone() {
339        let entry1 = DatabaseEntry::from_bytes(b"original");
340        let entry2 = entry1.clone();
341        assert_eq!(entry1.data_opt(), entry2.data_opt());
342    }
343
344    #[test]
345    fn test_equality() {
346        let entry1 = DatabaseEntry::from_bytes(b"data");
347        let entry2 = DatabaseEntry::from_bytes(b"data");
348        let entry3 = DatabaseEntry::from_bytes(b"other");
349        assert_eq!(entry1, entry2);
350        assert_ne!(entry1, entry3);
351    }
352
353    #[test]
354    fn test_empty_slice() {
355        let entry = DatabaseEntry::from_bytes(b"");
356        assert!(entry.is_empty());
357        assert_eq!(entry.len(), 0);
358    }
359
360    #[test]
361    fn test_offset_beyond_data() {
362        let mut entry = DatabaseEntry::from_bytes(b"short");
363        entry.set_offset(10);
364        entry.set_size(5);
365        // Should return empty slice when offset is beyond data
366        assert_eq!(entry.data_opt(), Some(&[][..]));
367    }
368
369    #[test]
370    fn test_size_beyond_data() {
371        let mut entry = DatabaseEntry::from_bytes(b"test");
372        entry.set_size(100);
373        // Should cap at actual data length
374        assert_eq!(entry.data_opt(), Some(b"test".as_ref()));
375    }
376}