Coverage Report

Created: 2026-05-30 06:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/h3/src/h3lib/lib/linkedGeo.c
Line
Count
Source
1
/*
2
 * Copyright 2017-2018, 2020-2021 Uber Technologies, Inc.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *         http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
/** @file linkedGeo.c
17
 * @brief   Linked data structure for geo data
18
 */
19
20
#include "linkedGeo.h"
21
22
#include <assert.h>
23
#include <stdlib.h>
24
25
#include "alloc.h"
26
#include "cellsToMultiPoly.h"
27
#include "h3Assert.h"
28
#include "h3api.h"
29
#include "polygon.h"
30
31
/**
32
 * Add a linked polygon to the current polygon
33
 * @param  polygon Polygon to add link to
34
 * @return         Pointer to new polygon
35
 */
36
0
LinkedGeoPolygon *addNewLinkedPolygon(LinkedGeoPolygon *polygon) {
37
0
    assert(polygon->next == NULL);
38
0
    LinkedGeoPolygon *next = H3_MEMORY(calloc)(1, sizeof(*next));
39
0
    assert(next != NULL);
40
0
    polygon->next = next;
41
0
    return next;
42
0
}
43
44
/**
45
 * Add a new linked loop to the current polygon
46
 * @param  polygon Polygon to add loop to
47
 * @return         Pointer to loop
48
 */
49
0
LinkedGeoLoop *addNewLinkedLoop(LinkedGeoPolygon *polygon) {
50
0
    LinkedGeoLoop *loop = H3_MEMORY(calloc)(1, sizeof(*loop));
51
0
    assert(loop != NULL);
52
0
    return addLinkedLoop(polygon, loop);
53
0
}
54
55
/**
56
 * Add an existing linked loop to the current polygon
57
 * @param  polygon Polygon to add loop to
58
 * @return         Pointer to loop
59
 */
60
0
LinkedGeoLoop *addLinkedLoop(LinkedGeoPolygon *polygon, LinkedGeoLoop *loop) {
61
0
    LinkedGeoLoop *last = polygon->last;
62
0
    if (last == NULL) {
63
0
        assert(polygon->first == NULL);
64
0
        polygon->first = loop;
65
0
    } else {
66
0
        last->next = loop;
67
0
    }
68
0
    polygon->last = loop;
69
0
    return loop;
70
0
}
71
72
/**
73
 * Add a new linked coordinate to the current loop
74
 * @param  loop   Loop to add coordinate to
75
 * @param  vertex Coordinate to add
76
 * @return        Pointer to the coordinate
77
 */
78
0
LinkedLatLng *addLinkedCoord(LinkedGeoLoop *loop, const LatLng *vertex) {
79
0
    LinkedLatLng *coord = H3_MEMORY(malloc)(sizeof(*coord));
80
0
    assert(coord != NULL);
81
0
    *coord = (LinkedLatLng){.vertex = *vertex, .next = NULL};
82
0
    LinkedLatLng *last = loop->last;
83
0
    if (last == NULL) {
84
0
        assert(loop->first == NULL);
85
0
        loop->first = coord;
86
0
    } else {
87
0
        last->next = coord;
88
0
    }
89
0
    loop->last = coord;
90
0
    return coord;
91
0
}
92
93
/**
94
 * Free all allocated memory for a linked geo loop. The caller is
95
 * responsible for freeing memory allocated to input loop struct.
96
 * @param loop Loop to free
97
 */
98
0
void destroyLinkedGeoLoop(LinkedGeoLoop *loop) {
99
0
    LinkedLatLng *nextCoord;
100
0
    for (LinkedLatLng *currentCoord = loop->first; currentCoord != NULL;
101
0
         currentCoord = nextCoord) {
102
0
        nextCoord = currentCoord->next;
103
0
        H3_MEMORY(free)(currentCoord);
104
0
    }
105
0
}
106
107
/**
108
 * Free all allocated memory for a linked geo structure. The caller is
109
 * responsible for freeing memory allocated to input polygon struct.
110
 * @param polygon Pointer to the first polygon in the structure
111
 */
