1use super::*;
2
3#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Ord, PartialOrd)]
4pub struct Row {
5 pub file: &'static File,
6 pub index: usize,
7}
8
9impl 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
19pub 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
127pub struct RowIterator<R: AsRow> {
128 file: &'static File,
129 rows: std::ops::Range<usize>,
130 phantom: std::marker::PhantomData<R>,
131}
132
133impl<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
139impl<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
147pub 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
153impl<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