1mod expand;
8mod fuzzy;
9mod raycast;
10mod reader;
11mod tile;
12
13pub(crate) use expand::ExpandedPolygon;
14pub(crate) use raycast::raycast_seg;
15pub(crate) use reader::Reader;
16pub(crate) use tile::TileId;
17
18use geometry_rs::I32Point;
19use std::fmt;
20
21pub(crate) const HEADER_SIZE: u64 = 64;
22pub(crate) const SECTION_ENTRY_LEN: u64 = 16;
23pub(crate) const FOOTER_SIZE: u64 = 4;
24pub(crate) const FORMAT_MAJOR: u8 = 1;
25pub(crate) const COORD_SCALE: u32 = 100_000;
26
27pub(crate) const PROFILE_OFFSET: usize = 48;
29pub(crate) const PROFILE_E: u8 = 0;
30pub(crate) const PROFILE_M: u8 = 1;
31
32pub(crate) const FLAG_GRID: u32 = 1 << 0;
33pub(crate) const FLAG_NO_SHORTCUT: u32 = 1 << 1;
34
35pub(crate) const SECTION_NAMES: u32 = 1;
36pub(crate) const SECTION_TZDIR: u32 = 2;
37pub(crate) const SECTION_POLYDIR: u32 = 3;
38pub(crate) const SECTION_RINGDIR: u32 = 4;
39pub(crate) const SECTION_RINGOPS: u32 = 5;
40pub(crate) const SECTION_GROUPDIR: u32 = 6;
41pub(crate) const SECTION_CHUNKDIR: u32 = 7;
42pub(crate) const SECTION_GRID: u32 = 8;
43pub(crate) const SECTION_POINTS: u32 = 9;
44pub(crate) const SECTION_FUZZY: u32 = 10;
45pub(crate) const SECTION_FLAT_POINTS: u32 = 12;
46pub(crate) const SECTION_FLAT_RING_DIR: u32 = 13;
47pub(crate) const SECTION_YSTRIPES: u32 = 14;
48
49pub(crate) const SECTION_SLOTS: usize = 15;
51
52pub(crate) const TZ_RECORD_LEN: u64 = 24;
53pub(crate) const POLY_RECORD_LEN: u64 = 24;
54pub(crate) const RING_RECORD_LEN: u64 = 28;
55pub(crate) const GROUP_RECORD_LEN: u64 = 44;
56pub(crate) const CHUNK_RECORD_LEN: u64 = 24;
57
58pub(crate) const FUZZY_HEADER_LEN: u64 = 16;
59pub(crate) const FUZZY_MULTI: u16 = 1 << 15;
62
63#[derive(Debug, Clone, PartialEq, Eq)]
65#[non_exhaustive]
66pub enum Error {
67 Malformed(&'static str),
69 Profile,
73 NoFuzzy,
75 Index,
77}
78
79impl fmt::Display for Error {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 match self {
82 Error::Malformed(what) => write!(f, "tzb: malformed file: {what}"),
83 Error::Profile => {
84 write!(
85 f,
86 "tzb: memory-image (.tzm) files are not supported; use .tzb"
87 )
88 }
89 Error::NoFuzzy => write!(f, "tzb: file has no FUZZY section"),
90 Error::Index => write!(f, "tzb: timezone index out of range"),
91 }
92 }
93}
94
95impl std::error::Error for Error {}
96
97pub(crate) fn malformed<T>(what: &'static str) -> Result<T, Error> {
98 Err(Error::Malformed(what))
99}
100
101#[derive(Debug, Clone, Copy)]
103pub(crate) struct BBox {
104 pub min_x: i32,
105 pub min_y: i32,
106 pub max_x: i32,
107 pub max_y: i32,
108}
109
110impl BBox {
111 pub(crate) fn read(raw: &[u8], off: usize) -> Self {
112 Self {
113 min_x: i32_le(raw, off),
114 min_y: i32_le(raw, off + 4),
115 max_x: i32_le(raw, off + 8),
116 max_y: i32_le(raw, off + 12),
117 }
118 }
119
120 fn ordered(self) -> bool {
121 self.min_x <= self.max_x && self.min_y <= self.max_y
122 }
123
124 pub(crate) fn in_domain(self) -> bool {
126 self.ordered()
127 && self.min_x >= -18_000_000
128 && self.max_x <= 18_000_000
129 && self.min_y >= -9_000_000
130 && self.max_y <= 9_000_000
131 }
132
133 pub(crate) fn contains(self, x: f64, y: f64) -> bool {
134 x >= f64::from(self.min_x)
135 && x <= f64::from(self.max_x)
136 && y >= f64::from(self.min_y)
137 && y <= f64::from(self.max_y)
138 }
139
140 pub(crate) fn ray_relevant(self, x: f64, y: f64) -> bool {
143 y >= f64::from(self.min_y) && y <= f64::from(self.max_y) && f64::from(self.max_x) >= x
144 }
145}
146
147pub(crate) fn point_in_domain(p: I32Point) -> bool {
148 (-18_000_000..=18_000_000).contains(&p.x) && (-9_000_000..=9_000_000).contains(&p.y)
149}
150
151pub(crate) fn same_point(a: I32Point, b: I32Point) -> bool {
152 a.x == b.x && a.y == b.y
153}
154
155pub(crate) fn align4(n: u64) -> u64 {
156 (n + 3) & !3
157}
158
159pub(crate) fn u16_le(raw: &[u8], off: usize) -> u16 {
162 u16::from_le_bytes([raw[off], raw[off + 1]])
163}
164
165pub(crate) fn u32_le(raw: &[u8], off: usize) -> u32 {
166 u32::from_le_bytes([raw[off], raw[off + 1], raw[off + 2], raw[off + 3]])
167}
168
169pub(crate) fn u64_le(raw: &[u8], off: usize) -> u64 {
170 let mut b = [0u8; 8];
171 b.copy_from_slice(&raw[off..off + 8]);
172 u64::from_le_bytes(b)
173}
174
175pub(crate) fn i16_le(raw: &[u8], off: usize) -> i16 {
176 u16_le(raw, off) as i16
177}
178
179pub(crate) fn i32_le(raw: &[u8], off: usize) -> i32 {
180 u32_le(raw, off) as i32
181}
182
183pub(crate) fn add_delta(prev: i32, delta: i32) -> Result<i32, Error> {
185 match prev.checked_add(delta) {
186 Some(v) => Ok(v),
187 None => malformed("coordinate overflow"),
188 }
189}
190
191pub(crate) fn crc32_ieee(data: &[u8]) -> u32 {
195 const TABLES: [[u32; 256]; 8] = crc32_tables();
196 let mut crc = !0u32;
197 let (chunks, remainder) = data.as_chunks::<8>();
198 for chunk in chunks {
199 let lo = u32_le(chunk, 0) ^ crc;
200 let hi = u32_le(chunk, 4);
201 crc = TABLES[7][(lo & 0xff) as usize]
202 ^ TABLES[6][((lo >> 8) & 0xff) as usize]
203 ^ TABLES[5][((lo >> 16) & 0xff) as usize]
204 ^ TABLES[4][(lo >> 24) as usize]
205 ^ TABLES[3][(hi & 0xff) as usize]
206 ^ TABLES[2][((hi >> 8) & 0xff) as usize]
207 ^ TABLES[1][((hi >> 16) & 0xff) as usize]
208 ^ TABLES[0][(hi >> 24) as usize];
209 }
210 for &b in remainder {
211 crc = TABLES[0][((crc ^ u32::from(b)) & 0xff) as usize] ^ (crc >> 8);
212 }
213 !crc
214}
215
216const fn crc32_tables() -> [[u32; 256]; 8] {
217 let mut tables = [[0u32; 256]; 8];
218 let mut i = 0;
219 while i < 256 {
220 let mut crc = i as u32;
221 let mut bit = 0;
222 while bit < 8 {
223 crc = if crc & 1 != 0 {
224 0xEDB8_8320 ^ (crc >> 1)
225 } else {
226 crc >> 1
227 };
228 bit += 1;
229 }
230 tables[0][i] = crc;
231 i += 1;
232 }
233 let mut t = 1;
234 while t < 8 {
235 let mut i = 0;
236 while i < 256 {
237 let prev = tables[t - 1][i];
238 tables[t][i] = tables[0][(prev & 0xff) as usize] ^ (prev >> 8);
239 i += 1;
240 }
241 t += 1;
242 }
243 tables
244}
245
246pub(crate) struct StreamCursor<'a> {
250 buf: &'a [u8],
251 pos: usize,
252}
253
254impl<'a> StreamCursor<'a> {
255 pub(crate) fn new(data: &'a [u8], pos: u64, end: u64) -> Result<Self, Error> {
258 let buf = data
259 .get(pos as usize..end as usize)
260 .ok_or(Error::Malformed("chunk byte range"))?;
261 Ok(Self { buf, pos: 0 })
262 }
263
264 pub(crate) fn at_end(&self) -> bool {
265 self.pos == self.buf.len()
266 }
267
268 #[inline]
269 pub(crate) fn varint(&mut self) -> Result<i32, Error> {
270 let Some(&b0) = self.buf.get(self.pos) else {
271 return malformed("truncated varint");
272 };
273 if b0 & 0x80 == 0 {
276 self.pos += 1;
277 let u = u32::from(b0);
278 return Ok(((u >> 1) ^ (u & 1).wrapping_neg()) as i32);
279 }
280 if let Some(&b1) = self.buf.get(self.pos + 1)
281 && b1 & 0x80 == 0
282 {
283 if b1 == 0 {
284 return malformed("nonminimal varint");
285 }
286 self.pos += 2;
287 let u = u32::from(b0 & 0x7f) | u32::from(b1) << 7;
288 return Ok(((u >> 1) ^ (u & 1).wrapping_neg()) as i32);
289 }
290 self.varint_slow(u32::from(b0 & 0x7f))
291 }
292
293 #[inline(never)]
294 fn varint_slow(&mut self, mut u: u32) -> Result<i32, Error> {
295 self.pos += 1;
296 for i in 1..5 {
297 let Some(&b) = self.buf.get(self.pos) else {
298 return malformed("truncated varint");
299 };
300 self.pos += 1;
301 if i == 4 && b & 0xf0 != 0 {
302 return malformed("varint exceeds 32 bits");
303 }
304 u |= u32::from(b & 0x7f) << (7 * i);
305 if b & 0x80 == 0 {
306 if b == 0 {
307 return malformed("nonminimal varint");
308 }
309 return Ok(((u >> 1) ^ (u & 1).wrapping_neg()) as i32);
310 }
311 }
312 malformed("unterminated varint")
313 }
314}