112
0
void H3_EXPORT(destroyLinkedMultiPolygon)(LinkedGeoPolygon *polygon) {
113
    // flag to skip the input polygon
114
0
    bool skip = true;
115
0
    LinkedGeoPolygon *nextPolygon;
116
0
    LinkedGeoLoop *nextLoop;
117
0
    for (LinkedGeoPolygon *currentPolygon = polygon; currentPolygon != NULL;
118
0
         currentPolygon = nextPolygon) {
119
0
        for (LinkedGeoLoop *currentLoop = currentPolygon->first;
120
0
             currentLoop != NULL; currentLoop = nextLoop) {
121
0
            destroyLinkedGeoLoop(currentLoop);
122
0
            nextLoop = currentLoop->next;
123
0
            H3_MEMORY(free)(currentLoop);
124
0
        }
125
0
        nextPolygon = currentPolygon->next;
126
0
        if (skip) {
127
            // do not free the input polygon, but zero it so this
128
            // function is idempotent (safe to call twice)
129
0
            *polygon = (LinkedGeoPolygon){0};
130
0
            skip = false;
131
0
        } else {
132
0
            H3_MEMORY(free)(currentPolygon);
133
0
        }
134
0
    }
135
0
}
136
137
/**
138
 * Count the number of polygons in a linked list
139
 * @param  polygon Starting polygon
140
 * @return         Count
141
 */
142
0
int countLinkedPolygons(const LinkedGeoPolygon *polygon) {
143
0
    int count = 0;
144
0
    while (polygon != NULL) {
145
0
        count++;
146
0
        polygon = polygon->next;
147
0
    }
148
0
    return count;
149
0
}
150
151
/**
152
 * Count the number of linked loops in a polygon
153
 * @param  polygon Polygon to count loops for
154
 * @return         Count
155
 */
156
0
int countLinkedLoops(const LinkedGeoPolygon *polygon) {
157
0
    LinkedGeoLoop *loop = polygon->first;
158
0
    int count = 0;
159
0
    while (loop != NULL) {
160
0
        count++;
161
0
        loop = loop->next;
162
0
    }
163
0
    return count;
164
0
}
165
166
/**
167
 * Count the number of coordinates in a loop
168
 * @param  loop Loop to count coordinates for
169
 * @return      Count
170
 */
171
0
int countLinkedCoords(const LinkedGeoLoop *loop) {
172
0
    LinkedLatLng *coord = loop->first;
173
0
    int count = 0;
174
0
    while (coord != NULL) {
175
0
        count++;
176
0
        coord = coord->next;
177
0
    }
178
0
    return count;
179
0
}
180
181
/**
182
 * Convert a LinkedGeoLoop to a GeoLoop by counting coords and copying.
183
 * @param  linked  Source LinkedGeoLoop
184
 * @param  out     Output GeoLoop (verts will be allocated)
185
 * @return         E_SUCCESS, E_FAILED (< 3 verts), or E_MEMORY_ALLOC
186
 */
187
static H3Error linkedGeoLoopToGeoLoop(const LinkedGeoLoop *linked,
188
0
                                      GeoLoop *out) {
189
0
    int numVerts = countLinkedCoords(linked);
190
0
    if (numVerts < 3) {
191
0
        return E_FAILED;
192
0
    }
193
0
    LatLng *verts = H3_MEMORY(calloc)(numVerts, sizeof(LatLng));
194
0
    if (!verts) return E_MEMORY_ALLOC;
195
196
0
    LinkedLatLng *coord = linked->first;
197
0
    for (int i = 0; coord != NULL && ALWAYS(i < numVerts); i++) {
198
0
        verts[i] = coord->vertex;
199
0
        coord = coord->next;
200
0
    }
201
0
    out->numVerts = numVerts;
202
0
    out->verts = verts;
203
0
    return E_SUCCESS;
204
0
}
205
206
/**
207
 * Convert a single LinkedGeoPolygon (outer loop + holes) to a GeoPolygon.
208
 * The output's geoloop and holes are allocated; on failure, all partial
209
 * allocations are freed and `out` is left in a clean (zeroed) state.
210
 * @param  linked  Source LinkedGeoPolygon
211
 * @param  out     Output GeoPolygon (caller-owned, will be populated)
212
 * @return         E_SUCCESS or E_MEMORY_ALLOC
213
 */
