LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/pmtiles/pmtiles - pmtiles.hpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 229 324 70.7 %
Date: 2025-07-11 10:11:13 Functions: 22 32 68.8 %

          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

Generated by: LCOV version 1.14