Skip to main content

journal_core/file/
object_hash.rs

1use super::object::*;
2use crate::error::Result;
3use std::num::NonZeroU64;
4use zerocopy::{ByteSlice, ByteSliceMut, Ref, SplitByteSlice, SplitByteSliceMut};
5
6pub trait HashableObject {
7    /// Get the hash value of this object
8    fn hash(&self) -> u64;
9
10    /// Get the payload data for matching
11    fn raw_payload(&self) -> &[u8];
12
13    /// Check if the payload is compressed
14    fn is_compressed(&self) -> bool;
15
16    /// Decompress the payload into the provided buffer.
17    /// Returns the number of decompressed bytes.
18    fn decompress(&self, buf: &mut Vec<u8>) -> Result<usize>;
19
20    /// Get the offset to the next object in the hash chain
21    fn next_hash_offset(&self) -> Option<NonZeroU64>;
22
23    /// Get the object type
24    fn object_type() -> ObjectType;
25}
26
27pub trait HashableObjectMut: HashableObject {
28    /// Set the offset to the next object in the hash chain
29    fn set_next_hash_offset(&mut self, offset: NonZeroU64);
30
31    /// Set the payload of the object
32    fn set_payload(&mut self, data: &[u8]);
33}
34
35/// Trait for hash table operations
36pub trait HashTable {
37    /// The type of objects stored in this hash table
38    type Object: HashableObject;
39
40    /// Get the hash item for a given hash value
41    fn hash_item_ref(&self, hash: u64) -> &HashItem;
42
43    /// Get the length of the hash table (number of buckets)
44    fn len(&self) -> usize;
45
46    /// Make clippy happy
47    fn is_empty(&self) -> bool {
48        self.len() == 0
49    }
50}
51
52/// Trait for mutable hash table operations
53pub trait HashTableMut: HashTable {
54    /// Get a mutable reference to the hash item for a given hash value
55    fn hash_item_mut(&mut self, hash: u64) -> &mut HashItem;
56}
57
58pub struct DataHashTable<B: ByteSlice> {
59    pub header: Ref<B, ObjectHeader>,
60    pub items: Ref<B, [HashItem]>,
61}
62
63pub struct FieldHashTable<B: ByteSlice> {
64    pub header: Ref<B, ObjectHeader>,
65    pub items: Ref<B, [HashItem]>,
66}
67
68// Implement HashTable for DataHashTable
69impl<B: ByteSlice> HashTable for DataHashTable<B> {
70    type Object = DataObject<B>;
71
72    fn hash_item_ref(&self, hash: u64) -> &HashItem {
73        let bucket_index = hash as usize % self.items.len();
74        &self.items[bucket_index]
75    }
76
77    fn len(&self) -> usize {
78        self.items.len()
79    }
80}
81
82// Implement HashTable for FieldHashTable
83impl<B: ByteSlice> HashTable for FieldHashTable<B> {
84    type Object = FieldObject<B>;
85
86    fn hash_item_ref(&self, hash: u64) -> &HashItem {
87        let bucket_index = hash as usize % self.items.len();
88        &self.items[bucket_index]
89    }
90
91    fn len(&self) -> usize {
92        self.items.len()
93    }
94}
95
96// Implement HashTableMut for DataHashTable
97impl<B: ByteSliceMut> HashTableMut for DataHashTable<B> {
98    fn hash_item_mut(&mut self, hash: u64) -> &mut HashItem {
99        let bucket_index = hash as usize % self.items.len();
100        &mut self.items[bucket_index]
101    }
102}
103
104// Implement HashTableMut for FieldHashTable
105impl<B: ByteSliceMut> HashTableMut for FieldHashTable<B> {
106    fn hash_item_mut(&mut self, hash: u64) -> &mut HashItem {
107        let bucket_index = hash as usize % self.items.len();
108        &mut self.items[bucket_index]
109    }
110}
111
112// Implement JournalObject for DataHashTable
113impl<B: SplitByteSlice> JournalObject<B> for DataHashTable<B> {
114    fn from_data(data: B, _is_compact: bool) -> Option<Self> {
115        let (header_data, items_data) = data.split_at(std::mem::size_of::<ObjectHeader>()).ok()?;
116
117        let header = zerocopy::Ref::from_bytes(header_data).ok()?;
118        let items = zerocopy::Ref::from_bytes(items_data).ok()?;
119
120        Some(DataHashTable { header, items })
121    }
122}
123
124// Implement JournalObjectMut for DataHashTable
125impl<B: SplitByteSliceMut> JournalObjectMut<B> for DataHashTable<B> {
126    fn from_data_mut(data: B, _is_compact: bool) -> Option<Self> {
127        let (header_data, items_data) = data.split_at(std::mem::size_of::<ObjectHeader>()).ok()?;
128
129        let header = zerocopy::Ref::from_bytes(header_data).ok()?;
130        let items = zerocopy::Ref::from_bytes(items_data).ok()?;
131
132        Some(DataHashTable { header, items })
133    }
134}
135
136// Implement JournalObject for FieldHashTable
137impl<B: SplitByteSlice> JournalObject<B> for FieldHashTable<B> {
138    fn from_data(data: B, _is_compact: bool) -> Option<Self> {
139        let (header_data, items_data) = data.split_at(std::mem::size_of::<ObjectHeader>()).ok()?;
140
141        let header = zerocopy::Ref::from_bytes(header_data).ok()?;
142        let items = zerocopy::Ref::from_bytes(items_data).ok()?;
143
144        Some(FieldHashTable { header, items })
145    }
146}
147
148// Implement JournalObjectMut for FieldHashTable
149impl<B: SplitByteSliceMut> JournalObjectMut<B> for FieldHashTable<B> {
150    fn from_data_mut(data: B, _is_compact: bool) -> Option<Self> {
151        let (header_data, items_data) = data.split_at(std::mem::size_of::<ObjectHeader>()).ok()?;
152
153        let header = zerocopy::Ref::from_bytes(header_data).ok()?;
154        let items = zerocopy::Ref::from_bytes(items_data).ok()?;
155
156        Some(FieldHashTable { header, items })
157    }
158}
159
160impl<B: ByteSlice> HashableObject for FieldObject<B> {
161    fn hash(&self) -> u64 {
162        self.header.hash
163    }
164
165    fn raw_payload(&self) -> &[u8] {
166        &self.payload
167    }
168
169    fn is_compressed(&self) -> bool {
170        false
171    }
172
173    fn decompress(&self, buf: &mut Vec<u8>) -> Result<usize> {
174        buf.clear();
175        buf.extend_from_slice(&self.payload);
176        Ok(buf.len())
177    }
178
179    fn next_hash_offset(&self) -> Option<NonZeroU64> {
180        self.header.next_hash_offset
181    }
182
183    fn object_type() -> ObjectType {
184        ObjectType::Field
185    }
186}
187
188impl HashableObjectMut for FieldObject<&mut [u8]> {
189    fn set_next_hash_offset(&mut self, next_hash_offset: NonZeroU64) {
190        self.header.next_hash_offset = Some(next_hash_offset);
191    }
192
193    fn set_payload(&mut self, data: &[u8]) {
194        self.payload.copy_from_slice(data);
195    }
196}
197
198impl<B: ByteSlice> HashableObject for DataObject<B> {
199    fn hash(&self) -> u64 {
200        self.header.hash
201    }
202
203    fn raw_payload(&self) -> &[u8] {
204        self.raw_payload()
205    }
206
207    fn is_compressed(&self) -> bool {
208        DataObject::is_compressed(self)
209    }
210
211    fn decompress(&self, buf: &mut Vec<u8>) -> Result<usize> {
212        DataObject::decompress(self, buf)
213    }
214
215    fn next_hash_offset(&self) -> Option<NonZeroU64> {
216        self.header.next_hash_offset
217    }
218
219    fn object_type() -> ObjectType {
220        ObjectType::Data
221    }
222}
223
224impl HashableObjectMut for DataObject<&mut [u8]> {
225    fn set_next_hash_offset(&mut self, next_hash_offset: NonZeroU64) {
226        self.header.next_hash_offset = Some(next_hash_offset);
227    }
228
229    fn set_payload(&mut self, data: &[u8]) {
230        match &mut self.payload {
231            DataPayloadType::Regular(payload) => {
232                payload.copy_from_slice(data);
233            }
234            DataPayloadType::Compact { payload, .. } => {
235                payload.copy_from_slice(data);
236            }
237        };
238    }
239}