214
static H3Error linkedGeoPolygonToGeoPolygon(const LinkedGeoPolygon *linked,
215
0
                                            GeoPolygon *out) {
216
    // Convert outer loop
217
0
    const LinkedGeoLoop *firstLoop = linked->first;
218
0
    H3Error err = linkedGeoLoopToGeoLoop(firstLoop, &out->geoloop);
219
0
    if (err) return err;
220
221
    // Count and convert holes
222
0
    int numHoles = countLinkedLoops(linked) - 1;
223
0
    if (numHoles > 0) {
224
0
        GeoLoop *holes = H3_MEMORY(calloc)(numHoles, sizeof(GeoLoop));
225
0
        if (!holes) {
226
0
            destroyGeoPolygon(out);
227
0
            return E_MEMORY_ALLOC;
228
0
        }
229
0
        out->holes = holes;
230
0
        out->numHoles = numHoles;
231
232
0
        LinkedGeoLoop *loop = firstLoop->next;
233
0
        for (int i = 0; loop != NULL && ALWAYS(i < numHoles); i++) {
234
0
            err = linkedGeoLoopToGeoLoop(loop, &holes[i]);
235
0
            if (err) {
236
0
                destroyGeoPolygon(out);
237
0
                return err;
238
0
            }
239
0
            loop = loop->next;
240
0
        }
241
0
    }
242
243
0
    return E_SUCCESS;
244
0
}
245
246
/**
247
 * Convert a LinkedGeoPolygon to a GeoMultiPolygon.
248
 *
249
 * An empty chain (head node with no loops and no `next`) produces an empty
250
 * output (numPolygons=0) and returns E_SUCCESS. Every non-empty polygon
251
 * node must have an outer loop, and every loop must have >= 3 vertices;
252
 * otherwise, E_FAILED is returned.
253
 *
254
 * On error, any (partial) output is cleaned up via `destroyGeoMultiPolygon()`.
255
 * On success the caller owns the output and must free
256
 * it with `destroyGeoMultiPolygon()`.
257
 *
258
 * @param  linked  Head of LinkedGeoPolygon chain
259
 * @param  out     Output GeoMultiPolygon (caller-owned, will be populated)
260
 * @return         E_SUCCESS, E_FAILED (invalid geometry), or E_MEMORY_ALLOC
261
 */
262
H3Error linkedGeoPolygonToGeoMultiPolygon(const LinkedGeoPolygon *linked,
263
0
                                          GeoMultiPolygon *out) {
264
0
    out->numPolygons = 0;
265
0
    out->polygons = NULL;
266
267
    // Empty chain (head has no loops and no next) is valid: 0 polygons
268
0
    if (linked->first == NULL && linked->next == NULL) {
269
0
        return E_SUCCESS;
270
0
    }
271
272
0
    int numPolygons = countLinkedPolygons(linked);
273
274
0
    GeoPolygon *polygons = H3_MEMORY(calloc)(numPolygons, sizeof(GeoPolygon));
275
0
    if (!polygons) return E_MEMORY_ALLOC;
276
277
0
    out->polygons = polygons;
278
0
    out->numPolygons = numPolygons;
279
280
0
    const LinkedGeoPolygon *lpoly = linked;
281
0
    for (int i = 0; lpoly != NULL && ALWAYS(i < numPolygons); i++) {
282
0
        if (lpoly->first == NULL) {
283
0
            H3_EXPORT(destroyGeoMultiPolygon)(out);
284
0
            return E_FAILED;
285
0
        }
286
0
        H3Error err = linkedGeoPolygonToGeoPolygon(lpoly, &polygons[i]);
287
0
        if (err) {
288
0
            H3_EXPORT(destroyGeoMultiPolygon)(out);
289
0
            return err;
290
0
        }
291
0
        lpoly = lpoly->next;
292
0
    }
293
294
0
    return E_SUCCESS;
295
0
}
296
297
/**
298
 * Populate a LinkedGeoLoop with vertices from a GeoLoop.
299
 * @param  src   Source GeoLoop
300
 * @param  loop  Target LinkedGeoLoop (must be calloc-zeroed)
301
 * @return       E_SUCCESS or E_MEMORY_ALLOC
302
 */
