assembly_fdb/ro/
buffer.rs1use crate::util::compare_bytes;
4use assembly_core::buffer::Buffer;
5use assembly_fdb_core::file::{
6 ArrayHeader, FDBHeader, FDBRowHeader, FDBRowHeaderListEntry, FDBTableDataHeader,
7 FDBTableDefHeader, FDBTableHeader,
8};
9use displaydoc::Display;
10use latin1str::Latin1Str;
11use std::{cmp::Ordering, convert::TryInto, mem::size_of, ops::Range};
12use thiserror::Error;
13
14#[derive(Error, Debug, Display, Clone, PartialEq, Eq)]
15pub enum BufferError {
17 OutOfBounds(Range<usize>),
19 Unaligned(usize),
21}
22
23pub type Res<T> = Result<T, BufferError>;
25
26pub fn get_at<T>(buf: &[u8], addr: usize) -> Res<&T> {
42 let base = buf.as_ptr();
43 let len = buf.len();
44 let size = std::mem::size_of::<T>();
45 let align = std::mem::align_of::<T>();
46
47 let needed = addr
48 .checked_add(size)
49 .ok_or(BufferError::OutOfBounds(addr..len))?;
50
51 if needed > len {
52 return Err(BufferError::OutOfBounds(addr..needed));
53 }
54
55 let start = unsafe { base.add(addr) };
56 if 0 != start.align_offset(align) {
57 return Err(BufferError::Unaligned(addr));
58 }
59 Ok(unsafe { &*(start as *const T) })
60}
61
62pub fn get_slice_at<T>(buf: &[u8], addr: usize, count: usize) -> Res<&[T]> {
66 let base = buf.as_ptr();
67 let len = buf.len();
68 let size = std::mem::size_of::<T>();
69 let align = std::mem::align_of::<T>();
70
71 let slice_bytes = size
72 .checked_mul(count)
73 .ok_or(BufferError::OutOfBounds(addr..len))?;
74
75 let needed = addr
76 .checked_add(slice_bytes)
77 .ok_or(BufferError::OutOfBounds(addr..len))?;
78
79 if needed > len {
80 return Err(BufferError::OutOfBounds(addr..needed));
81 }
82
83 let start = unsafe { base.add(addr) };
84 if 0 != start.align_offset(align) {
85 return Err(BufferError::Unaligned(addr));
86 }
87
88 Ok(unsafe { &*(std::ptr::slice_from_raw_parts(start as *const T, count)) })
89}
90
91#[cfg(target_endian = "little")]
93pub fn header_ref(buf: &[u8]) -> Res<&FDBHeader> {
94 get_at(buf, 0)
95}
96
97pub fn header(buf: &[u8], _: ()) -> Res<FDBHeader> {
99 Ok(*header_ref(buf)?)
100}
101
102pub fn table_headers<'a>(buf: &'a [u8], header: &'a FDBHeader) -> Res<&'a [FDBTableHeader]> {
104 get_slice_at(
105 buf,
106 header.tables.base_offset as usize,
107 header.tables.count as usize,
108 )
109}
110
111pub fn table_definition_ref(buf: &[u8], header: FDBTableHeader) -> Res<&FDBTableDefHeader> {
113 get_at(buf, header.table_def_header_addr as usize)
114}
115
116pub fn table_data_ref(buf: &[u8], header: FDBTableHeader) -> Res<&FDBTableDataHeader> {
118 get_at(buf, header.table_data_header_addr as usize)
119}
120
121pub fn table_definition(buf: &[u8], header: FDBTableHeader) -> Res<FDBTableDefHeader> {
123 table_definition_ref(buf, header).map(|x| *x)
124}
125
126pub fn table_data(buf: &[u8], header: FDBTableHeader) -> Res<FDBTableDataHeader> {
128 table_data_ref(buf, header).map(|x| *x)
129}
130
131pub fn cmp_table_header_name(buf: &[u8], bytes: &[u8], table_header: FDBTableHeader) -> Ordering {
133 let def_header_addr = table_header.table_def_header_addr;
134 let def_header = get_at::<FDBTableDefHeader>(buf, def_header_addr as usize).unwrap();
136 let name_addr = def_header.table_name_addr as usize;
137
138 let name_bytes = buf.get(name_addr..).unwrap();
139
140 compare_bytes(bytes, name_bytes)
141}
142
143pub trait BufferExt: Buffer {
145 fn get_len_at(&self, start: usize, len: usize) -> Res<&[u8]>;
147 fn string(&self, addr: u32) -> Res<&Latin1Str>;
149 fn i64(&self, addr: u32) -> Res<i64>;
151 fn table_def_header(&self, addr: u32) -> Res<FDBTableDefHeader>;
153 fn table_data_header(&self, addr: u32) -> Res<FDBTableDataHeader>;
155 fn row_header_list_entry(&self, addr: u32) -> Res<FDBRowHeaderListEntry>;
157 fn row_header(&self, addr: u32) -> Res<FDBRowHeader>;
159}
160
161impl BufferExt for [u8] {
162 fn get_len_at(&self, start: usize, len: usize) -> Res<&[u8]> {
164 let end = start + len;
165 let range = Range { start, end };
166 self.get(range.clone())
167 .ok_or(BufferError::OutOfBounds(range))
168 }
169
170 fn string(&self, addr: u32) -> Res<&Latin1Str> {
171 let start = addr as usize;
172 let buf = self.get(start..).ok_or_else(|| {
173 let end = self.len();
174 BufferError::OutOfBounds(Range { start, end })
175 })?;
176 Ok(Latin1Str::from_bytes_until_nul(buf))
177 }
178
179 fn i64(&self, addr: u32) -> Res<i64> {
180 let start = addr as usize;
181 let end = start + size_of::<u64>();
182 if end > self.len() {
183 Err(BufferError::OutOfBounds(Range { start, end }))
184 } else {
185 let (_, base) = self.split_at(start);
186 let (bytes, _) = base.split_at(size_of::<u64>());
187 let val = i64::from_le_bytes(bytes.try_into().unwrap());
188 Ok(val)
189 }
190 }
191
192 fn table_def_header(&self, addr: u32) -> Res<FDBTableDefHeader> {
193 let buf = self.get_len_at(addr as usize, 12)?;
194 let (a, buf) = buf.split_at(4);
195 let (b, c) = buf.split_at(4);
196 Ok(FDBTableDefHeader {
197 column_count: u32::from_le_bytes(a.try_into().unwrap()),
198 table_name_addr: u32::from_le_bytes(b.try_into().unwrap()),
199 column_header_list_addr: u32::from_le_bytes(c.try_into().unwrap()),
200 })
201 }
202
203 fn table_data_header(&self, addr: u32) -> Res<FDBTableDataHeader> {
204 let buf = self.get_len_at(addr as usize, 8)?;
205 let (a, b) = buf.split_at(4);
206 Ok(FDBTableDataHeader {
207 buckets: ArrayHeader {
208 count: u32::from_le_bytes(a.try_into().unwrap()),
209 base_offset: u32::from_le_bytes(b.try_into().unwrap()),
210 },
211 })
212 }
213
214 fn row_header_list_entry(&self, addr: u32) -> Res<FDBRowHeaderListEntry> {
216 let buf = self.get_len_at(addr as usize, 8)?;
217 let (a, b) = buf.split_at(4);
218 Ok(FDBRowHeaderListEntry {
219 row_header_addr: u32::from_le_bytes(a.try_into().unwrap()),
220 row_header_list_next_addr: u32::from_le_bytes(b.try_into().unwrap()),
221 })
222 }
223
224 fn row_header(&self, addr: u32) -> Res<FDBRowHeader> {
226 let buf = self.get_len_at(addr as usize, 8)?;
227 let (a, b) = buf.split_at(4);
228 Ok(FDBRowHeader {
229 fields: ArrayHeader {
230 count: u32::from_le_bytes(a.try_into().unwrap()),
231 base_offset: u32::from_le_bytes(b.try_into().unwrap()),
232 },
233 })
234 }
235}