Line data Source code
1 : #ifndef PMTILES_HPP
2 : #define PMTILES_HPP
3 :
4 : #include <string>
5 : #include <sstream>
6 : #include <vector>
7 : #include <tuple>
8 : #include <functional>
9 : #include <algorithm>
10 : #include <limits> // for std::numeric_limits<>
11 :
12 : namespace pmtiles {
13 :
14 : const uint8_t TILETYPE_UNKNOWN = 0x0;
15 : const uint8_t TILETYPE_MVT = 0x1;
16 : const uint8_t TILETYPE_PNG = 0x2;
17 : const uint8_t TILETYPE_JPEG = 0x3;
18 : const uint8_t TILETYPE_WEBP = 0x4;
19 :
20 : const uint8_t COMPRESSION_UNKNOWN = 0x0;
21 : const uint8_t COMPRESSION_NONE = 0x1;
22 : const uint8_t COMPRESSION_GZIP = 0x2;
23 : const uint8_t COMPRESSION_BROTLI = 0x3;
24 : const uint8_t COMPRESSION_ZSTD = 0x4;
25 :
26 : #ifdef PMTILES_MSB
27 : template<class T>
28 : inline void swap_byte_order_if_msb(T* ptr) {
29 : unsigned char* ptrBytes = reinterpret_cast<unsigned char*>(ptr);
30 : for (size_t i = 0; i < sizeof(T)/2; ++i) {
31 : std::swap(ptrBytes[i], ptrBytes[sizeof(T)-1-i]);
32 : }
33 : }
34 : #else
35 : template<class T>
36 1887 : inline void swap_byte_order_if_msb(T* /*ptr*/)
37 : {
38 1887 : }
39 : #endif
40 :
41 : template<class T>
42 323 : inline void copy_to_lsb(std::stringstream& ss, T val) {
43 323 : swap_byte_order_if_msb(&val);
44 323 : ss.write(reinterpret_cast<char*>(&val), sizeof(T));
45 323 : }
46 :
47 : template<>
48 152 : inline void copy_to_lsb<uint8_t>(std::stringstream& ss, uint8_t val) {
49 152 : ss.write(reinterpret_cast<char*>(&val), 1);
50 152 : }
51 :
52 : struct headerv3 {
53 : uint64_t root_dir_offset;
54 : uint64_t root_dir_bytes;
55 : uint64_t json_metadata_offset;
56 : uint64_t json_metadata_bytes;
57 : uint64_t leaf_dirs_offset;
58 : uint64_t leaf_dirs_bytes;
59 : uint64_t tile_data_offset;
60 : uint64_t tile_data_bytes;
61 : uint64_t addressed_tiles_count;
62 : uint64_t tile_entries_count;
63 : uint64_t tile_contents_count;
64 : bool clustered;
65 : uint8_t internal_compression;
66 : uint8_t tile_compression;
67 : uint8_t tile_type;
68 : uint8_t min_zoom;
69 : uint8_t max_zoom;
70 : int32_t min_lon_e7;
71 : int32_t min_lat_e7;
72 : int32_t max_lon_e7;
73 : int32_t max_lat_e7;
74 : uint8_t center_zoom;
75 : int32_t center_lon_e7;
76 : int32_t center_lat_e7;
77 :
78 : // WARNING: this is limited to little-endian
79 19 : std::string serialize() {
80 38 : std::stringstream ss;
81 19 : ss << "PMTiles";
82 19 : uint8_t version = 3;
83 19 : copy_to_lsb(ss, version);
84 19 : copy_to_lsb(ss, root_dir_offset);
85 19 : copy_to_lsb(ss, root_dir_bytes);
86 19 : copy_to_lsb(ss, json_metadata_offset);
87 19 : copy_to_lsb(ss, json_metadata_bytes);
88 19 : copy_to_lsb(ss, leaf_dirs_offset);
89 19 : copy_to_lsb(ss, leaf_dirs_bytes);
90 19 : copy_to_lsb(ss, tile_data_offset);
91 19 : copy_to_lsb(ss, tile_data_bytes);
92 19 : copy_to_lsb(ss, addressed_tiles_count);
93 19 : copy_to_lsb(ss, tile_entries_count);
94 19 : copy_to_lsb(ss, tile_contents_count);
95 :
96 19 : uint8_t clustered_val = 0x0;
97 19 : if (clustered) {
98 19 : clustered_val = 0x1;
99 : }
100 :
101 19 : copy_to_lsb(ss, clustered_val);
102 19 : copy_to_lsb(ss, internal_compression);
103 19 : copy_to_lsb(ss, tile_compression);
104 19 : copy_to_lsb(ss, tile_type);
105 19 : copy_to_lsb(ss, min_zoom);
106 19 : copy_to_lsb(ss, max_zoom);
107 19 : copy_to_lsb(ss, min_lon_e7);
108 19 : copy_to_lsb(ss, min_lat_e7);
109 19 : copy_to_lsb(ss, max_lon_e7);
110 19 : copy_to_lsb(ss, max_lat_e7);
111 19 : copy_to_lsb(ss, center_zoom);
112 19 : copy_to_lsb(ss, center_lon_e7);
113 19 : copy_to_lsb(ss, center_lat_e7);
114 :
115 38 : return ss.str();
116 : }
117 : };
118 :
119 : #if defined(__clang__)
120 : #pragma clang diagnostic push
121 : #pragma clang diagnostic ignored "-Wweak-vtables"
122 : #endif
123 :
124 : struct pmtiles_magic_number_exception : std::exception {
125 0 : const char *what() const noexcept override {
126 0 : return "pmtiles magic number exception";
127 : }
128 : };
129 :
130 : struct pmtiles_version_exception : std::exception {
131 0 : const char *what() const noexcept override {
132 0 : return "pmtiles version: must be 3";
133 : }
134 : };
135 :
136 : #if defined(__clang__)
137 : #pragma clang diagnostic pop
138 : #endif
139 :
140 : template<class T>
141 1564 : inline void copy_from_lsb(T* ptr, const std::string &s, size_t offset) {
142 1564 : s.copy(reinterpret_cast<char *>(ptr), sizeof(T), offset);
143 1564 : swap_byte_order_if_msb(ptr);
144 1564 : }
145 :
146 93 : inline headerv3 deserialize_header(const std::string &s) {
147 93 : if (s.substr(0, 7) != "PMTiles") {
148 1 : throw pmtiles_magic_number_exception{};
149 : }
150 92 : if (s.size() != 127 || s[7] != 0x3) {
151 0 : throw pmtiles_version_exception{};
152 : }
153 : headerv3 h;
154 92 : copy_from_lsb(&h.root_dir_offset, s, 8);
155 92 : copy_from_lsb(&h.root_dir_bytes, s, 16);
156 92 : copy_from_lsb(&h.json_metadata_offset, s, 24);
157 92 : copy_from_lsb(&h.json_metadata_bytes, s, 32);
158 92 : copy_from_lsb(&h.leaf_dirs_offset, s, 40);
159 92 : copy_from_lsb(&h.leaf_dirs_bytes, s, 48);
160 92 : copy_from_lsb(&h.tile_data_offset, s, 56);
161 92 : copy_from_lsb(&h.tile_data_bytes, s, 64);
162 92 : copy_from_lsb(&h.addressed_tiles_count, s, 72);
163 92 : copy_from_lsb(&h.tile_entries_count, s, 80);
164 92 : copy_from_lsb(&h.tile_contents_count, s, 88);
165 92 : if (s[96] == 0x1) {
166 92 : h.clustered = true;
167 : } else {
168 0 : h.clustered = false;
169 : }
170 92 : h.internal_compression = s[97];
171 92 : h.tile_compression = s[98];
172 92 : h.tile_type = s[99];
173 92 : h.min_zoom = s[100];
174 92 : h.max_zoom = s[101];
175 92 : copy_from_lsb(&h.min_lon_e7, s, 102);
176 92 : copy_from_lsb(&h.min_lat_e7, s, 106);
177 92 : copy_from_lsb(&h.max_lon_e7, s, 110);
178 92 : copy_from_lsb(&h.max_lat_e7, s, 114);
179 92 : h.center_zoom = s[118];
180 92 : copy_from_lsb(&h.center_lon_e7, s, 119);
181 92 : copy_from_lsb(&h.center_lat_e7, s, 123);
182 92 : return h;
183 : }
184 :
185 : struct zxy {
186 : uint8_t z;
187 : uint32_t x;
188 : uint32_t y;
189 :
190 2669 : zxy(uint8_t _z, int _x, int _y)
191 2669 : : z(_z), x(_x), y(_y) {
192 2669 : }
193 : };
194 :
195 : struct entryv3 {
196 : uint64_t tile_id;
197 : uint64_t offset;
198 : uint32_t length;
199 : uint32_t run_length;
200 :
201 10145 : entryv3()
202 10145 : : tile_id(0), offset(0), length(0), run_length(0) {
203 10145 : }
204 :
205 0 : entryv3(uint64_t _tile_id, uint64_t _offset, uint32_t _length, uint32_t _run_length)
206 0 : : tile_id(_tile_id), offset(_offset), length(_length), run_length(_run_length) {
207 0 : }
208 : };
209 :
210 : #ifdef PMTILES_NEED_ENTRYV3_CMP
211 : struct {
212 : bool operator()(entryv3 a, entryv3 b) const {
213 : return a.tile_id < b.tile_id;
214 : }
215 : } entryv3_cmp;
216 : #endif
217 :
218 : struct entry_zxy {
219 : uint8_t z;
220 : uint32_t x;
221 : uint32_t y;
222 : uint64_t offset;
223 : uint32_t length;
224 :
225 858 : entry_zxy(uint8_t _z, uint32_t _x, uint32_t _y, uint64_t _offset, uint32_t _length)
226 858 : : z(_z), x(_x), y(_y), offset(_offset), length(_length) {
227 858 : }
228 : };
229 :
230 : #if defined(__clang__)
231 : #pragma clang diagnostic push
232 : #pragma clang diagnostic ignored "-Wweak-vtables"
233 : #endif
234 :
235 : struct varint_too_long_exception : std::exception {
236 0 : const char *what() const noexcept override {
237 0 : return "varint too long exception";
238 : }
239 : };
240 :
241 : struct end_of_buffer_exception : std::exception {
242 0 : const char *what() const noexcept override {
243 0 : return "end of buffer exception";
244 : }
245 : };
246 :
247 : #if defined(__clang__)
248 : #pragma clang diagnostic pop
249 : #endif
250 :
251 : namespace {
252 : constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1;
253 :
254 : // from https://github.com/mapbox/protozero/blob/master/include/protozero/varint.hpp
255 9638 : uint64_t decode_varint_impl(const char **data, const char *end) {
256 9638 : const auto *begin = reinterpret_cast<const int8_t *>(*data);
257 9638 : const auto *iend = reinterpret_cast<const int8_t *>(end);
258 9638 : const int8_t *p = begin;
259 9638 : uint64_t val = 0;
260 :
261 9638 : if (iend - begin >= max_varint_length) { // fast path
262 : do {
263 8888 : int64_t b = *p++;
264 8888 : val = ((uint64_t(b) & 0x7fU));
265 8888 : if (b >= 0) {
266 0 : break;
267 : }
268 8888 : b = *p++;
269 8888 : val |= ((uint64_t(b) & 0x7fU) << 7U);
270 8888 : if (b >= 0) {
271 7790 : break;
272 : }
273 1098 : b = *p++;
274 1098 : val |= ((uint64_t(b) & 0x7fU) << 14U);
275 1098 : if (b >= 0) {
276 244 : break;
277 : }
278 854 : b = *p++;
279 854 : val |= ((uint64_t(b) & 0x7fU) << 21U);
280 854 : if (b >= 0) {
281 806 : break;
282 : }
283 48 : b = *p++;
284 48 : val |= ((uint64_t(b) & 0x7fU) << 28U);
285 48 : if (b >= 0) {
286 48 : break;
287 : }
288 0 : b = *p++;
289 0 : val |= ((uint64_t(b) & 0x7fU) << 35U);
290 0 : if (b >= 0) {
291 0 : break;
292 : }
293 0 : b = *p++;
294 0 : val |= ((uint64_t(b) & 0x7fU) << 42U);
295 0 : if (b >= 0) {
296 0 : break;
297 : }
298 0 : b = *p++;
299 0 : val |= ((uint64_t(b) & 0x7fU) << 49U);
300 0 : if (b >= 0) {
301 0 : break;
302 : }
303 0 : b = *p++;
304 0 : val |= ((uint64_t(b) & 0x7fU) << 56U);
305 0 : if (b >= 0) {
306 0 : break;
307 : }
308 0 : b = *p++;
309 0 : val |= ((uint64_t(b) & 0x01U) << 63U);
310 0 : if (b >= 0) {
311 0 : break;
312 : }
313 0 : throw varint_too_long_exception{};
314 : } while (false);
315 : } else {
316 750 : unsigned int shift = 0;
317 1572 : while (p != iend && *p < 0) {
318 822 : val |= (uint64_t(*p++) & 0x7fU) << shift;
319 822 : shift += 7;
320 : }
321 750 : if (p == iend) {
322 0 : throw end_of_buffer_exception{};
323 : }
324 750 : val |= uint64_t(*p++) << shift;
325 : }
326 :
327 9638 : *data = reinterpret_cast<const char *>(p);
328 9638 : return val;
329 : }
330 :
331 40020 : uint64_t decode_varint(const char **data, const char *end) {
332 : // If this is a one-byte varint, decode it here.
333 40020 : if (end != *data && ((static_cast<uint64_t>(**data) & 0x80U) == 0)) {
334 30382 : const auto val = static_cast<uint64_t>(**data);
335 30382 : ++(*data);
336 30382 : return val;
337 : }
338 : // If this varint is more than one byte, defer to complete implementation.
339 9638 : return decode_varint_impl(data, end);
340 : }
341 :
342 34397 : void rotate(int64_t n, int64_t &x, int64_t &y, int64_t rx, int64_t ry) {
343 34397 : if (ry == 0) {
344 16742 : if (rx == 1) {
345 7291 : x = n - 1 - x;
346 7291 : y = n - 1 - y;
347 : }
348 16742 : int64_t t = x;
349 16742 : x = y;
350 16742 : y = t;
351 : }
352 34397 : }
353 :
354 2669 : zxy t_on_level(uint8_t z, uint64_t pos) {
355 2669 : int64_t n = 1LL << z;
356 2669 : int64_t rx, ry, s, t = pos;
357 2669 : int64_t tx = 0;
358 2669 : int64_t ty = 0;
359 :
360 24610 : for (s = 1; s < n; s *= 2) {
361 21941 : rx = 1LL & (t / 2);
362 21941 : ry = 1LL & (t ^ rx);
363 21941 : rotate(s, tx, ty, rx, ry);
364 21941 : tx += s * rx;
365 21941 : ty += s * ry;
366 21941 : t /= 4;
367 : }
368 2669 : return zxy(z, static_cast<int>(tx), static_cast<int>(ty));
369 : }
370 :
371 1035 : int write_varint(std::back_insert_iterator<std::string> data, uint64_t value) {
372 1035 : int n = 1;
373 :
374 1355 : while (value >= 0x80U) {
375 320 : *data++ = char((value & 0x7fU) | 0x80U);
376 320 : value >>= 7U;
377 320 : ++n;
378 : }
379 1035 : *data = char(value);
380 :
381 1035 : return n;
382 : }
383 :
384 : // TMS order
385 : struct {
386 0 : bool operator()(entry_zxy a, entry_zxy b) const {
387 0 : if (a.z != b.z) {
388 0 : return a.z < b.z;
389 : }
390 0 : if (a.x != b.x) {
391 0 : return a.x < b.x;
392 : }
393 0 : return a.y > b.y;
394 : }
395 : } colmajor_cmp;
396 :
397 : // use a 0 length entry as a null value.
398 0 : entryv3 find_tile(const std::vector<entryv3> &entries, uint64_t tile_id) {
399 0 : int m = 0;
400 0 : int n = static_cast<int>(entries.size()) - 1;
401 0 : while (m <= n) {
402 0 : int k = (n + m) >> 1;
403 0 : if (tile_id > entries[k].tile_id) {
404 0 : m = k + 1;
405 0 : } else if (tile_id < entries[k].tile_id) {
406 0 : n = k - 1;
407 : } else {
408 0 : return entries[k];
409 : }
410 : }
411 :
412 0 : if (n >= 0) {
413 0 : if (entries[n].run_length == 0) {
414 0 : return entries[n];
415 : }
416 0 : if (tile_id - entries[n].tile_id < entries[n].run_length) {
417 0 : return entries[n];
418 : }
419 : }
420 :
421 0 : return entryv3{0, 0, 0, 0};
422 : }
423 :
424 : } // end anonymous namespace
425 :
426 2669 : inline zxy tileid_to_zxy(uint64_t tileid) {
427 2669 : uint64_t acc = 0;
428 24610 : for (uint8_t t_z = 0; t_z < 32; t_z++) {
429 24610 : uint64_t num_tiles = (1LL << t_z) * (1LL << t_z);
430 24610 : if (acc + num_tiles > tileid) {
431 2669 : return t_on_level(t_z, tileid - acc);
432 : }
433 21941 : acc += num_tiles;
434 : }
435 0 : throw std::overflow_error("tile zoom exceeds 64-bit limit");
436 : }
437 :
438 1833 : inline uint64_t zxy_to_tileid(uint32_t z, uint32_t x, uint32_t y) {
439 1833 : if (z > 31) {
440 0 : throw std::overflow_error("tile zoom exceeds 64-bit limit");
441 : }
442 1833 : if (x > (1U << z) - 1U || y > (1U << z) - 1U) {
443 2 : throw std::overflow_error("tile x/y outside zoom level bounds");
444 : }
445 1831 : uint64_t acc = 0;
446 14287 : for (uint32_t t_z = 0; t_z < z; t_z++) acc += (1LL << t_z) * (1LL << t_z);
447 1831 : int64_t n = 1LL << z;
448 1831 : int64_t rx, ry, s, d = 0;
449 1831 : int64_t tx = x;
450 1831 : int64_t ty = y;
451 14287 : for (s = n / 2; s > 0; s /= 2) {
452 12456 : rx = (tx & s) > 0;
453 12456 : ry = (ty & s) > 0;
454 12456 : d += s * s * ((3LL * rx) ^ ry);
455 12456 : rotate(s, tx, ty, rx, ry);
456 : }
457 1831 : return acc + d;
458 : }
459 :
460 : // returns an uncompressed byte buffer
461 19 : inline std::string serialize_directory(const std::vector<entryv3> &entries) {
462 19 : std::string data;
463 :
464 19 : write_varint(std::back_inserter(data), entries.size());
465 :
466 19 : uint64_t last_id = 0;
467 273 : for (auto const &entry : entries) {
468 254 : write_varint(std::back_inserter(data), entry.tile_id - last_id);
469 254 : last_id = entry.tile_id;
470 : }
471 :
472 273 : for (auto const &entry : entries) {
473 254 : write_varint(std::back_inserter(data), entry.run_length);
474 : }
475 :
476 273 : for (auto const &entry : entries) {
477 254 : write_varint(std::back_inserter(data), entry.length);
478 : }
479 :
480 273 : for (size_t i = 0; i < entries.size(); i++) {
481 254 : if (i > 0 && entries[i].offset == entries[i - 1].offset + entries[i - 1].length) {
482 191 : write_varint(std::back_inserter(data), 0);
483 : } else {
484 63 : write_varint(std::back_inserter(data), entries[i].offset + 1);
485 : }
486 : }
487 :
488 19 : return data;
489 : }
490 :
491 : #if defined(__clang__)
492 : #pragma clang diagnostic push
493 : #pragma clang diagnostic ignored "-Wweak-vtables"
494 : #endif
495 :
496 : struct malformed_directory_exception : std::exception {
497 0 : const char *what() const noexcept override {
498 0 : return "malformed directory exception";
499 : }
500 : };
501 :
502 : #if defined(__clang__)
503 : #pragma clang diagnostic pop
504 : #endif
505 :
506 : // takes an uncompressed byte buffer
507 456 : inline std::vector<entryv3> deserialize_directory(const std::string &decompressed) {
508 456 : const char *t = decompressed.data();
509 456 : const char *end = t + decompressed.size();
510 :
511 456 : const uint64_t num_entries_64bit = decode_varint(&t, end);
512 : // Sanity check to avoid excessive memory allocation attempt:
513 : // each directory entry takes at least 4 bytes
514 456 : if (num_entries_64bit / 4U > decompressed.size()) {
515 0 : throw malformed_directory_exception();
516 : }
517 456 : const size_t num_entries = static_cast<size_t>(num_entries_64bit);
518 :
519 456 : std::vector<entryv3> result;
520 456 : result.resize(num_entries);
521 :
522 456 : uint64_t last_id = 0;
523 10347 : for (size_t i = 0; i < num_entries; i++) {
524 9891 : const uint64_t val = decode_varint(&t, end);
525 9891 : if (val > std::numeric_limits<uint64_t>::max() - last_id) {
526 0 : throw malformed_directory_exception();
527 : }
528 9891 : const uint64_t tile_id = last_id + val;
529 9891 : result[i].tile_id = tile_id;
530 9891 : last_id = tile_id;
531 : }
532 :
533 10347 : for (size_t i = 0; i < num_entries; i++) {
534 9891 : const uint64_t val = decode_varint(&t, end);
535 9891 : if (val > std::numeric_limits<uint32_t>::max()) {
536 0 : throw malformed_directory_exception();
537 : }
538 9891 : result[i].run_length = static_cast<uint32_t>(val);
539 : }
540 :
541 10347 : for (size_t i = 0; i < num_entries; i++) {
542 9891 : const uint64_t val = decode_varint(&t, end);
543 9891 : if (val > std::numeric_limits<uint32_t>::max()) {
544 0 : throw malformed_directory_exception();
545 : }
546 9891 : result[i].length = static_cast<uint32_t>(val);
547 : }
548 :
549 10347 : for (size_t i = 0; i < num_entries; i++) {
550 9891 : uint64_t tmp = decode_varint(&t, end);
551 :
552 9891 : if (i > 0 && tmp == 0) {
553 8328 : if (result[i - 1].offset > std::numeric_limits<uint64_t>::max() - result[i - 1].length) {
554 0 : throw malformed_directory_exception();
555 : }
556 8328 : result[i].offset = result[i - 1].offset + result[i - 1].length;
557 : } else {
558 1563 : result[i].offset = tmp - 1;
559 : }
560 : }
561 :
562 : // assert the directory has been fully consumed
563 456 : if (t != end) {
564 0 : throw malformed_directory_exception();
565 : }
566 :
567 912 : return result;
568 : }
569 :
570 0 : inline std::tuple<std::string, std::string, int> build_root_leaves(const std::function<std::string(const std::string &, uint8_t)> mycompress, uint8_t compression, const std::vector<pmtiles::entryv3> &entries, int leaf_size) {
571 0 : std::vector<pmtiles::entryv3> root_entries;
572 0 : std::string leaves_bytes;
573 0 : int num_leaves = 0;
574 0 : for (size_t i = 0; i < entries.size(); i += leaf_size) {
575 0 : num_leaves++;
576 0 : size_t end = i + leaf_size;
577 0 : if (i + leaf_size > entries.size()) {
578 0 : end = entries.size();
579 : }
580 0 : std::vector<pmtiles::entryv3> subentries = {entries.begin() + i, entries.begin() + end};
581 0 : auto uncompressed_leaf = pmtiles::serialize_directory(subentries);
582 0 : auto compressed_leaf = mycompress(uncompressed_leaf, compression);
583 0 : root_entries.emplace_back(entries[i].tile_id, leaves_bytes.size(), static_cast<uint32_t>(compressed_leaf.size()), 0);
584 0 : leaves_bytes += compressed_leaf;
585 : }
586 0 : auto uncompressed_root = pmtiles::serialize_directory(root_entries);
587 0 : auto compressed_root = mycompress(uncompressed_root, compression);
588 0 : return std::make_tuple(compressed_root, leaves_bytes, num_leaves);
589 : }
590 :
591 19 : inline std::tuple<std::string, std::string, int> make_root_leaves(const std::function<std::string(const std::string &, uint8_t)> mycompress, uint8_t compression, const std::vector<pmtiles::entryv3> &entries) {
592 38 : auto test_bytes = pmtiles::serialize_directory(entries);
593 38 : auto compressed = mycompress(test_bytes, compression);
594 19 : if (compressed.size() <= 16384 - 127) {
595 38 : return std::make_tuple(compressed, "", 0);
596 : }
597 0 : int leaf_size = 4096;
598 : while (true) {
599 0 : std::string root_bytes;
600 0 : std::string leaves_bytes;
601 : int num_leaves;
602 0 : std::tie(root_bytes, leaves_bytes, num_leaves) = build_root_leaves(mycompress, compression, entries, leaf_size);
603 0 : if (root_bytes.length() < 16384 - 127) {
604 0 : return std::make_tuple(root_bytes, leaves_bytes, num_leaves);
605 : }
606 0 : if (leaf_size > std::numeric_limits<int>::max() / 2) {
607 0 : return std::make_tuple(compressed, "", 0);
608 : }
609 0 : leaf_size *= 2;
610 0 : }
611 : }
612 :
613 : inline void collect_entries(const std::function<std::string(const std::string &, uint8_t)> decompress, std::vector<entry_zxy> &tile_entries, const char *pmtiles_map, const headerv3 &h, uint64_t dir_offset, uint64_t dir_len) {
614 : std::string dir_s{pmtiles_map + dir_offset, static_cast<size_t>(dir_len)};
615 : std::string decompressed_dir = decompress(dir_s, h.internal_compression);
616 :
617 : auto dir_entries = pmtiles::deserialize_directory(decompressed_dir);
618 : for (auto const &entry : dir_entries) {
619 : if (entry.run_length == 0) {
620 : collect_entries(decompress, tile_entries, pmtiles_map, h, h.leaf_dirs_offset + entry.offset, entry.length);
621 : } else {
622 : for (uint64_t i = entry.tile_id; i < entry.tile_id + entry.run_length; i++) {
623 : pmtiles::zxy zxy = pmtiles::tileid_to_zxy(i);
624 : tile_entries.emplace_back(zxy.z, zxy.x, zxy.y, h.tile_data_offset + entry.offset, entry.length);
625 : }
626 : }
627 : }
628 : }
629 :
630 : inline std::vector<entry_zxy> entries_tms(const std::function<std::string(const std::string &, uint8_t)> decompress, const char *pmtiles_map) {
631 : std::string header_s{pmtiles_map, 127};
632 : auto header = pmtiles::deserialize_header(header_s);
633 :
634 : std::vector<entry_zxy> tile_entries;
635 :
636 : collect_entries(decompress, tile_entries, pmtiles_map, header, header.root_dir_offset, header.root_dir_bytes);
637 : std::sort(tile_entries.begin(), tile_entries.end(), colmajor_cmp);
638 : return tile_entries;
639 : }
640 :
641 : inline std::pair<uint64_t, uint32_t> get_tile(const std::function<std::string(const std::string &, uint8_t)> decompress, const char *pmtiles_map, uint8_t z, uint32_t x, uint32_t y) {
642 : uint64_t tile_id = pmtiles::zxy_to_tileid(z, x, y);
643 :
644 : std::string header_s{pmtiles_map, 127};
645 : auto h = pmtiles::deserialize_header(header_s);
646 :
647 : uint64_t dir_offset = h.root_dir_offset;
648 : if (h.root_dir_bytes > std::numeric_limits<uint32_t>::max()) {
649 : throw malformed_directory_exception();
650 : }
651 : uint32_t dir_length = static_cast<uint32_t>(h.root_dir_bytes);
652 : for (int depth = 0; depth <= 3; depth++) {
653 : std::string dir_s{pmtiles_map + dir_offset, dir_length};
654 : std::string decompressed_dir = decompress(dir_s, h.internal_compression);
655 : auto dir_entries = pmtiles::deserialize_directory(decompressed_dir);
656 : auto entry = find_tile(dir_entries, tile_id);
657 :
658 : if (entry.length > 0) {
659 : if (entry.run_length > 0) {
660 : return std::make_pair(h.tile_data_offset + entry.offset, entry.length);
661 : } else {
662 : dir_offset = h.leaf_dirs_offset + entry.offset;
663 : dir_length = entry.length;
664 : }
665 : } else {
666 : return std::make_pair(0, 0);
667 : }
668 : }
669 :
670 : return std::make_pair(0, 0);
671 : }
672 :
673 : } // namespace pmtiles
674 : #endif
|