Coverage Report

Created: 2025-08-29 06:26

/src/h3/src/h3lib/lib/vertexGraph.c
Line
Count
Source (jump to first uncovered line)
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 vertexGraph.c
17
 * @brief   Data structure for storing a graph of vertices
18
 */
19
20
#include "vertexGraph.h"
21
22
#include <assert.h>
23
#include <math.h>
24
#include <stdint.h>
25
#include <stdio.h>
26
#include <stdlib.h>
27
28
#include "alloc.h"
29
#include "latLng.h"
30
31
/**
32
 * Initialize a new VertexGraph
33
 * @param graph       Graph to initialize
34
 * @param  numBuckets Number of buckets to include in the graph
35
 * @param  res        Resolution of the hexagons whose vertices we're storing
36
 */
37
0
void initVertexGraph(VertexGraph *graph, int numBuckets, int res) {
38
0
    if (numBuckets > 0) {
39
0
        graph->buckets = H3_MEMORY(calloc)(numBuckets, sizeof(VertexNode *));
40
0
        assert(graph->buckets != NULL);
41
0
    } else {
42
0
        graph->buckets = NULL;
43
0
    }
44
0
    graph->numBuckets = numBuckets;
45
0
    graph->size = 0;
46
0
    graph->res = res;
47
0
}
48
49
/**
50
 * Destroy a VertexGraph's sub-objects, freeing their memory. The caller is
51
 * responsible for freeing memory allocated to the VertexGraph struct itself.
52
 * @param graph Graph to destroy
53
 */
54
0
void destroyVertexGraph(VertexGraph *graph) {
55
0
    VertexNode *node;
56
0
    while ((node = firstVertexNode(graph)) != NULL) {
57
0
        removeVertexNode(graph, node);
58
0
    }
59
0
    H3_MEMORY(free)(graph->buckets);
60
0
}
61
62
/**
63
 * Get an integer hash for a lat/lng point, at a precision determined
64
 * by the current hexagon resolution.
65
 * TODO: Light testing suggests this might not be sufficient at resolutions
66
 * finer than 10. Design a better hash function if performance and collisions
67
 * seem to be an issue here.
68
 * @param  vertex     Lat/lng vertex to hash
69
 * @param  res        Resolution of the hexagon the vertex belongs to
70
 * @param  numBuckets Number of buckets in the graph
71
 * @return            Integer hash
72
 */
73
0
uint32_t _hashVertex(const LatLng *vertex, int res, int numBuckets) {
74
    // Simple hash: Take the sum of the lat and lng with a precision level
75
    // determined by the resolution, converted to int, modulo bucket count.
76
0
    return (uint32_t)fmod(fabs((vertex->lat + vertex->lng) * pow(10, 15 - res)),
77
0
                          numBuckets);
78
0
}
79
80
void _initVertexNode(VertexNode *node, const LatLng *fromVtx,
81
0
                     const LatLng *toVtx) {
82
0
    node->from = *fromVtx;
83
0
    node->to = *toVtx;
84
0
    node->next = NULL;
85
0
}
86
87
/**
88
 * Add a edge to the graph
89
 * @param graph   Graph to add node to
90
 * @param fromVtx Start vertex
91
 * @param toVtx   End vertex
92
 * @return        Pointer to the new node
93
 */
94
VertexNode *addVertexNode(VertexGraph *graph, const LatLng *fromVtx,
95
0
                          const LatLng *toVtx) {
96
    // Make the new node
97
0
    VertexNode *node = H3_MEMORY(malloc)(sizeof(VertexNode));
98
0
    assert(node != NULL);
99
0
    _initVertexNode(node, fromVtx, toVtx);
100
    // Determine location
101
0
    uint32_t index = _hashVertex(fromVtx, graph->res, graph->numBuckets);
102
    // Check whether there's an existing node in that spot
103
0
    VertexNode *currentNode = graph->buckets[index];
104
0
    if (currentNode == NULL) {
105
        // Set bucket to the new node
106
0
        graph->buckets[index] = node;
107
0
    } else {
108
        // Find the end of the list
109
0
        do {
110
            // Check the the edge we're adding doesn't already exist
111
0
            if (geoAlmostEqual(&currentNode->from, fromVtx) &&
112
0
                geoAlmostEqual(&currentNode->to, toVtx)) {
113
                // already exists, bail
114
0
                H3_MEMORY(free)(node);
115
0
                return currentNode;
116
0
            }
117
0
            if (currentNode->next != NULL) {
118
0
                currentNode = currentNode->next;
119
0
            }
120
0
        } while (currentNode->next != NULL);
121
        // Add the new node to the end of the list
122
0
        currentNode->next = node;
123
0
    }
124
0
    graph->size++;
125
0
    return node;
126
0
}
127
128
/**
129
 * Remove a node from the graph. The input node will be freed, and should
130
 * not be used after removal.
131
 * @param graph Graph to mutate
132
 * @param node  Node to remove
133
 * @return      0 on success, 1 on failure (node not found)
134
 */