303
0
static H3Error geoLoopToLinkedGeoLoop(const GeoLoop *src, LinkedGeoLoop *loop) {
304
0
    if (src->numVerts < 3) return E_FAILED;
305
0
    for (int i = 0; i < src->numVerts; i++) {
306
0
        LinkedLatLng *coord = H3_MEMORY(malloc)(sizeof(LinkedLatLng));
307
0
        if (!coord) return E_MEMORY_ALLOC;
308
309
0
        *coord = (LinkedLatLng){.vertex = src->verts[i], .next = NULL};
310
0
        if (loop->last == NULL) {
311
0
            loop->first = coord;
312
0
        } else {
313
0
            loop->last->next = coord;
314
0
        }
315
0
        loop->last = coord;
316
0
    }
317
0
    return E_SUCCESS;
318
0
}
319
320
/**
321
 * Convert a single GeoPolygon to LinkedGeoLoops within a LinkedGeoPolygon.
322
 * @param  poly        Source GeoPolygon
323
 * @param  currentPoly Target LinkedGeoPolygon to populate
324
 * @return             E_SUCCESS or E_MEMORY_ALLOC
325
 */
326
static H3Error addLinkedGeoLoop(const GeoLoop *gl,
327
0
                                LinkedGeoPolygon *currentPoly) {
328
0
    LinkedGeoLoop *loop = H3_MEMORY(calloc)(1, sizeof(LinkedGeoLoop));
329
0
    if (!loop) return E_MEMORY_ALLOC;
330
331
0
    if (currentPoly->last) {
332
0
        currentPoly->last->next = loop;
333
0
    } else {
334
0
        currentPoly->first = loop;
335
0
    }
336
0
    currentPoly->last = loop;
337
338
0
    return geoLoopToLinkedGeoLoop(gl, loop);
339
0
}
340
341
static H3Error geoPolygonToLinkedGeoLoops(const GeoPolygon *poly,
342
0
                                          LinkedGeoPolygon *currentPoly) {
343
0
    H3Error err = addLinkedGeoLoop(&poly->geoloop, currentPoly);
344
0
    if (err) return err;
345
346
0
    for (int i = 0; i < poly->numHoles; i++) {
347
0
        err = addLinkedGeoLoop(&poly->holes[i], currentPoly);
348
0
        if (err) return err;
349
0
    }
350
351
0
    return E_SUCCESS;
352
0
}
353
354
/**
355
 * Convert a GeoMultiPolygon to a LinkedGeoPolygon.
356
 *
357
 * The first polygon is placed in the caller-owned `out` node.
358
 * Every loop must have >= 3 vertices; otherwise E_FAILED is returned.
359
 *
360
 * On error, the output is cleaned up via `destroyLinkedMultiPolygon()`.
361
 * On success, the caller owns the output and must free it with
362
 * `destroyLinkedMultiPolygon()`.
363
 *
364
 * @param  mpoly  Source GeoMultiPolygon
365
 * @param  out    Output LinkedGeoPolygon
366
 * @return        E_SUCCESS, E_FAILED (invalid geometry), or E_MEMORY_ALLOC
367
 */
368
H3Error geoMultiPolygonToLinkedGeoPolygon(const GeoMultiPolygon *mpoly,
369
0
                                          LinkedGeoPolygon *out) {
370
0
    *out = (LinkedGeoPolygon){0};
371
372
0
    LinkedGeoPolygon *currentPoly = out;
373
0
    for (int i = 0; i < mpoly->numPolygons; i++) {
374
0
        if (i > 0) {
375
0
            LinkedGeoPolygon *newPoly =
376
0
                H3_MEMORY(calloc)(1, sizeof(LinkedGeoPolygon));
377
0
            if (!newPoly) {
378
0
                H3_EXPORT(destroyLinkedMultiPolygon)(out);
379
0
                return E_MEMORY_ALLOC;
380
0
            }
381
0
            currentPoly->next = newPoly;
382
0
            currentPoly = newPoly;
383
0
        }
384
385
0
        H3Error err =
386
0
            geoPolygonToLinkedGeoLoops(&mpoly->polygons[i], currentPoly);
387
0
        if (err) {
388
0
            H3_EXPORT(destroyLinkedMultiPolygon)(out);
389
0
            return err;
390
0
        }
391
0
    }
392
393
0
    return E_SUCCESS;
394
0
}
395
396
/**
397
 * Count the number of polygons containing a given loop.
398
 * @param  loop         Loop to count containers for
399
 * @param  polygons     Polygons to test
400
 * @param  bboxes       Bounding boxes for polygons, used in point-in-poly check
401
 * @param  polygonCount Number of polygons in the test array
402
 * @return              Number of polygons containing the loop
403
 */
