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    /// Gets a reference to the data.
86    ///
87    /// Returns None if the entry is empty, otherwise returns a slice
88    /// from offset to offset+size.
89    pub fn get_data(&self) -> Option<&[u8]> {
90        self.data.as_ref().map(|d| {
91            let start = self.offset.min(d.len());
92            let end = (self.offset + self.size).min(d.len());
93            &d[start..end]
94        })
95    }
96
97    /// Sets the data from a byte slice (copies the slice).
98    pub fn set_data(&mut self, data: &[u8]) {
99        self.data = Some(Bytes::copy_from_slice(data));
100        self.offset = 0;
101        self.size = data.len();
102    }
103
104    /// Sets the data from an owned `Vec<u8>` — zero-copy.
105    pub fn set_data_vec(&mut self, data: Vec<u8>) {
106        self.size = data.len();
107        self.data = Some(Bytes::from(data));
108        self.offset = 0;
109    }
110
111    /// Sets the data from an existing `Bytes` — zero-copy.
112    pub fn set_data_bytes(&mut self, data: Bytes) {
113        self.size = data.len();
114        self.data = Some(data);
115        self.offset = 0;
116    }
117
118    /// Creates a DatabaseEntry from a byte slice.
119    ///
120    /// Alias for `from_bytes`.
121    pub fn from_data(data: &[u8]) -> Self {
122        Self::from_bytes(data)
123    }
124
125    /// Gets the data as a byte slice, returning an empty slice if no data.
126    ///
127    /// Convenience method that unwraps the Option from `get_data()`.
128    pub fn data(&self) -> &[u8] {
129        self.get_data().unwrap_or(&[])
130    }
131
132    /// Gets the size of the data.
133    pub fn get_size(&self) -> usize {
134        self.size
135    }
136
137    /// Sets the offset within the data array.
138    pub fn set_offset(&mut self, offset: usize) {
139        self.offset = offset;
140    }
141
142    /// Gets the offset within the data array.
143    pub fn get_offset(&self) -> usize {
144        self.offset
145    }
146
147    /// Sets the size of the data.
148    pub fn set_size(&mut self, size: usize) {
149        self.size = size;
150    }
151
152    /// Configures this entry as a partial entry.
153    ///
154    /// Partial entries are used to read or write only a portion of a record.
155    pub fn set_partial(&mut self, offset: usize, length: usize, partial: bool) {
156        self.partial = partial;
157        self.partial_offset = offset;
158        self.partial_length = length;
159    }
160
161    /// Returns whether this is a partial entry.
162    pub fn is_partial(&self) -> bool {
163        self.partial
164    }
165
166    /// Gets the partial offset.
167    pub fn get_partial_offset(&self) -> usize {
168        self.partial_offset
169    }
170
171    /// Gets the partial length.
172    pub fn get_partial_length(&self) -> usize {
173        self.partial_length
174    }
175
176    /// Clears the entry, removing all data.
177    pub fn clear(&mut self) {
178        self.data = None;
179        self.offset = 0;
180        self.size = 0;
181        self.partial = false;
182        self.partial_offset = 0;
183        self.partial_length = 0;
184    }
185
186    /// Returns true if the entry is empty (has no data).
187    pub fn is_empty(&self) -> bool {
188        self.data.is_none() || self.size == 0
189    }
190}
191
192impl Default for DatabaseEntry {
193    fn default() -> Self {
194        Self::new()
195    }
196}
197
198impl From<Vec<u8>> for DatabaseEntry {
199    fn from(data: Vec<u8>) -> Self {
200        Self::from_vec(data)
201    }
202}
203
204impl From<Bytes> for DatabaseEntry {
205    fn from(data: Bytes) -> Self {
206        Self::from_bytes_ref(data)
207    }
208}
209
210impl From<&[u8]> for DatabaseEntry {
211    fn from(data: &[u8]) -> Self {
212        Self::from_bytes(data)
213    }
214}
215
216impl From<String> for DatabaseEntry {
217    fn from(s: String) -> Self {
218        Self::from_vec(s.into_bytes())
219    }
220}
221
222impl From<&str> for DatabaseEntry {
223    fn from(s: &str) -> Self {
224        Self::from_bytes(s.as_bytes())
225    }
226}
227
228#[cfg(test)]
229mod tests {
230    use super::*;
231
232    #[test]
233    fn test_new_empty() {
234        let entry = DatabaseEntry::new();
235        assert!(entry.is_empty());
236        assert_eq!(entry.get_size(), 0);
237        assert_eq!(entry.get_data(), None);
238    }
239
240    #[test]
241    fn test_from_bytes() {
242        let data = b"hello";
243        let entry = DatabaseEntry::from_bytes(data);
244        assert!(!entry.is_empty());
245        assert_eq!(entry.get_size(), 5);
246        assert_eq!(entry.get_data(), Some(&data[..]));
247    }
248
249    #[test]
250    fn test_from_vec() {
251        let data = vec![1, 2, 3, 4, 5];
252        let entry = DatabaseEntry::from_vec(data.clone());
253        assert_eq!(entry.get_size(), 5);
254        assert_eq!(entry.get_data(), Some(&data[..]));
255    }
256
257    #[test]
258    fn test_set_data() {
259        let mut entry = DatabaseEntry::new();
260        entry.set_data(b"test");
261        assert_eq!(entry.get_size(), 4);
262        assert_eq!(entry.get_data(), Some(b"test".as_ref()));
263    }
264
265    #[test]
266    fn test_set_data_vec() {
267        let mut entry = DatabaseEntry::new();
268        let data = vec![10, 20, 30];
269        entry.set_data_vec(data.clone());
270        assert_eq!(entry.get_size(), 3);
271        assert_eq!(entry.get_data(), Some(&data[..]));
272    }
273
274    #[test]
275    fn test_offset_and_size() {
276        let mut entry = DatabaseEntry::from_bytes(b"hello world");
277        entry.set_offset(6);
278        entry.set_size(5);
279        assert_eq!(entry.get_data(), Some(b"world".as_ref()));
280    }
281
282    #[test]
283    fn test_partial_entry() {
284        let mut entry = DatabaseEntry::new();
285        assert!(!entry.is_partial());
286
287        entry.set_partial(10, 20, true);
288        assert!(entry.is_partial());
289        assert_eq!(entry.get_partial_offset(), 10);
290        assert_eq!(entry.get_partial_length(), 20);
291
292        entry.set_partial(0, 0, false);
293        assert!(!entry.is_partial());
294    }
295
296    #[test]
297    fn test_clear() {
298        let mut entry = DatabaseEntry::from_bytes(b"data");
299        assert!(!entry.is_empty());
300
301        entry.clear();
302        assert!(entry.is_empty());
303        assert_eq!(entry.get_size(), 0);
304        assert_eq!(entry.get_data(), None);
305    }
306
307    #[test]
308    fn test_default() {
309        let entry = DatabaseEntry::default();
310        assert!(entry.is_empty());
311        assert_eq!(entry.get_size(), 0);
312    }
313
314    #[test]
315    fn test_from_string() {
316        let entry = DatabaseEntry::from(String::from("test"));
317        assert_eq!(entry.get_data(), Some(b"test".as_ref()));
318    }
319
320    #[test]
321    fn test_from_str() {
322        let entry = DatabaseEntry::from("hello");
323        assert_eq!(entry.get_data(), Some(b"hello".as_ref()));
324    }
325
326    #[test]
327    fn test_clone() {
328        let entry1 = DatabaseEntry::from_bytes(b"original");
329        let entry2 = entry1.clone();
330        assert_eq!(entry1.get_data(), entry2.get_data());
331    }
332
333    #[test]
334    fn test_equality() {
335        let entry1 = DatabaseEntry::from_bytes(b"data");
336        let entry2 = DatabaseEntry::from_bytes(b"data");
337        let entry3 = DatabaseEntry::from_bytes(b"other");
338        assert_eq!(entry1, entry2);
339        assert_ne!(entry1, entry3);
340    }
341
342    #[test]
343    fn test_empty_slice() {
344        let entry = DatabaseEntry::from_bytes(b"");
345        assert!(entry.is_empty());
346        assert_eq!(entry.get_size(), 0);
347    }
348
349    #[test]
350    fn test_offset_beyond_data() {
351        let mut entry = DatabaseEntry::from_bytes(b"short");
352        entry.set_offset(10);
353        entry.set_size(5);
354        // Should return empty slice when offset is beyond data
355        assert_eq!(entry.get_data(), Some(&[][..]));
356    }
357
358    #[test]
359    fn test_size_beyond_data() {
360        let mut entry = DatabaseEntry::from_bytes(b"test");
361        entry.set_size(100);
362        // Should cap at actual data length
363        assert_eq!(entry.get_data(), Some(b"test".as_ref()));
364    }
365}