assembly_fdb/io/read/
mod.rs

1//! Low-Level [`std::io`] reader for FDB files
2//!
3//! This module provides a struct which can be used to access
4//! a FDB file in any order the user desires.
5
6use assembly_fdb_core::value::{UnknownValueType, ValueType};
7use displaydoc::Display;
8use thiserror::Error;
9
10mod parser;
11use assembly_core::reader::{FileError, FileResult};
12use assembly_fdb_core::file::{
13    FDBHeader, FDBRowHeader, FDBRowHeaderListEntry, FDBTableDataHeader, FDBTableDefHeader,
14};
15use assembly_fdb_core::{
16    file::{FDBBucketHeader, FDBColumnHeader, FDBFieldData, FDBTableHeader},
17    value::owned::Field,
18};
19use latin1str::Latin1String;
20use nom::{Finish, IResult};
21use parser::{ParseFDB, ParseLE};
22use std::convert::TryFrom;
23use std::io::{self, BufRead, Read, Seek, SeekFrom};
24
25/// Implementation of [`DatabaseReader::get_row_header_addr_iterator`]
26#[allow(clippy::upper_case_acronyms)]
27pub struct FDBRowHeaderAddrIterator<'a, T: ?Sized> {
28    next_addr: u32,
29    file: &'a mut T,
30}
31
32/// Extension trait to `Seek + BufRead` for reading strings
33pub trait DatabaseBufReader
34where
35    Self: Seek + BufRead,
36{
37    /// Read a string from the file
38    fn get_string(&mut self, addr: u32) -> io::Result<String>;
39}
40
41impl<T> DatabaseBufReader for T
42where
43    T: Seek + BufRead,
44{
45    fn get_string(&mut self, addr: u32) -> io::Result<String> {
46        self.seek(SeekFrom::Start(addr.into()))?;
47        let string = Latin1String::read_cstring(self)?;
48        Ok(string.decode().into_owned())
49    }
50}
51impl<T> DatabaseReader for T where T: Seek + Read + ?Sized {}
52
53/// Parse a struct at the give offset into the buffer
54fn parse_at<R: Seek + Read + ?Sized, T>(
55    reader: &mut R,
56    addr: impl Into<u64>,
57    buf: &mut [u8],
58    parser: impl Fn(&[u8]) -> IResult<&[u8], T>,
59) -> FileResult<T> {
60    let addr = addr.into();
61    reader.seek(SeekFrom::Start(addr))?;
62    reader.read_exact(buf)?;
63    let (_rest, header) = parser(buf).finish().map_err(|e| FileError::Parse {
64        addr,
65        offset: buf.len() - e.input.len(),
66        code: e.code,
67    })?;
68    Ok(header)
69}
70
71fn bytes<IO: ParseLE>() -> IO::Buf {
72    IO::Buf::default()
73}
74
75fn parse_list_at<R: Seek + Read + ?Sized, T: ParseFDB>(
76    reader: &mut R,
77    addr: impl Into<u64>,
78    count: u32,
79) -> FileResult<Vec<T>> {
80    let addr = addr.into();
81    reader.seek(SeekFrom::Start(addr))?;
82    let buf_len = <T::IO as ParseLE>::BYTE_COUNT;
83    let mut buf = bytes::<T::IO>();
84    let mut offset = 0;
85    let mut list = Vec::with_capacity(count as usize);
86    for _ in 0..count {
87        reader.read_exact(buf.as_mut())?;
88        let (_rest, t) = T::parse(buf.as_mut())
89            .finish()
90            .map_err(|e| FileError::Parse {
91                addr,
92                offset: offset + buf_len - e.input.len(),
93                code: e.code,
94            })?;
95        list.push(t);
96        offset += buf_len;
97    }
98    Ok(list)
99}
100/// Extension to `Seek + Read` to read an FDB file
101pub trait DatabaseReader
102where
103    Self: Seek + Read,
104{
105    /// Read the schema header
106    fn get_header(&mut self) -> FileResult<FDBHeader> {
107        let mut bytes = [0; 8];
108        parse_at(self, 0u64, &mut bytes, FDBHeader::parse)
109    }
110
111    /// Read the table header
112    fn get_table_header_list(&mut self, header: FDBHeader) -> FileResult<Vec<FDBTableHeader>> {
113        let addr = header.tables.base_offset;
114        let count = header.tables.count;
115        parse_list_at(self, addr, count)
116    }
117
118    /// Read the table def header
119    fn get_table_def_header(&mut self, addr: u32) -> FileResult<FDBTableDefHeader> {
120        let mut bytes = [0; std::mem::size_of::<FDBTableDefHeader>()];
121        parse_at(self, addr, &mut bytes, FDBTableDefHeader::parse)
122    }
123
124    /// Get a 64bit integer
125    fn get_i64(&mut self, addr: u32) -> io::Result<i64> {
126        let mut bytes: [u8; 8] = [0; 8];
127        self.seek(SeekFrom::Start(addr.into()))?;
128        self.read_exact(&mut bytes)?;
129        Ok(i64::from_le_bytes(bytes))
130    }
131
132    /// Get the column header list
133    fn get_column_header_list(
134        &mut self,
135        header: &FDBTableDefHeader,
136    ) -> FileResult<Vec<FDBColumnHeader>> {
137        parse_list_at(self, header.column_header_list_addr, header.column_count)
138    }
139
140    /// Get the table data header
141    fn get_table_data_header(&mut self, addr: u32) -> FileResult<FDBTableDataHeader> {
142        let mut bytes = bytes::<<FDBTableDataHeader as ParseFDB>::IO>();
143        parse_at(self, addr, &mut bytes, FDBTableDataHeader::parse)
144    }
145
146    /// Get the table bucket header list
147    fn get_bucket_header_list(
148        &mut self,
149        header: &FDBTableDataHeader,
150    ) -> FileResult<Vec<FDBBucketHeader>> {
151        let addr = header.buckets.base_offset;
152        let count = header.buckets.count;
153        parse_list_at(self, addr, count)
154    }
155
156    /// Get a row header list entry
157    fn get_row_header_list_entry(&mut self, addr: u32) -> FileResult<FDBRowHeaderListEntry> {
158        let mut bytes = [0; std::mem::size_of::<FDBRowHeaderListEntry>()];
159        parse_at(self, addr, &mut bytes, FDBRowHeaderListEntry::parse)
160    }
161
162    /// Get a row header
163    fn get_row_header(&mut self, addr: u32) -> FileResult<FDBRowHeader> {
164        let mut bytes: [u8; 8] = [0; std::mem::size_of::<FDBRowHeader>()];
165        parse_at(self, addr, &mut bytes, FDBRowHeader::parse)
166    }
167
168    /// Returns a vector of `FDBFieldData`
169    fn get_field_data_list(&mut self, header: FDBRowHeader) -> FileResult<Vec<FDBFieldData>> {
170        parse_list_at(self, header.fields.base_offset, header.fields.count)
171    }
172
173    /// Returns an iterator over `FDBRowHeader` offsets
174    fn get_row_header_addr_iterator<'a>(
175        &'a mut self,
176        addr: u32,
177    ) -> FDBRowHeaderAddrIterator<'a, Self> {
178        FDBRowHeaderAddrIterator::<'a> {
179            file: self,
180            next_addr: addr,
181        }
182    }
183}
184
185impl<'a, T> Iterator for FDBRowHeaderAddrIterator<'a, T>
186where
187    T: Read + Seek,
188{
189    type Item = FileResult<u32>;
190
191    fn next(&mut self) -> Option<Self::Item> {
192        match self.next_addr {
193            std::u32::MAX => None,
194            addr => match self.file.get_row_header_list_entry(addr) {
195                Ok(entry) => {
196                    self.next_addr = entry.row_header_list_next_addr;
197                    Some(Ok(entry.row_header_addr))
198                }
199                Err(e) => {
200                    self.next_addr = std::u32::MAX;
201                    Some(Err(e))
202                }
203            },
204        }
205    }
206}
207
208/// Errors generated by the builder module
209#[derive(Debug, Display, Error)]
210pub enum BuilderError {
211    /// Failed IO
212    Io(#[from] io::Error),
213    /// Unknown Value Type
214    UnknownValueType(#[from] UnknownValueType),
215}
216
217/// Results for this module
218pub type Result<T> = std::result::Result<T, BuilderError>;
219
220impl<T: ?Sized> DatabaseBuilder for T where T: DatabaseBufReader + DatabaseReader + Seek + BufRead {}
221
222/// Extension trait for `Seek + BufRead + DatabaseBufReader + DatabaseReader`
223pub trait DatabaseBuilder
224where
225    Self: Seek + BufRead + DatabaseBufReader + DatabaseReader,
226{
227    /// Try to load a field value
228    fn try_load_field(&mut self, data: &FDBFieldData) -> Result<Field> {
229        let bytes = data.value;
230        match ValueType::try_from(data.data_type)? {
231            ValueType::Nothing => Ok(Field::Nothing),
232            ValueType::Integer => Ok(bytes).map(i32::from_le_bytes).map(Field::Integer),
233            ValueType::Float => Ok(bytes)
234                .map(u32::from_le_bytes)
235                .map(f32::from_bits)
236                .map(Field::Float),
237            ValueType::Text => Ok(bytes)
238                .map(u32::from_le_bytes)
239                .and_then(|addr| self.get_string(addr))
240                .map(Field::Text)
241                .map_err(Into::into),
242            ValueType::Boolean => Ok(bytes).map(|v| v != [0; 4]).map(Field::Boolean),
243            ValueType::BigInt => Ok(bytes)
244                .map(u32::from_le_bytes)
245                .and_then(|addr| self.get_i64(addr))
246                .map(Field::BigInt)
247                .map_err(Into::into),
248            ValueType::VarChar => Ok(bytes)
249                .map(u32::from_le_bytes)
250                .and_then(|addr| self.get_string(addr))
251                .map(Field::VarChar)
252                .map_err(Into::into),
253        }
254    }
255}