Repository: protomaps/PMTiles (language: python)

Issue:
pmtiles.hpp: multiple warning and robustness fixes
This fixes a number of warnings found when integrating it in GDAL builds, which are done with quite pedantic warning levels.

- add explicit casts when shortening variables
- add support for big-endian targets if defining PMTILES_MSB
- add santity checks on reading directories against corrupt/hostile data
- replace a exit() call by an exception


Discussion / hints:
Fabulous! merged.

> This fixes a number of warnings found when integrating it in GDAL builds, which are done with quite pedantic warning levels.

Is there somewhere these warning levels are defined so we could reproduce them here to catch warnings in the future? 

> add santity checks on reading directories against corrupt/hostile data

FYI, for write support we may want to narrow the specification to add a limit on the byte size or # of entries in a leaf directly According to the current spec https://github.com/protomaps/PMTiles/blob/main/spec/v3/spec.md a single huge leaf directory is valid, but a 100MB+ leaf directory would cause problems on [runtimes like Cloudflare](https://github.com/protomaps/PMTiles/tree/main/serverless/aws) with a memory limit of 128MB. 

I'm only familiar with the writers authored mostly by me at [go-pmtiles](https://github.com/protomaps/go-pmtiles), [cpp/pmtiles.hpp](https://github.com/protomaps/PMTiles/blob/main/cpp/pmtiles.hpp) and [planetiler](https://github.com/onthegomap/planetiler/blob/main/planetiler-core/src/main/java/com/onthegomap/planetiler/pmtiles/WriteablePmtiles.java). None of these writers currently write more than 1 level of leaves. 

The potential issue with constraining to a limited size leaf is it will implicitly create a maximum addressable archive size for single-leaf-level archives. In practice, this has never happened since the use cases I'm aware of are z0-z15 tilesets with significant redundancy. Adding GDAL write support and/or attempting larger tilesets (z16, less repetitive tilesets like DEMs) is a good point to revisit this. 
> Is there somewhere these warning levels are defined so we could reproduce them here to catch warnings in the future?
> 
> >

https://github.com/OSGeo/gdal/blob/master/gdal.cmake#L133 to https://github.com/OSGeo/gdal/blob/master/gdal.cmake#L177 is warning flags that GDAL tries to add for gcc/clang
and https://github.com/OSGeo/gdal/blob/master/gdal.cmake#L72 for MSVC

Fix patch:
diff --git a/cpp/Makefile b/cpp/Makefile
index f2e8f9c..4e4ecef 100644
--- a/cpp/Makefile
+++ b/cpp/Makefile
@@ -1,6 +1,6 @@
 .PHONY: test
 test: 
-	clang test.cpp -std=c++11 -lstdc++ -o test && ./test
+	clang test.cpp -std=c++11 -lstdc++ -Wall -Wextra -Winit-self -Wunused-parameter -Wmissing-prototypes -Wmissing-declarations -Wshorten-64-to-32 -Wnull-dereference -Wzero-as-null-pointer-constant -Wold-style-cast -o test && ./test
 
 indent:
 	clang-format -i -style="{BasedOnStyle: Google, IndentWidth: 8, UseTab: Always, AllowShortIfStatementsOnASingleLine: false, ColumnLimit: 0, ContinuationIndentWidth: 8, SpaceAfterCStyleCast: true, IndentCaseLabels: false, AllowShortBlocksOnASingleLine: false, AllowShortFunctionsOnASingleLine: false, SortIncludes: false}" pmtiles.hpp test.cpp


Test patch:
diff --git a/cpp/test.cpp b/cpp/test.cpp
index 3f83237..0d9088c 100644
--- a/cpp/test.cpp
+++ b/cpp/test.cpp
@@ -47,7 +47,7 @@ MU_TEST(test_zxy_to_tileid) {
 
 MU_TEST(test_roundtrip) {
 	for (int z = 0; z < 32; z++) {
-		uint64_t dim = (1 << z) - 1;
+		uint32_t dim = (1 << z) - 1;
 		auto tl = tileid_to_zxy(zxy_to_tileid(z, 0, 0));
 		mu_check(tl.z == z);
 		mu_check(tl.x == 0);
@@ -184,13 +184,13 @@ MU_TEST(test_deserialize_header) {
 	mu_check(deserialized.center_lat_e7 == 19);
 }
 
-string mycompress(const string &input, uint8_t compression) {
+static string mycompress(const string &input, uint8_t compression) {
 	if (compression != pmtiles::COMPRESSION_NONE)
 		throw runtime_error("Unsupported compression");
 	return input;
 }
 
-string mydecompress(const string &input, uint8_t compression) {
+static string mydecompress(const string &input, uint8_t compression) {
 	if (compression != pmtiles::COMPRESSION_NONE)
 		throw runtime_error("Unsupported compression");
 	return input;
@@ -238,7 +238,7 @@ MU_TEST(test_build_dirs) {
 	struct stat st;
 	int fd = open("tmp.pmtiles", O_RDONLY);
 	fstat(fd, &st);
-	char *map = (char *) mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+	char *map = static_cast<char *>(mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0));
 
 	auto result = entries_tms(&mydecompress, map);
 	mu_check(result.size() == 100000);
@@ -272,7 +272,7 @@ MU_TEST_SUITE(test_suite) {
 	MU_RUN_TEST(test_build_dirs_one);
 }
 
-int main(int argc, char *argv[]) {
+int main() {
 	MU_RUN_SUITE(test_suite);
 	MU_REPORT();
 	return MU_EXIT_CODE;