404
static int countContainers(const LinkedGeoLoop *loop,
405
                           const LinkedGeoPolygon **polygons,
406
0
                           const BBox **bboxes, const int polygonCount) {
407
0
    int containerCount = 0;
408
0
    for (int i = 0; i < polygonCount; i++) {
409
0
        if (loop != polygons[i]->first &&
410
0
            pointInsideLinkedGeoLoop(polygons[i]->first, bboxes[i],
411
0
                                     &loop->first->vertex)) {
412
0
            containerCount++;
413
0
        }
414
0
    }
415
0
    return containerCount;
416
0
}
417
418
/**
419
 * Given a list of nested containers, find the one most deeply nested.
420
 * @param  polygons     Polygon containers to check
421
 * @param  bboxes       Bounding boxes for polygons, used in point-in-poly check
422
 * @param  polygonCount Number of polygons in the list
423
 * @return              Deepest container, or null if list is empty
424
 */
425
static const LinkedGeoPolygon *findDeepestContainer(
426
    const LinkedGeoPolygon **polygons, const BBox **bboxes,
427
0
    const int polygonCount) {
428
    // Set the initial return value to the first candidate
429
0
    const LinkedGeoPolygon *parent = polygonCount > 0 ? polygons[0] : NULL;
430
431
    // If we have multiple polygons, they must be nested inside each other.
432
    // Find the innermost polygon by taking the one with the most containers
433
    // in the list.
434
0
    if (polygonCount > 1) {
435
0
        int max = -1;
436
0
        for (int i = 0; i < polygonCount; i++) {
437
0
            int count = countContainers(polygons[i]->first, polygons, bboxes,
438
0
                                        polygonCount);
439
0
            if (count > max) {
440
0
                parent = polygons[i];
441
0
                max = count;
442
0
            }
443
0
        }
444
0
    }
445
446
0
    return parent;
447
0
}
448
449
/**
450
 * Find the polygon to which a given hole should be allocated. Note that this
451
 * function will return null if no parent is found.
452
 * @param  loop         Inner loop describing a hole
453
 * @param  polygon      Head of a linked list of polygons to check
454
 * @param  bboxes       Bounding boxes for polygons, used in point-in-poly check
455
 * @param  polygonCount Number of polygons to check
456
 * @return              Pointer to parent polygon, or null if not found
457
 */
458
static const LinkedGeoPolygon *findPolygonForHole(
459
    const LinkedGeoLoop *loop, const LinkedGeoPolygon *polygon,
460
0
    const BBox *bboxes, const int polygonCount) {
461
    // Early exit with no polygons
462
0
    if (polygonCount == 0) {
463
0
        return NULL;
464
0
    }
465
    // Initialize arrays for candidate loops and their bounding boxes
466
0
    const LinkedGeoPolygon **candidates =
467
0
        H3_MEMORY(malloc)(polygonCount * sizeof(LinkedGeoPolygon *));
468
0
    assert(candidates != NULL);
469
0
    const BBox **candidateBBoxes =
470
0
        H3_MEMORY(malloc)(polygonCount * sizeof(BBox *));
471
0
    assert(candidateBBoxes != NULL);
472
473
    // Find all polygons that contain the loop
474
0
    int candidateCount = 0;
475
0
    int index = 0;
476
0
    while (polygon) {
477
        // We are guaranteed not to overlap, so just test the first point
478
0
        if (pointInsideLinkedGeoLoop(polygon->first, &bboxes[index],
479
0
                                     &loop->first->vertex)) {
480
0
            candidates[candidateCount] = polygon;
481
0
            candidateBBoxes[candidateCount] = &bboxes[index];
482
0
            candidateCount++;
483
0
        }
484
0
        polygon = polygon->next;
485
0
        index++;
486
0
    }
487
488
    // The most deeply nested container is the immediate parent
489
0
    const LinkedGeoPolygon *parent =
490
0
        findDeepestContainer(candidates, candidateBBoxes, candidateCount);
491
492
    // Free allocated memory
493
0
    H3_MEMORY(free)(candidates);
494
0
    H3_MEMORY(free)(candidateBBoxes);
495
496
0
    return parent;
497
0
}
498
499
/**
500
 * Normalize a LinkedGeoPolygon in-place into a structure following GeoJSON
501
 * MultiPolygon rules: Each polygon must have exactly one outer loop, which
502
 * must be first in the list, followed by any holes. Holes in this algorithm
503
 * are identified by winding order (holes are clockwise).
504
 *
505
 * Input to this function is assumed to be a single polygon including all
506
 * loops to normalize. It's assumed that a valid arrangement is possible.
507
 *
508
 * @param root Root polygon including all loops
509
 * @return     0 on success, or an error code > 0 for invalid input
510
 */
