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