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

Generated by: LCOV version 1.14