135
0
int removeVertexNode(VertexGraph *graph, VertexNode *node) {
136
    // Determine location
137
0
    uint32_t index = _hashVertex(&node->from, graph->res, graph->numBuckets);
138
0
    VertexNode *currentNode = graph->buckets[index];
139
0
    int found = 0;
140
0
    if (currentNode != NULL) {
141
0
        if (currentNode == node) {
142
0
            graph->buckets[index] = node->next;
143
0
            found = 1;
144
0
        }
145
        // Look through the list
146
0
        while (!found && currentNode->next != NULL) {
147
0
            if (currentNode->next == node) {
148
                // splice the node out
149
0
                currentNode->next = node->next;
150
0
                found = 1;
151
0
            }
152
0
            currentNode = currentNode->next;
153
0
        }
154
0
    }
155
0
    if (found) {
156
0
        H3_MEMORY(free)(node);
157
0
        graph->size--;
158
0
        return 0;
159
0
    }
160
    // Failed to find the node
161
0
    return 1;
162
0
}
163
164
/**
165
 * Find the Vertex node for a given edge, if it exists
166
 * @param  graph   Graph to look in
167
 * @param  fromVtx Start vertex
168
 * @param  toVtx   End vertex, or NULL if we don't care
169
 * @return         Pointer to the vertex node, if found
170
 */
171
VertexNode *findNodeForEdge(const VertexGraph *graph, const LatLng *fromVtx,
172
0
                            const LatLng *toVtx) {
173
    // Determine location
174
0
    uint32_t index = _hashVertex(fromVtx, graph->res, graph->numBuckets);
175
    // Check whether there's an existing node in that spot
176
0
    VertexNode *node = graph->buckets[index];
177
0
    if (node != NULL) {
178
        // Look through the list and see if we find the edge
179
0
        do {
180
0
            if (geoAlmostEqual(&node->from, fromVtx) &&
181
0
                (toVtx == NULL || geoAlmostEqual(&node->to, toVtx))) {
182
0
                return node;
183
0
            }
184
0
            node = node->next;
185
0
        } while (node != NULL);
186
0
    }
187
    // Iteration lookup fail
188
0
    return NULL;
189
0
}
190
191
/**
192
 * Find a Vertex node starting at the given vertex
193
 * @param  graph   Graph to look in
194
 * @param  fromVtx Start vertex
195
 * @return         Pointer to the vertex node, if found
196
 */
197
0
VertexNode *findNodeForVertex(const VertexGraph *graph, const LatLng *fromVtx) {
198
0
    return findNodeForEdge(graph, fromVtx, NULL);
199
0
}
200
201
/**
202
 * Get the next vertex node in the graph.
203
 * @param  graph Graph to iterate
204
 * @return       Vertex node, or NULL if at the end
205
 */
206
0
VertexNode *firstVertexNode(const VertexGraph *graph) {
207
0
    VertexNode *node = NULL;
208
0
    int currentIndex = 0;
209
0
    while (node == NULL) {
210
0
        if (currentIndex < graph->numBuckets) {
211
            // find the first node in the next bucket
212
0
            node = graph->buckets[currentIndex];
213
0
        } else {
214
            // end of iteration
215
0
            return NULL;
216
0
        }
217
0
        currentIndex++;
218
0
    }
219
0
    return node;
220
0
}