diff --git a/cpp/pmtiles.hpp b/cpp/pmtiles.hpp index 0b3de55..1bfeaf7 100644 --- a/cpp/pmtiles.hpp +++ b/cpp/pmtiles.hpp @@ -311,14 +311,14 @@ void rotate(int64_t n, int64_t &x, int64_t &y, int64_t rx, int64_t ry) { } zxy t_on_level(uint8_t z, uint64_t pos) { - int64_t n = 1 << z; + int64_t n = 1LL << z; int64_t rx, ry, s, t = pos; int64_t tx = 0; int64_t ty = 0; for (s = 1; s < n; s *= 2) { - rx = 1 & (t / 2); - ry = 1 & (t ^ rx); + rx = 1LL & (t / 2); + ry = 1LL & (t ^ rx); rotate(s, tx, ty, rx, ry); tx += s * rx; ty += s * ry; @@ -385,28 +385,33 @@ entryv3 find_tile(const std::vector &entries, uint64_t tile_id) { inline zxy tileid_to_zxy(uint64_t tileid) { uint64_t acc = 0; - uint8_t t_z = 0; - while (true) { + for (uint8_t t_z = 0; t_z < 32; t_z++) { uint64_t num_tiles = (1LL << t_z) * (1LL << t_z); if (acc + num_tiles > tileid) { return t_on_level(t_z, tileid - acc); } acc += num_tiles; - t_z++; } + throw std::overflow_error("tile zoom exceeds 64-bit limit"); } inline uint64_t zxy_to_tileid(uint8_t z, uint32_t x, uint32_t y) { + if (z > 31) { + throw std::overflow_error("tile zoom exceeds 64-bit limit"); + } + if (x > (1 << z) - 1 || y > (1 << z) - 1) { + throw std::overflow_error("tile x/y outside zoom level bounds"); + } uint64_t acc = 0; for (uint8_t t_z = 0; t_z < z; t_z++) acc += (1LL << t_z) * (1LL << t_z); - int64_t n = 1 << z; + int64_t n = 1LL << z; int64_t rx, ry, s, d = 0; int64_t tx = x; int64_t ty = y; for (s = n / 2; s > 0; s /= 2) { rx = (tx & s) > 0; ry = (ty & s) > 0; - d += s * s * ((3 * rx) ^ ry); + d += s * s * ((3LL * rx) ^ ry); rotate(s, tx, ty, rx, ry); } return acc + d; diff --git a/cpp/test.cpp b/cpp/test.cpp index 90e8217..37afb5e 100644 --- a/cpp/test.cpp +++ b/cpp/test.cpp @@ -46,7 +46,7 @@ MU_TEST(test_zxy_to_tileid) { } MU_TEST(test_roundtrip) { - for (int z = 0; z < 25; z++) { + for (int z = 0; z < 32; z++) { uint64_t dim = (1 << z) - 1; auto tl = tileid_to_zxy(zxy_to_tileid(z, 0, 0)); mu_check(tl.z == z); @@ -65,6 +65,41 @@ MU_TEST(test_roundtrip) { mu_check(br.x == dim); mu_check(br.y == dim); } + + bool caught = false; + try { + tileid_to_zxy(18446744073709551615ULL); + } catch (const std::runtime_error &e) { + caught = true; + } + + mu_check(caught); + + caught = false; + try { + zxy_to_tileid(32, 0, 0); + } catch (const std::runtime_error &e) { + caught = true; + } + + mu_check(caught); + + caught = false; + try { + zxy_to_tileid(0, 1, 1); + } catch (const std::runtime_error &e) { + caught = true; + } + + mu_check(caught); + + caught = false; + try { + zxy_to_tileid(31, 2147483647ULL + 1, 2147483647ULL + 1); + } catch (const std::runtime_error &e) { + caught = true; + } + mu_check(caught); } MU_TEST(test_serialize_directory) {