1use crate::fdb::{
4 common::Latin1Str,
5 file::{
6 FDBHeader, FDBRowHeader, FDBRowHeaderListEntry, FDBTableDataHeader, FDBTableDefHeader,
7 FDBTableHeader,
8 },
9};
10use assembly_core::{
11 buffer::{CastError, MinimallyAligned},
12 displaydoc::Display,
13};
14use bytemuck::from_bytes;
15use std::{
16 cmp::Ordering,
17 convert::TryInto,
18 fmt,
19 mem::size_of,
20 ops::{Deref, Range},
21};
22use thiserror::Error;
23
24#[derive(Error, Debug, Display, Clone, PartialEq, Eq)]
25pub enum BufferError {
27 OutOfBounds(Range<usize>),
29 Unaligned(usize),
31}
32
33#[derive(Copy, Clone)]
34pub struct Buffer<'a>(&'a [u8]);
36
37pub type Res<T> = Result<T, BufferError>;
39
40impl<'a> Deref for Buffer<'a> {
41 type Target = [u8];
42
43 fn deref(&self) -> &Self::Target {
44 self.0
45 }
46}
47
48pub(crate) fn compare_bytes(bytes: &[u8], name_bytes: &[u8]) -> Ordering {
54 for i in 0..bytes.len() {
55 match name_bytes[i].cmp(&bytes[i]) {
56 Ordering::Equal => {}
57 Ordering::Less => {
58 return Ordering::Less;
60 }
61 Ordering::Greater => {
62 return Ordering::Greater;
63 }
64 }
65 }
66 if name_bytes[bytes.len()] == 0 {
67 Ordering::Equal
68 } else {
69 Ordering::Greater
70 }
71}
72
73pub fn get_at<T>(buf: &[u8], addr: usize) -> Res<&T> {
77 let base = buf.as_ptr();
78 let len = buf.len();
79 let size = std::mem::size_of::<T>();
80 let align = std::mem::align_of::<T>();
81
82 let needed = addr
83 .checked_add(size)
84 .ok_or(BufferError::OutOfBounds(addr..len))?;
85
86 if needed > len {
87 return Err(BufferError::OutOfBounds(addr..needed));
88 }
89
90 let start = unsafe { base.add(addr) };
91 if 0 != start.align_offset(align) {
92 return Err(BufferError::Unaligned(addr));
93 }
94 Ok(unsafe { &*(start as *const T) })
95}
96
97pub fn get_slice_at<T>(buf: &[u8], addr: usize, count: usize) -> Res<&[T]> {
101 let base = buf.as_ptr();
102 let len = buf.len();
103 let size = std::mem::size_of::<T>();
104 let align = std::mem::align_of::<T>();
105
106 let slice_bytes = size
107 .checked_mul(count)
108 .ok_or(BufferError::OutOfBounds(addr..len))?;
109
110 let needed = addr
111 .checked_add(slice_bytes)
112 .ok_or(BufferError::OutOfBounds(addr..len))?;
113
114 if needed > len {
115 return Err(BufferError::OutOfBounds(addr..needed));
116 }
117
118 let start = unsafe { base.add(addr) };
119 if 0 != start.align_offset(align) {
120 return Err(BufferError::Unaligned(addr));
121 }
122
123 Ok(unsafe { &*(std::ptr::slice_from_raw_parts(start as *const T, count)) })
124}
125
126#[cfg(target_endian = "little")]
128pub fn header_ref(buf: &[u8]) -> Res<&FDBHeader> {
129 get_at(buf, 0)
130}
131
132pub fn header(buf: &[u8], _: ()) -> Res<FDBHeader> {
134 Ok(*header_ref(buf)?)
135}
136
137pub fn table_headers<'a>(buf: &'a [u8], header: &'a FDBHeader) -> Res<&'a [FDBTableHeader]> {
139 get_slice_at(
140 buf,
141 header.tables.base_offset as usize,
142 header.tables.count as usize,
143 )
144}
145
146pub fn table_definition_ref(
148 buf: &[u8],
149 header: FDBTableHeader,
150) -> Res<&FDBTableDefHeader> {
151 get_at(buf, header.table_def_header_addr as usize)
152}
153
154pub fn table_data_ref(buf: &[u8], header: FDBTableHeader) -> Res<&FDBTableDataHeader> {
156 get_at(buf, header.table_data_header_addr as usize)
157}
158
159pub fn table_definition(buf: &[u8], header: FDBTableHeader) -> Res<FDBTableDefHeader> {
161 table_definition_ref(buf, header).map(|x| *x)
162}
163
164pub fn table_data(buf: &[u8], header: FDBTableHeader) -> Res<FDBTableDataHeader> {
166 table_data_ref(buf, header).map(|x| *x)
167}
168
169pub fn cmp_table_header_name(buf: &[u8], bytes: &[u8], table_header: FDBTableHeader) -> Ordering {
171 let def_header_addr = table_header.table_def_header_addr;
172 let def_header = get_at::<FDBTableDefHeader>(buf, def_header_addr as usize).unwrap();
174 let name_addr = def_header.table_name_addr as usize;
175
176 let name_bytes = buf.get(name_addr..).unwrap();
177
178 compare_bytes(bytes, name_bytes)
179}
180
181impl<'a> Buffer<'a> {
182 pub fn new(buf: &'a [u8]) -> Self {
184 Self(buf)
185 }
186
187 pub fn as_bytes(self) -> &'a [u8] {
189 self.0
190 }
191
192 pub fn try_cast<T: MinimallyAligned>(
194 self,
195 offset: u32,
196 ) -> std::result::Result<&'a T, CastError> {
197 assembly_core::buffer::try_cast(self.as_bytes(), offset)
198 }
199
200 pub fn try_cast_slice<T: MinimallyAligned>(
202 self,
203 offset: u32,
204 len: u32,
205 ) -> std::result::Result<&'a [T], CastError> {
206 assembly_core::buffer::try_cast_slice(self.as_bytes(), offset, len)
207 }
208
209 pub fn get_len_at(self, start: usize, len: usize) -> Res<&'a [u8]> {
211 let end = start + len;
212 self.0
213 .get(Range { start, end })
214 .ok_or(BufferError::OutOfBounds(Range { start, end }))
215 }
216
217 pub fn string(self, addr: u32) -> Res<&'a Latin1Str> {
219 let start = addr as usize;
220 let mut buf = self.0.get(start..).ok_or_else(|| {
221 let end = self.0.len();
222 BufferError::OutOfBounds(Range { start, end })
223 })?;
224 if let Some(nullpos) = memchr::memchr(0, buf) {
225 buf = buf.split_at(nullpos).0;
226 }
227 Ok(Latin1Str::new(buf))
228 }
229
230 pub fn i64(self, addr: u32) -> Res<i64> {
232 let start = addr as usize;
233 let end = start + size_of::<u64>();
234 if end > self.0.len() {
235 Err(BufferError::OutOfBounds(Range { start, end }))
236 } else {
237 let (_, base) = self.0.split_at(start);
238 let (bytes, _) = base.split_at(size_of::<u64>());
239 let val = i64::from_le_bytes(bytes.try_into().unwrap());
240 Ok(val)
241 }
242 }
243
244 pub fn table_def_header(&self, addr: u32) -> Res<FDBTableDefHeader> {
246 let buf = self.get_len_at(addr as usize, 12)?;
247 let (a, buf) = buf.split_at(4);
248 let (b, c) = buf.split_at(4);
249 Ok(FDBTableDefHeader {
250 column_count: u32::from_le_bytes(a.try_into().unwrap()),
251 table_name_addr: u32::from_le_bytes(b.try_into().unwrap()),
252 column_header_list_addr: u32::from_le_bytes(c.try_into().unwrap()),
253 })
254 }
255
256 pub fn table_data_header(self, addr: u32) -> Res<FDBTableDataHeader> {
258 let buf = self.get_len_at(addr as usize, 8)?;
259 Ok(*from_bytes(buf))
260 }
261
262 pub fn row_header_list_entry(self, addr: u32) -> Res<FDBRowHeaderListEntry> {
264 let buf = self.get_len_at(addr as usize, 8)?;
265 let (a, b) = buf.split_at(4);
266 Ok(FDBRowHeaderListEntry {
267 row_header_addr: u32::from_le_bytes(a.try_into().unwrap()),
268 row_header_list_next_addr: u32::from_le_bytes(b.try_into().unwrap()),
269 })
270 }
271
272 pub fn row_header(self, addr: u32) -> Res<FDBRowHeader> {
274 let buf = self.get_len_at(addr as usize, 8)?;
275 Ok(*from_bytes(buf))
276 }
277}
278
279impl<'a> fmt::Debug for Buffer<'a> {
280 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281 f.debug_struct("Buffer")
282 .field("base", &self.0.as_ptr())
283 .field("len", &self.0.len())
284 .finish()
285 }
286}