diff --git a/examples/neighbors.c b/examples/neighbors.c index 2482c029d..ce6f7c7ae 100644 --- a/examples/neighbors.c +++ b/examples/neighbors.c @@ -27,7 +27,8 @@ int main(int argc, char *argv[]) { // Distance away from the origin to find: int k = 2; - int maxNeighboring = maxGridDiskSize(k); + int64_t maxNeighboring; + maxGridDiskSize(k, &maxNeighboring); H3Index *neighboring = calloc(maxNeighboring, sizeof(H3Index)); gridDisk(indexed, k, neighboring); diff --git a/src/apps/benchmarks/benchmarkGridDiskCells.c b/src/apps/benchmarks/benchmarkGridDiskCells.c index 348a538b9..3a132b0c1 100644 --- a/src/apps/benchmarks/benchmarkGridDiskCells.c +++ b/src/apps/benchmarks/benchmarkGridDiskCells.c @@ -23,7 +23,12 @@ H3Index pentagon = 0x89080000003ffff; BEGIN_BENCHMARKS(); -H3Index *out = malloc(H3_EXPORT(maxGridDiskSize)(40) * sizeof(H3Index)); +int64_t outSz; +if (H3_EXPORT(maxGridDiskSize)(40, &outSz)) { + printf("Failed\n"); + return 1; +} +H3Index *out = calloc(outSz, sizeof(H3Index)); BENCHMARK(gridDisk10, 10000, { H3_EXPORT(gridDisk)(hex, 10, out); }); BENCHMARK(gridDisk20, 10000, { H3_EXPORT(gridDisk)(hex, 20, out); }); diff --git a/src/apps/filters/gridDisk.c b/src/apps/filters/gridDisk.c index 09aff9c67..48296446e 100644 --- a/src/apps/filters/gridDisk.c +++ b/src/apps/filters/gridDisk.c @@ -36,12 +36,13 @@ #include "utility.h" void doCell(H3Index h, int k, int printDistances) { - int maxSize = H3_EXPORT(maxGridDiskSize)(k); + int64_t maxSize; + H3_EXPORT(maxGridDiskSize)(k, &maxSize); H3Index *rings = calloc(maxSize, sizeof(H3Index)); int *distances = calloc(maxSize, sizeof(int)); H3_EXPORT(gridDiskDistances)(h, k, rings, distances); - for (int i = 0; i < maxSize; i++) { + for (int64_t i = 0; i < maxSize; i++) { if (rings[i] != 0) { h3Print(rings[i]); if (printDistances) { diff --git a/src/apps/filters/gridDiskUnsafe.c b/src/apps/filters/gridDiskUnsafe.c index b94766460..17f3dfbea 100644 --- a/src/apps/filters/gridDiskUnsafe.c +++ b/src/apps/filters/gridDiskUnsafe.c @@ -37,11 +37,12 @@ #include "utility.h" void doCell(H3Index h, int k) { - int maxSize = H3_EXPORT(maxGridDiskSize)(k); + int64_t maxSize; + H3_EXPORT(maxGridDiskSize)(k, &maxSize); H3Index *rings = calloc(maxSize, sizeof(H3Index)); if (!H3_EXPORT(gridDiskUnsafe)(h, k, rings)) { - for (int i = 0; i < maxSize; i++) { + for (int64_t i = 0; i < maxSize; i++) { h3Println(rings[i]); } } else { diff --git a/src/apps/fuzzers/fuzzerGridDisk.c b/src/apps/fuzzers/fuzzerGridDisk.c index 024166c18..7e004252a 100644 --- a/src/apps/fuzzers/fuzzerGridDisk.c +++ b/src/apps/fuzzers/fuzzerGridDisk.c @@ -35,7 +35,8 @@ int main(int argc, char *argv[]) { } fclose(fp); - int sz = H3_EXPORT(maxGridDiskSize)(args.k); + int64_t sz; + H3_EXPORT(maxGridDiskSize)(args.k, &sz); H3Index *results = calloc(sizeof(H3Index), sz); if (results != NULL) { H3_EXPORT(gridDisk)(args.index, args.k, results); diff --git a/src/apps/testapps/testCompactCells.c b/src/apps/testapps/testCompactCells.c index 630618c2c..bdad998c4 100644 --- a/src/apps/testapps/testCompactCells.c +++ b/src/apps/testapps/testCompactCells.c @@ -30,7 +30,8 @@ H3Index uncompactableWithZero[] = {0x89283470803ffff, 0x8928347081bffff, 0, SUITE(compactCells) { TEST(roundtrip) { int k = 9; - int hexCount = H3_EXPORT(maxGridDiskSize)(k); + int64_t hexCount; + t_assertSuccess(H3_EXPORT(maxGridDiskSize)(k, &hexCount)); int expectedCompactCount = 73; // Generate a set of hexagons to compact @@ -42,7 +43,7 @@ SUITE(compactCells) { H3_EXPORT(compactCells)(sunnyvaleExpanded, compressed, hexCount)); int count = 0; - for (int i = 0; i < hexCount; i++) { + for (int64_t i = 0; i < hexCount; i++) { if (compressed[i] != 0) { count++; } diff --git a/src/apps/testapps/testDirectedEdge.c b/src/apps/testapps/testDirectedEdge.c index 8ecefe8f8..e8a55e99f 100644 --- a/src/apps/testapps/testDirectedEdge.c +++ b/src/apps/testapps/testDirectedEdge.c @@ -42,7 +42,9 @@ SUITE(directedEdge) { t_assert(!isNeighbor, "an index does not neighbor itself"); int neighbors = 0; - for (int i = 0; i < H3_EXPORT(maxGridDiskSize)(1); i++) { + int64_t neighborsSize; + t_assertSuccess(H3_EXPORT(maxGridDiskSize)(1, &neighborsSize)); + for (int64_t i = 0; i < neighborsSize; i++) { if (ring[i] != 0) { t_assertSuccess( H3_EXPORT(areNeighborCells)(sf, ring[i], &isNeighbor)); @@ -58,7 +60,8 @@ SUITE(directedEdge) { t_assertSuccess(H3_EXPORT(gridRingUnsafe)(sf, 2, largerRing)); neighbors = 0; - for (int i = 0; i < H3_EXPORT(maxGridDiskSize)(2); i++) { + t_assertSuccess(H3_EXPORT(maxGridDiskSize)(2, &neighborsSize)); + for (int64_t i = 0; i < neighborsSize; i++) { if (largerRing[i] != 0) { t_assertSuccess(H3_EXPORT(areNeighborCells)(sf, largerRing[i], &isNeighbor)); diff --git a/src/apps/testapps/testGridDisk.c b/src/apps/testapps/testGridDisk.c index 29ce9df8b..71718f40b 100644 --- a/src/apps/testapps/testGridDisk.c +++ b/src/apps/testapps/testGridDisk.c @@ -29,7 +29,8 @@ static void gridDisk_equals_gridDiskDistancesSafe_assertions(H3Index h3) { for (int k = 0; k < 3; k++) { - int kSz = H3_EXPORT(maxGridDiskSize)(k); + int64_t kSz; + t_assertSuccess(H3_EXPORT(maxGridDiskSize)(k, &kSz)); H3Index *neighbors = calloc(kSz, sizeof(H3Index)); int *distances = calloc(kSz, sizeof(int)); @@ -43,11 +44,11 @@ static void gridDisk_equals_gridDiskDistancesSafe_assertions(H3Index h3) { int found = 0; int internalFound = 0; - for (int iNeighbor = 0; iNeighbor < kSz; iNeighbor++) { + for (int64_t iNeighbor = 0; iNeighbor < kSz; iNeighbor++) { if (neighbors[iNeighbor] != 0) { found++; - for (int iInternal = 0; iInternal < kSz; iInternal++) { + for (int64_t iInternal = 0; iInternal < kSz; iInternal++) { if (internalNeighbors[iInternal] == neighbors[iNeighbor]) { internalFound++; @@ -360,7 +361,8 @@ SUITE(gridDisk) { TEST(gridDiskInvalid) { int k = 1000; - int kSz = H3_EXPORT(maxGridDiskSize)(k); + int64_t kSz; + t_assertSuccess(H3_EXPORT(maxGridDiskSize)(k, &kSz)); H3Index *neighbors = calloc(kSz, sizeof(H3Index)); t_assert(H3_EXPORT(gridDisk)(0x7fffffffffffffff, k, neighbors) == E_CELL_INVALID, @@ -370,11 +372,37 @@ SUITE(gridDisk) { TEST(gridDiskInvalidDigit) { int k = 2; - int kSz = H3_EXPORT(maxGridDiskSize)(k); + int64_t kSz; + t_assertSuccess(H3_EXPORT(maxGridDiskSize)(k, &kSz)); H3Index *neighbors = calloc(kSz, sizeof(H3Index)); t_assert(H3_EXPORT(gridDisk)(0x4d4b00fe5c5c3030, k, neighbors) == E_CELL_INVALID, "gridDisk returns error for invalid input"); free(neighbors); } + + TEST(gridDiskDistances_invalidK) { + H3Index index = 0x811d7ffffffffff; + t_assert( + H3_EXPORT(gridDiskDistances)(index, -1, NULL, NULL) == E_DOMAIN, + "gridDiskDistances invalid k"); + t_assert(H3_EXPORT(gridDiskDistancesUnsafe)(index, -1, NULL, NULL) == + E_DOMAIN, + "gridDiskDistancesUnsafe invalid k"); + t_assert( + H3_EXPORT(gridDiskDistancesSafe)(index, -1, NULL, NULL) == E_DOMAIN, + "gridDiskDistancesSafe invalid k"); + } + + TEST(maxGridDiskSize_invalid) { + int64_t sz; + t_assert(H3_EXPORT(maxGridDiskSize)(-1, &sz) == E_DOMAIN, + "negative k is invalid"); + } + + TEST(maxGridDiskSize_large) { + int64_t sz; + t_assertSuccess(H3_EXPORT(maxGridDiskSize)(26755, &sz)); + t_assert(sz == 2147570341, "large (> 32 bit signed int) k works"); + } } diff --git a/src/apps/testapps/testGridDisksUnsafe.c b/src/apps/testapps/testGridDisksUnsafe.c index 32b450137..516b09b9a 100644 --- a/src/apps/testapps/testGridDisksUnsafe.c +++ b/src/apps/testapps/testGridDisksUnsafe.c @@ -76,4 +76,9 @@ SUITE(gridDisksUnsafe) { "Expected error on gridDisksUnsafe"); free(allKrings); } + + TEST(invalid_k) { + t_assert(H3_EXPORT(gridDisksUnsafe)(k1, 6, -1, NULL) == E_DOMAIN, + "gridDisksUnsafe invalid k"); + } } diff --git a/src/apps/testapps/testGridDistanceExhaustive.c b/src/apps/testapps/testGridDistanceExhaustive.c index 2cf1162c4..eb3f7a014 100644 --- a/src/apps/testapps/testGridDistanceExhaustive.c +++ b/src/apps/testapps/testGridDistanceExhaustive.c @@ -45,14 +45,15 @@ static void gridDistance_gridDisk_assertions(H3Index h3) { t_assert(r <= 5, "resolution supported by test function (gridDisk)"); int maxK = MAX_DISTANCES[r]; - int sz = H3_EXPORT(maxGridDiskSize)(maxK); + int64_t sz; + t_assertSuccess(H3_EXPORT(maxGridDiskSize)(maxK, &sz)); H3Index *neighbors = calloc(sz, sizeof(H3Index)); int *distances = calloc(sz, sizeof(int)); t_assertSuccess( H3_EXPORT(gridDiskDistances)(h3, maxK, neighbors, distances)); - for (int i = 0; i < sz; i++) { + for (int64_t i = 0; i < sz; i++) { if (neighbors[i] == 0) { continue; } diff --git a/src/apps/testapps/testGridPathCellsExhaustive.c b/src/apps/testapps/testGridPathCellsExhaustive.c index 8ffb81e83..92cf7d2a5 100644 --- a/src/apps/testapps/testGridPathCellsExhaustive.c +++ b/src/apps/testapps/testGridPathCellsExhaustive.c @@ -85,7 +85,8 @@ static void gridPathCells_gridDisk_assertions(H3Index h3) { t_assert(r <= 5, "resolution supported by test function (gridDisk)"); int maxK = MAX_DISTANCES[r]; - int sz = H3_EXPORT(maxGridDiskSize)(maxK); + int64_t sz; + t_assertSuccess(H3_EXPORT(maxGridDiskSize)(maxK, &sz)); if (H3_EXPORT(isPentagon)(h3)) { return; diff --git a/src/apps/testapps/testGridRingUnsafe.c b/src/apps/testapps/testGridRingUnsafe.c index 0255516d3..24c01d75d 100644 --- a/src/apps/testapps/testGridRingUnsafe.c +++ b/src/apps/testapps/testGridRingUnsafe.c @@ -113,7 +113,8 @@ SUITE(gridRingUnsafe) { for (int k = 0; k < 3; k++) { int ringSz = k != 0 ? 6 * k : 1; - int kSz = H3_EXPORT(maxGridDiskSize)(k); + int64_t kSz; + t_assertSuccess(H3_EXPORT(maxGridDiskSize)(k, &kSz)); H3Index *ring = calloc(ringSz, sizeof(H3Index)); H3Error failed = @@ -133,7 +134,7 @@ SUITE(gridRingUnsafe) { if (ring[iRing] != 0) { found++; - for (int iInternal = 0; iInternal < kSz; + for (int64_t iInternal = 0; iInternal < kSz; iInternal++) { if (internalNeighbors[iInternal] == ring[iRing]) { diff --git a/src/apps/testapps/testH3Memory.c b/src/apps/testapps/testH3Memory.c index e5c8d250e..f75a93e7d 100644 --- a/src/apps/testapps/testH3Memory.c +++ b/src/apps/testapps/testH3Memory.c @@ -95,7 +95,8 @@ static GeoPolygon sfGeoPolygon; SUITE(h3Memory) { TEST(gridDisk) { int k = 2; - int hexCount = H3_EXPORT(maxGridDiskSize)(k); + int64_t hexCount; + t_assertSuccess(H3_EXPORT(maxGridDiskSize)(k, &hexCount)); H3Index *gridDiskOutput = calloc(hexCount, sizeof(H3Index)); resetMemoryCounters(0); @@ -116,7 +117,7 @@ SUITE(h3Memory) { t_assert(actualAllocCalls == 1, "gridDisk called alloc"); t_assert(actualFreeCalls == 0, "gridDisk did not call free"); - for (int i = 0; i < hexCount; i++) { + for (int64_t i = 0; i < hexCount; i++) { t_assert(!gridDiskOutput[i], "gridDisk did not produce output without alloc"); } @@ -126,8 +127,9 @@ SUITE(h3Memory) { TEST(compactCells) { int k = 9; - int hexCount = H3_EXPORT(maxGridDiskSize)(k); - int expectedCompactCount = 73; + int64_t hexCount; + t_assertSuccess(H3_EXPORT(maxGridDiskSize)(k, &hexCount)); + int64_t expectedCompactCount = 73; // Generate a set of hexagons to compact H3Index *sunnyvaleExpanded = calloc(hexCount, sizeof(H3Index)); @@ -170,8 +172,8 @@ SUITE(h3Memory) { t_assert(actualAllocCalls == 4, "alloc called four times"); t_assert(actualFreeCalls == 4, "free called four times"); - int count = 0; - for (int i = 0; i < hexCount; i++) { + int64_t count = 0; + for (int64_t i = 0; i < hexCount; i++) { if (compressed[i] != 0) { count++; } diff --git a/src/apps/testapps/testH3NeighborRotations.c b/src/apps/testapps/testH3NeighborRotations.c index ba65d5a74..da8e95797 100644 --- a/src/apps/testapps/testH3NeighborRotations.c +++ b/src/apps/testapps/testH3NeighborRotations.c @@ -50,7 +50,8 @@ typedef struct { void doCell(H3Index h, int maxK, TestOutput *testOutput) { for (int k = 0; k < maxK; k++) { - int maxSz = H3_EXPORT(maxGridDiskSize)(k); + int64_t maxSz; + H3_EXPORT(maxGridDiskSize)(k, &maxSz); H3Index *gridDiskInternalOutput = calloc(sizeof(H3Index), maxSz); H3Index *gridDiskUnsafeOutput = calloc(sizeof(H3Index), maxSz); int *gridDiskInternalDistances = calloc(sizeof(int), maxSz); @@ -77,7 +78,7 @@ void doCell(H3Index h, int maxK, TestOutput *testOutput) { H3Index h2 = gridDiskUnsafeOutput[ii + startIdx]; int found = 0; - for (int iii = 0; iii < maxSz; iii++) { + for (int64_t iii = 0; iii < maxSz; iii++) { if (gridDiskInternalOutput[iii] == h2 && gridDiskInternalDistances[iii] == i) { found = 1; @@ -99,7 +100,7 @@ void doCell(H3Index h, int maxK, TestOutput *testOutput) { } else if (gridDiskUnsafeFailed == E_PENTAGON) { testOutput->ret1++; int foundPent = 0; - for (int i = 0; i < maxSz; i++) { + for (int64_t i = 0; i < maxSz; i++) { if (H3_EXPORT(isPentagon)(gridDiskInternalOutput[i])) { foundPent = 1; break; diff --git a/src/apps/testapps/testH3ToLocalIjExhaustive.c b/src/apps/testapps/testH3ToLocalIjExhaustive.c index ea0255d9b..82410c5b3 100644 --- a/src/apps/testapps/testH3ToLocalIjExhaustive.c +++ b/src/apps/testapps/testH3ToLocalIjExhaustive.c @@ -132,14 +132,15 @@ void localIjToH3_gridDisk_assertions(H3Index h3) { t_assert(r <= 5, "resolution supported by test function (gridDisk)"); int maxK = MAX_DISTANCES[r]; - int sz = H3_EXPORT(maxGridDiskSize)(maxK); + int64_t sz; + t_assertSuccess(H3_EXPORT(maxGridDiskSize)(maxK, &sz)); H3Index *neighbors = calloc(sz, sizeof(H3Index)); int *distances = calloc(sz, sizeof(int)); t_assertSuccess( H3_EXPORT(gridDiskDistances)(h3, maxK, neighbors, distances)); - for (int i = 0; i < sz; i++) { + for (int64_t i = 0; i < sz; i++) { if (neighbors[i] == 0) { continue; } diff --git a/src/h3lib/include/algos.h b/src/h3lib/include/algos.h index 9e08744ba..72e3f64ba 100644 --- a/src/h3lib/include/algos.h +++ b/src/h3lib/include/algos.h @@ -52,5 +52,5 @@ H3Error _getEdgeHexagons(const GeoLoop *geoloop, int64_t numHexagons, int res, // The safe gridDiskDistances algorithm. H3Error _gridDiskDistancesInternal(H3Index origin, int k, H3Index *out, - int *distances, int maxIdx, int curK); + int *distances, int64_t maxIdx, int curK); #endif diff --git a/src/h3lib/include/h3api.h.in b/src/h3lib/include/h3api.h.in index 5a796ba55..64df1e978 100644 --- a/src/h3lib/include/h3api.h.in +++ b/src/h3lib/include/h3api.h.in @@ -226,7 +226,7 @@ DECLSPEC H3Error H3_EXPORT(cellToBoundary)(H3Index h3, CellBoundary *gp); * @{ */ /** @brief maximum number of hexagons in k-ring */ -DECLSPEC int H3_EXPORT(maxGridDiskSize)(int k); +DECLSPEC H3Error H3_EXPORT(maxGridDiskSize)(int k, int64_t *out); /** @brief hexagons neighbors in all directions, assuming no pentagons */ DECLSPEC H3Error H3_EXPORT(gridDiskUnsafe)(H3Index origin, int k, H3Index *out); diff --git a/src/h3lib/lib/algos.c b/src/h3lib/lib/algos.c index e47b65600..e637eb4f2 100644 --- a/src/h3lib/lib/algos.c +++ b/src/h3lib/lib/algos.c @@ -155,9 +155,16 @@ static const Direction NEW_ADJUSTMENT_III[7][7] = { * Maximum number of cells that result from the gridDisk algorithm with the * given k. Formula source and proof: https://oeis.org/A003215 * - * @param k k value, k >= 0. + * @param k k value, k >= 0. + * @param out size in indexes */ -int H3_EXPORT(maxGridDiskSize)(int k) { return 3 * k * (k + 1) + 1; } +H3Error H3_EXPORT(maxGridDiskSize)(int k, int64_t *out) { + if (k < 0) { + return E_DOMAIN; + } + *out = 3 * (int64_t)k * ((int64_t)k + 1) + 1; + return E_SUCCESS; +} /** * Produce cells within grid distance k of the origin cell. @@ -199,7 +206,11 @@ H3Error H3_EXPORT(gridDiskDistances)(H3Index origin, int k, H3Index *out, const H3Error failed = H3_EXPORT(gridDiskDistancesUnsafe)(origin, k, out, distances); if (failed) { - const int maxIdx = H3_EXPORT(maxGridDiskSize)(k); + int64_t maxIdx; + H3Error err = H3_EXPORT(maxGridDiskSize)(k, &maxIdx); + if (err) { + return err; + } // Fast algo failed, fall back to slower, correct algo // and also wipe out array because contents untrustworthy memset(out, 0, maxIdx * sizeof(H3Index)); @@ -241,9 +252,9 @@ H3Error H3_EXPORT(gridDiskDistances)(H3Index origin, int k, H3Index *out, * @param curK Current distance from the origin */ H3Error _gridDiskDistancesInternal(H3Index origin, int k, H3Index *out, - int *distances, int maxIdx, int curK) { + int *distances, int64_t maxIdx, int curK) { // Put origin in the output array. out is used as a hash set. - int off = origin % maxIdx; + int64_t off = origin % maxIdx; while (out[off] != 0 && out[off] != origin) { off = (off + 1) % maxIdx; } @@ -297,7 +308,11 @@ H3Error _gridDiskDistancesInternal(H3Index origin, int k, H3Index *out, */ H3Error H3_EXPORT(gridDiskDistancesSafe)(H3Index origin, int k, H3Index *out, int *distances) { - int maxIdx = H3_EXPORT(maxGridDiskSize)(k); + int64_t maxIdx; + H3Error err = H3_EXPORT(maxGridDiskSize)(k, &maxIdx); + if (err) { + return err; + } return _gridDiskDistancesInternal(origin, k, out, distances, maxIdx, 0); } @@ -531,6 +546,9 @@ H3Error H3_EXPORT(gridDiskDistancesUnsafe)(H3Index origin, int k, H3Index *out, // Pentagon being encountered is not itself a problem; really the deleted // k-subsequence is the problem, but for compatibility reasons we fail on // the pentagon. + if (k < 0) { + return E_DOMAIN; + } // k must be >= 0, so origin is always needed int idx = 0; @@ -619,7 +637,11 @@ H3Error H3_EXPORT(gridDiskDistancesUnsafe)(H3Index origin, int k, H3Index *out, H3Error H3_EXPORT(gridDisksUnsafe)(H3Index *h3Set, int length, int k, H3Index *out) { H3Index *segment; - int segmentSize = H3_EXPORT(maxGridDiskSize)(k); + int64_t segmentSize; + H3Error err = H3_EXPORT(maxGridDiskSize)(k, &segmentSize); + if (err) { + return err; + } for (int i = 0; i < length; i++) { // Determine the appropriate segment of the output array to operate on segment = out + i * segmentSize; diff --git a/website/docs/api/traversal.mdx b/website/docs/api/traversal.mdx index cece8c366..6cca6f334 100644 --- a/website/docs/api/traversal.mdx +++ b/website/docs/api/traversal.mdx @@ -85,7 +85,7 @@ the output array may be left as zero, which can happen when crossing a pentagon. ```c -int maxGridDiskSize(int k); +H3Error maxGridDiskSize(int k, int64_t *out); ```