511
0
H3Error normalizeMultiPolygon(LinkedGeoPolygon *root) {
512
    // We assume that the input is a single polygon with loops;
513
    // if it has multiple polygons, don't touch it
514
0
    if (root->next) {
515
0
        return E_FAILED;
516
0
    }
517
518
    // Count loops, exiting early if there's only one
519
0
    int loopCount = countLinkedLoops(root);
520
0
    if (loopCount <= 1) {
521
0
        return E_SUCCESS;
522
0
    }
523
524
0
    H3Error resultCode = E_SUCCESS;
525
0
    LinkedGeoPolygon *polygon = NULL;
526
0
    LinkedGeoLoop *next;
527
0
    int innerCount = 0;
528
0
    int outerCount = 0;
529
530
    // Create an array to hold all of the inner loops. Note that
531
    // this array will never be full, as there will always be fewer
532
    // inner loops than outer loops.
533
0
    LinkedGeoLoop **innerLoops =
534
0
        H3_MEMORY(malloc)(loopCount * sizeof(LinkedGeoLoop *));
535
0
    assert(innerLoops != NULL);
536
537
    // Create an array to hold the bounding boxes for the outer loops
538
0
    BBox *bboxes = H3_MEMORY(malloc)(loopCount * sizeof(BBox));
539
0
    assert(bboxes != NULL);
540
541
    // Get the first loop and unlink it from root
542
0
    LinkedGeoLoop *loop = root->first;
543
0
    *root = (LinkedGeoPolygon){0};
544
545
    // Iterate over all loops, moving inner loops into an array and
546
    // assigning outer loops to new polygons
547
0
    while (loop) {
548
0
        if (isClockwiseLinkedGeoLoop(loop)) {
549
0
            innerLoops[innerCount] = loop;
550
0
            innerCount++;
551
0
        } else {
552
0
            polygon = polygon == NULL ? root : addNewLinkedPolygon(polygon);
553
0
            addLinkedLoop(polygon, loop);
554
0
            bboxFromLinkedGeoLoop(loop, &bboxes[outerCount]);
555
0
            outerCount++;
556
0
        }
557
        // get the next loop and unlink it from this one
558
0
        next = loop->next;
559
0
        loop->next = NULL;
560
0
        loop = next;
561
0
    }
562
563
    // Find polygon for each inner loop and assign the hole to it
564
0
    for (int i = 0; i < innerCount; i++) {
565
0
        polygon = (LinkedGeoPolygon *)findPolygonForHole(innerLoops[i], root,
566
0
                                                         bboxes, outerCount);
567
0
        if (polygon) {
568
0
            addLinkedLoop(polygon, innerLoops[i]);
569
0
        } else {
570
            // If we can't find a polygon (possible with invalid input), then
571
            // we need to release the memory for the hole, because the loop has
572
            // been unlinked from the root and the caller will no longer have
573
            // a way to destroy it with destroyLinkedMultiPolygon.
574
0
            destroyLinkedGeoLoop(innerLoops[i]);
575
0
            H3_MEMORY(free)(innerLoops[i]);
576
0
            resultCode = E_FAILED;
577
0
        }
578
0
    }
579
580
    // Free allocated memory
581
0
    H3_MEMORY(free)(innerLoops);
582
0
    H3_MEMORY(free)(bboxes);
583
584
0
    return resultCode;
585
0
}
586
587
// Define macros used in polygon algos for LinkedGeoLoop
588
#define TYPE LinkedGeoLoop
589
0
#define INIT_ITERATION INIT_ITERATION_LINKED_LOOP
590
0
#define ITERATE ITERATE_LINKED_LOOP
591
0
#define IS_EMPTY IS_EMPTY_LINKED_LOOP
592
593
#include "polygonAlgos.h"
594
595
#undef TYPE
596
#undef IS_EMPTY
597
#undef INIT_ITERATION
598
#undef ITERATE