hff_core/
table.rs

1use crate::{Identifier, Result};
2use byteorder::{ByteOrder, ReadBytesExt, WriteBytesExt};
3use std::{
4    fmt::Debug,
5    io::{Read, Write},
6};
7
8/// A table entry in the file format.
9/// Tables are 48 bytes in length when stored.
10#[repr(C, align(16))]
11#[derive(Copy, Eq, PartialEq, Clone, Hash)]
12pub struct Table {
13    /// The table identifier.
14    identifier: Identifier,
15    /// Length of the metadata optionally attached to this table.
16    metadata_length: u64,
17    /// Absolute offset to the metadata content.
18    metadata_offset: u64,
19    /// The number of child tables that are owned by this table.
20    /// This is direct children only, not the children of children.
21    child_count: u32,
22    /// The index of the next sibling table.
23    sibling: u32,
24    /// The index into the chunk table where the first chunk exists.
25    chunk_index: u32,
26    /// The number of chunks associated with this table or zero.
27    chunk_count: u32,
28}
29
30impl Debug for Table {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        write!(
33            f,
34            "({:X}): meta \"{}:{}\", sib: {}, children: {}, chunks(count: {}, index: {})",
35            *self.identifier,
36            self.metadata_length,
37            self.metadata_offset,
38            self.sibling,
39            self.child_count,
40            self.chunk_count,
41            self.chunk_index
42        )
43    }
44}
45
46impl Default for Table {
47    fn default() -> Self {
48        Self {
49            identifier: Identifier::INVALID,
50            metadata_length: 0,
51            metadata_offset: 0,
52            child_count: 0,
53            sibling: 0,
54            chunk_index: 0,
55            chunk_count: 0,
56        }
57    }
58}
59
60impl Table {
61    /// Size of the table entry.
62    pub const SIZE: usize = std::mem::size_of::<Self>();
63
64    /// Create a table using the builder.
65    pub fn create() -> TableBuilder {
66        TableBuilder::new()
67    }
68
69    /// Get the table identifier.
70    pub fn identifier(&self) -> Identifier {
71        self.identifier
72    }
73
74    /// Get the metadata length.
75    pub fn metadata_length(&self) -> u64 {
76        self.metadata_length
77    }
78
79    /// Get the metadata length mutably
80    pub fn metadata_length_mut(&mut self) -> &mut u64 {
81        &mut self.metadata_length
82    }
83
84    /// Get the metadata offset in the file.
85    /// This is an absolute offset within the file, i.e. zero based.
86    pub fn metadata_offset(&self) -> u64 {
87        self.metadata_offset
88    }
89
90    /// Get the metadata offset mutably.
91    pub fn metadata_offset_mut(&mut self) -> &mut u64 {
92        &mut self.metadata_offset
93    }
94
95    /// Helper to collapse tables into parents.
96    pub fn offset(&mut self, data_length: u64, chunk_count: u32) {
97        if self.metadata_length > 0 {
98            self.metadata_offset += data_length;
99        }
100        if self.chunk_count > 0 {
101            self.chunk_index += chunk_count;
102        }
103    }
104
105    /// Get the child table count.
106    pub fn child_count(&self) -> u32 {
107        self.child_count
108    }
109
110    /// Get the index offset from this entry to its sibling.
111    /// Zero if there is no sibling.
112    pub fn sibling(&self) -> u32 {
113        self.sibling
114    }
115
116    /// Get the sibling index mutably.
117    pub fn sibling_mut(&mut self) -> &mut u32 {
118        &mut self.sibling
119    }
120
121    /// Get the index of the first chunk owned by this table.
122    pub fn chunk_index(&self) -> u32 {
123        self.chunk_index
124    }
125
126    /// Get the number of chunks owned by this table.
127    pub fn chunk_count(&self) -> u32 {
128        self.chunk_count
129    }
130
131    /// Read a table from the given stream.
132    pub fn read<E: ByteOrder>(reader: &mut dyn Read) -> Result<Self> {
133        Ok(Self {
134            identifier: Identifier::new(reader.read_u128::<E>()?),
135            metadata_length: reader.read_u64::<E>()?,
136            metadata_offset: reader.read_u64::<E>()?,
137            child_count: reader.read_u32::<E>()?,
138            sibling: reader.read_u32::<E>()?,
139            chunk_index: reader.read_u32::<E>()?,
140            chunk_count: reader.read_u32::<E>()?,
141        })
142    }
143
144    /// Write a table to the given stream.
145    pub fn write<E: ByteOrder>(self, writer: &mut dyn Write) -> Result<()> {
146        writer.write_u128::<E>(*self.identifier)?;
147        writer.write_u64::<E>(self.metadata_length)?;
148        writer.write_u64::<E>(self.metadata_offset)?;
149        writer.write_u32::<E>(self.child_count)?;
150        writer.write_u32::<E>(self.sibling)?;
151        writer.write_u32::<E>(self.chunk_index)?;
152        writer.write_u32::<E>(self.chunk_count)?;
153
154        Ok(())
155    }
156}
157
158/// Build a table.
159pub struct TableBuilder {
160    table: Table,
161}
162
163impl TableBuilder {
164    /// Create a new builder.
165    fn new() -> Self {
166        Self {
167            table: Table::default(),
168        }
169    }
170
171    /// Set the identifier.
172    pub fn identifier(mut self, value: Identifier) -> Self {
173        self.table.identifier = value;
174        self
175    }
176
177    /// Set the metadata length.
178    pub fn metadata_length(mut self, value: u64) -> Self {
179        self.table.metadata_length = value;
180        self
181    }
182
183    /// Set the metadata offset in the file.
184    pub fn metadata_offset(mut self, value: u64) -> Self {
185        self.table.metadata_offset = value;
186        self
187    }
188
189    /// Get the child table count.
190    pub fn child_count(mut self, value: u32) -> Self {
191        self.table.child_count = value;
192        self
193    }
194
195    /// Get the index of the next sibling table.
196    pub fn sibling(mut self, value: u32) -> Self {
197        self.table.sibling = value;
198        self
199    }
200
201    /// Get the index of the first chunk owned by this table.
202    pub fn chunk_index(mut self, value: u32) -> Self {
203        self.table.chunk_index = value;
204        self
205    }
206
207    /// Get the number of chunks owned by this table.
208    pub fn chunk_count(mut self, value: u32) -> Self {
209        self.table.chunk_count = value;
210        self
211    }
212
213    /// Finalize the table.
214    pub fn end(self) -> Table {
215        self.table
216    }
217}
218
219#[cfg(test)]
220mod tests {
221    use crate::Ecc;
222
223    use super::*;
224
225    #[test]
226    fn test_layout() {
227        assert_eq!(std::mem::size_of::<Table>(), 48);
228    }
229
230    #[test]
231    fn test_basics() {
232        let table = Table::create()
233            .identifier((Ecc::from("test1"), Ecc::INVALID).into())
234            .metadata_length(1)
235            .metadata_offset(2)
236            .child_count(3)
237            .sibling(4)
238            .chunk_count(5)
239            .chunk_index(6)
240            .end();
241
242        assert_eq!(
243            table.identifier().as_ecc2(),
244            (Ecc::new("test1"), Ecc::INVALID)
245        );
246        assert_eq!(table.metadata_length(), 1);
247        assert_eq!(table.metadata_offset(), 2);
248        assert_eq!(table.child_count(), 3);
249        assert_eq!(table.sibling(), 4);
250        assert_eq!(table.chunk_count(), 5);
251        assert_eq!(table.chunk_index(), 6);
252    }
253
254    #[test]
255    fn test_serialization() {
256        let mut buffer = vec![];
257        let table = Table::create()
258            .identifier((Ecc::from("test1"), Ecc::INVALID).into())
259            .metadata_length(1)
260            .metadata_offset(2)
261            .child_count(3)
262            .sibling(4)
263            .chunk_count(5)
264            .chunk_index(6)
265            .end();
266        assert!(table.write::<crate::LE>(&mut buffer).is_ok());
267
268        let result = Table::read::<crate::LE>(&mut buffer.as_slice()).unwrap();
269        assert_eq!(table, result);
270    }
271}