Coverage Report

Created: 2023-09-25 06:53

/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
703
void initVertexGraph(VertexGraph *graph, int numBuckets, int res) {
38
703
    if (numBuckets > 0) {
39
703
        graph->buckets = H3_MEMORY(calloc)(numBuckets, sizeof(VertexNode *));
40
703
        assert(graph->buckets != NULL);
41
703
    } else {
42
0
        graph->buckets = NULL;
43
0
    }
44
703
    graph->numBuckets = numBuckets;
45
703
    graph->size = 0;
46
703
    graph->res = res;
47
703
}
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
703
void destroyVertexGraph(VertexGraph *graph) {
55
703
    VertexNode *node;
56
29.3k
    while ((node = firstVertexNode(graph)) != NULL) {
57
28.6k
        removeVertexNode(graph, node);
58
28.6k
    }
59
703
    H3_MEMORY(free)(graph->buckets);
60
703
}
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
15.8M
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
15.8M
    return (uint32_t)fmod(fabs((vertex->lat + vertex->lng) * pow(10, 15 - res)),
77
15.8M
                          numBuckets);
78
15.8M
}
79
80
void _initVertexNode(VertexNode *node, const LatLng *fromVtx,
81
7.82M
                     const LatLng *toVtx) {
82
7.82M
    node->from = *fromVtx;
83
7.82M
    node->to = *toVtx;
84
7.82M
    node->next = NULL;
85
7.82M
}
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
7.82M
                          const LatLng *toVtx) {
96
    // Make the new node
97
7.82M
    VertexNode *node = H3_MEMORY(malloc)(sizeof(VertexNode));
98
7.82M
    assert(node != NULL);
99
7.82M
    _initVertexNode(node, fromVtx, toVtx);
100
    // Determine location
101
7.82M
    uint32_t index = _hashVertex(fromVtx, graph->res, graph->numBuckets);
102
    // Check whether there's an existing node in that spot
103
7.82M
    VertexNode *currentNode = graph->buckets[index];
104
7.82M
    if (currentNode == NULL) {
105
        // Set bucket to the new node
106
54.8k
        graph->buckets[index] = node;
107
7.77M
    } else {
108
        // Find the end of the list
109
9.12M
        do {
110
            // Check the the edge we're adding doesn't already exist
111
9.12M
            if (geoAlmostEqual(&currentNode->from, fromVtx) &&
112
9.12M
                geoAlmostEqual(&currentNode->to, toVtx)) {
113
                // already exists, bail
114
7.71M
                H3_MEMORY(free)(node);
115
7.71M
                return currentNode;
116
7.71M
            }
117
1.41M
            if (currentNode->next != NULL) {
118
1.39M
                currentNode = currentNode->next;
119
1.39M
            }
120
1.41M
        } while (currentNode->next != NULL);
121
        // Add the new node to the end of the list
122
59.7k
        currentNode->next = node;
123
59.7k
    }
124
114k
    graph->size++;
125
114k
    return node;
126
7.82M
}
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
114k
int removeVertexNode(VertexGraph *graph, VertexNode *node) {
136
    // Determine location
137
114k
    uint32_t index = _hashVertex(&node->from, graph->res, graph->numBuckets);
138
114k
    VertexNode *currentNode = graph->buckets[index];
139
114k
    int found = 0;
140
114k
    if (currentNode != NULL) {
141
114k
        if (currentNode == node) {
142
76.9k
            graph->buckets[index] = node->next;
143
76.9k
            found = 1;
144
76.9k
        }
145
        // Look through the list
146
201k
        while (!found && currentNode->next != NULL) {
147
87.0k
            if (currentNode->next == node) {
148
                // splice the node out
149
37.6k
                currentNode->next = node->next;
150
37.6k
                found = 1;
151
37.6k
            }
152
87.0k
            currentNode = currentNode->next;
153
87.0k
        }
154
114k
    }
155
114k
    if (found) {
156
114k
        H3_MEMORY(free)(node);
157
114k
        graph->size--;
158
114k
        return 0;
159
114k
    }
160
    // Failed to find the node
161
0
    return 1;
162
114k
}
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
7.91M
                            const LatLng *toVtx) {
173
    // Determine location
174
7.91M
    uint32_t index = _hashVertex(fromVtx, graph->res, graph->numBuckets);
175
    // Check whether there's an existing node in that spot
176
7.91M
    VertexNode *node = graph->buckets[index];
177
7.91M
    if (node != NULL) {
178
        // Look through the list and see if we find the edge
179
11.1M
        do {
180
11.1M
            if (geoAlmostEqual(&node->from, fromVtx) &&
181
11.1M
                (toVtx == NULL || geoAlmostEqual(&node->to, toVtx))) {
182
75.6k
                return node;
183
75.6k
            }
184
11.1M
            node = node->next;
185
11.1M
        } while (node != NULL);
186
7.85M
    }
187
    // Iteration lookup fail
188
7.83M
    return NULL;
189
7.91M
}
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
50.7k
VertexNode *findNodeForVertex(const VertexGraph *graph, const LatLng *fromVtx) {
198
50.7k
    return findNodeForEdge(graph, fromVtx, NULL);
199
50.7k
}
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
40.1k
VertexNode *firstVertexNode(const VertexGraph *graph) {
207
40.1k
    VertexNode *node = NULL;
208
40.1k
    int currentIndex = 0;
209
789M
    while (node == NULL) {
210
789M
        if (currentIndex < graph->numBuckets) {
211
            // find the first node in the next bucket
212
789M
            node = graph->buckets[currentIndex];
213
789M
        } else {
214
            // end of iteration
215
1.30k
            return NULL;
216
1.30k
        }
217
789M
        currentIndex++;
218
789M
    }
219
38.8k
    return node;
220
40.1k
}