modelvault_core/
file_format.rs1use crate::error::{DbError, FormatError};
6
7pub const FILE_MAGIC: [u8; 4] = *b"TDB0";
8
9pub const FORMAT_MAJOR: u16 = 0;
14pub const FORMAT_MINOR_V4: u16 = 4;
16pub const FORMAT_MINOR: u16 = 5;
18pub const FORMAT_MINOR_V6: u16 = 6;
20pub const FORMAT_MINOR_V3: u16 = 3;
22
23pub const FILE_HEADER_SIZE: usize = 32;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub struct FileHeader {
28 pub format_major: u16,
29 pub format_minor: u16,
30 pub header_size: u32,
31 pub flags: u64,
32}
33
34impl FileHeader {
35 pub fn new_v0_3() -> Self {
36 Self {
37 format_major: FORMAT_MAJOR,
38 format_minor: FORMAT_MINOR_V3,
39 header_size: FILE_HEADER_SIZE as u32,
40 flags: 0,
41 }
42 }
43
44 pub fn new_v0_4() -> Self {
45 Self {
46 format_major: FORMAT_MAJOR,
47 format_minor: FORMAT_MINOR_V4,
48 header_size: FILE_HEADER_SIZE as u32,
49 flags: 0,
50 }
51 }
52
53 pub fn new_v0_5() -> Self {
54 Self {
55 format_major: FORMAT_MAJOR,
56 format_minor: FORMAT_MINOR,
57 header_size: FILE_HEADER_SIZE as u32,
58 flags: 0,
59 }
60 }
61
62 pub fn new_v0_8() -> Self {
64 Self {
65 format_major: FORMAT_MAJOR,
66 format_minor: FORMAT_MINOR_V6,
67 header_size: FILE_HEADER_SIZE as u32,
68 flags: 0,
69 }
70 }
71
72 pub fn encode(self) -> [u8; FILE_HEADER_SIZE] {
73 let mut buf = [0u8; FILE_HEADER_SIZE];
74 buf[0..4].copy_from_slice(&FILE_MAGIC);
75 buf[4..6].copy_from_slice(&self.format_major.to_le_bytes());
76 buf[6..8].copy_from_slice(&self.format_minor.to_le_bytes());
77 buf[8..12].copy_from_slice(&self.header_size.to_le_bytes());
78 buf[12..20].copy_from_slice(&self.flags.to_le_bytes());
79 buf
80 }
81}
82
83pub fn decode_header(bytes: &[u8]) -> Result<FileHeader, DbError> {
84 if bytes.len() < FILE_HEADER_SIZE {
85 return Err(DbError::Format(FormatError::TruncatedHeader {
86 got: bytes.len(),
87 expected: FILE_HEADER_SIZE,
88 }));
89 }
90
91 if bytes[0..4] != FILE_MAGIC {
92 let mut got = [0u8; 4];
93 got.copy_from_slice(&bytes[0..4]);
94 return Err(DbError::Format(FormatError::BadMagic { got }));
95 }
96
97 let format_major = u16::from_le_bytes([bytes[4], bytes[5]]);
98 let format_minor = u16::from_le_bytes([bytes[6], bytes[7]]);
99 if format_major != FORMAT_MAJOR || !(2..=6).contains(&format_minor) {
100 return Err(DbError::Format(FormatError::UnsupportedVersion {
101 major: format_major,
102 minor: format_minor,
103 }));
104 }
105
106 let header_size = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]);
107 let flags = u64::from_le_bytes([
108 bytes[12], bytes[13], bytes[14], bytes[15], bytes[16], bytes[17], bytes[18], bytes[19],
109 ]);
110
111 Ok(FileHeader {
112 format_major,
113 format_minor,
114 header_size,
115 flags,
116 })
117}