1 | use super::*; |
2 | |
3 | #[derive (Copy, Clone, Debug, Hash, PartialEq, Eq, Ord, PartialOrd)] |
4 | pub struct Row { |
5 | pub file: &'static File, |
6 | pub index: usize, |
7 | } |
8 | |
9 | impl Row { |
10 | pub fn new(file: &'static File, index: usize) -> Self { |
11 | Self { file, index } |
12 | } |
13 | |
14 | fn next(&self) -> Self { |
15 | Self { file: self.file, index: self.index + 1 } |
16 | } |
17 | } |
18 | |
19 | pub trait AsRow: Copy { |
20 | const TABLE: usize; |
21 | fn to_row(&self) -> Row; |
22 | fn from_row(row: Row) -> Self; |
23 | |
24 | fn file(&self) -> &'static File { |
25 | self.to_row().file |
26 | } |
27 | |
28 | fn reader(&self) -> &'static Reader { |
29 | // Safety: At this point the File is already pointing to a valid Reader. |
30 | unsafe { &*self.file().reader } |
31 | } |
32 | |
33 | fn index(&self) -> usize { |
34 | self.to_row().index |
35 | } |
36 | |
37 | fn next(&self) -> Self { |
38 | Self::from_row(self.to_row().next()) |
39 | } |
40 | |
41 | fn usize(&self, column: usize) -> usize { |
42 | self.file().usize(self.index(), Self::TABLE, column) |
43 | } |
44 | |
45 | fn str(&self, column: usize) -> &'static str { |
46 | let file = self.file(); |
47 | let offset = file.strings + self.usize(column); |
48 | let bytes = &file.bytes[offset..]; |
49 | let nul_pos = bytes.iter().position(|&c| c == 0).expect("expected null-terminated C-string" ); |
50 | std::str::from_utf8(&bytes[..nul_pos]).expect("expected valid utf-8 C-string" ) |
51 | } |
52 | |
53 | fn row(&self, column: usize) -> Row { |
54 | Row::new(self.file(), self.usize(column) - 1) |
55 | } |
56 | |
57 | fn decode<T: Decode>(&self, column: usize) -> T { |
58 | T::decode(self.file(), self.usize(column)) |
59 | } |
60 | |
61 | fn blob(&self, column: usize) -> Blob { |
62 | let file = self.file(); |
63 | let offset = file.blobs + self.usize(column); |
64 | let initial_byte = file.bytes[offset]; |
65 | |
66 | let (blob_size, blob_size_bytes) = match initial_byte >> 5 { |
67 | 0..=3 => (initial_byte & 0x7f, 1), |
68 | 4..=5 => (initial_byte & 0x3f, 2), |
69 | 6 => (initial_byte & 0x1f, 4), |
70 | rest => unimplemented!(" {rest:?}" ), |
71 | }; |
72 | |
73 | let mut blob_size = blob_size as usize; |
74 | |
75 | for byte in &file.bytes[offset + 1..offset + blob_size_bytes] { |
76 | blob_size = blob_size.checked_shl(8).unwrap_or(0) + (*byte as usize); |
77 | } |
78 | |
79 | let offset = offset + blob_size_bytes; |
80 | Blob::new(file, &file.bytes[offset..offset + blob_size]) |
81 | } |
82 | |
83 | fn list<R: AsRow>(&self, column: usize) -> RowIterator<R> { |
84 | let file = self.file(); |
85 | let first = self.usize(column) - 1; |
86 | let next = self.next(); |
87 | let last = if next.index() < file.tables[Self::TABLE].len { next.usize(column) - 1 } else { file.tables[R::TABLE].len }; |
88 | RowIterator::new(file, first..last) |
89 | } |
90 | |
91 | fn equal_range<L: AsRow>(&self, column: usize, value: usize) -> RowIterator<L> { |
92 | let file = self.file(); |
93 | let mut first = 0; |
94 | let mut last = file.tables[L::TABLE].len; |
95 | let mut count = last; |
96 | |
97 | loop { |
98 | if count == 0 { |
99 | last = first; |
100 | break; |
101 | } |
102 | |
103 | let count2 = count / 2; |
104 | let middle = first + count2; |
105 | let middle_value = file.usize(middle, L::TABLE, column); |
106 | |
107 | match middle_value.cmp(&value) { |
108 | Ordering::Less => { |
109 | first = middle + 1; |
110 | count -= count2 + 1; |
111 | } |
112 | Ordering::Greater => count = count2, |
113 | Ordering::Equal => { |
114 | let first2 = file.lower_bound_of(L::TABLE, first, middle, column, value); |
115 | first += count; |
116 | last = file.upper_bound_of(L::TABLE, middle + 1, first, column, value); |
117 | first = first2; |
118 | break; |
119 | } |
120 | } |
121 | } |
122 | |
123 | RowIterator::new(file, first..last) |
124 | } |
125 | } |
126 | |
127 | pub struct RowIterator<R: AsRow> { |
128 | file: &'static File, |
129 | rows: std::ops::Range<usize>, |
130 | phantom: std::marker::PhantomData<R>, |
131 | } |
132 | |
133 | impl<R: AsRow> RowIterator<R> { |
134 | pub fn new(file: &'static File, rows: std::ops::Range<usize>) -> Self { |
135 | Self { file, rows, phantom: std::marker::PhantomData } |
136 | } |
137 | } |
138 | |
139 | impl<R: AsRow> Iterator for RowIterator<R> { |
140 | type Item = R; |
141 | |
142 | fn next(&mut self) -> Option<Self::Item> { |
143 | self.rows.next().map(|row: usize| R::from_row(Row::new(self.file, index:row))) |
144 | } |
145 | } |
146 | |
147 | pub trait HasAttributes { |
148 | fn attributes(&self) -> RowIterator<Attribute>; |
149 | fn find_attribute(&self, name: &str) -> Option<Attribute>; |
150 | fn has_attribute(&self, name: &str) -> bool; |
151 | } |
152 | |
153 | impl<R: AsRow + Into<HasAttribute>> HasAttributes for R { |
154 | fn attributes(&self) -> RowIterator<Attribute> { |
155 | self.equal_range(column:0, value:Into::<HasAttribute>::into(*self).encode()) |
156 | } |
157 | |
158 | fn find_attribute(&self, name: &str) -> Option<Attribute> { |
159 | self.attributes().find(|attribute: &Attribute| attribute.name() == name) |
160 | } |
161 | |
162 | fn has_attribute(&self, name: &str) -> bool { |
163 | self.find_attribute(name).is_some() |
164 | } |
165 | } |
166 | |