]> git.proxmox.com Git - mirror_ovs.git/blame - lib/hindex.c
ovsdb-idl: Fix iteration over tracked rows with no actual data.
[mirror_ovs.git] / lib / hindex.c
CommitLineData
822b7f52
BP
1/*
2 * Copyright (c) 2013 Nicira, 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
17#include <config.h>
18#include "hindex.h"
19#include "coverage.h"
20
21static bool hindex_node_is_body(const struct hindex_node *);
22static bool hindex_node_is_head(const struct hindex_node *);
23static void hindex_resize(struct hindex *, size_t new_mask);
24static size_t hindex_calc_mask(size_t capacity);
25
26COVERAGE_DEFINE(hindex_pathological);
27COVERAGE_DEFINE(hindex_expand);
28COVERAGE_DEFINE(hindex_shrink);
29COVERAGE_DEFINE(hindex_reserve);
30
31/* Initializes 'hindex' as an empty hash index. */
32void
33hindex_init(struct hindex *hindex)
34{
35 hindex->buckets = &hindex->one;
36 hindex->one = NULL;
37 hindex->mask = 0;
38 hindex->n_unique = 0;
39}
40
41/* Frees memory reserved by 'hindex'. It is the client's responsibility to
42 * free the nodes themselves, if necessary. */
43void
44hindex_destroy(struct hindex *hindex)
45{
46 if (hindex && hindex->buckets != &hindex->one) {
47 free(hindex->buckets);
48 }
49}
50
51/* Removes all node from 'hindex', leaving it ready to accept more nodes. Does
52 * not free memory allocated for 'hindex'.
53 *
54 * This function is appropriate when 'hindex' will soon have about as many
55 * elements as it before. If 'hindex' will likely have fewer elements than
56 * before, use hindex_destroy() followed by hindex_clear() to save memory and
57 * iteration time. */
58void
59hindex_clear(struct hindex *hindex)
60{
61 if (hindex->n_unique > 0) {
62 hindex->n_unique = 0;
63 memset(hindex->buckets, 0,
64 (hindex->mask + 1) * sizeof *hindex->buckets);
65 }
66}
67
68/* Exchanges hash indexes 'a' and 'b'. */
69void
70hindex_swap(struct hindex *a, struct hindex *b)
71{
72 struct hindex tmp = *a;
73 *a = *b;
74 *b = tmp;
75 hindex_moved(a);
76 hindex_moved(b);
77}
78
79/* Adjusts 'hindex' to compensate for having moved position in memory (e.g. due
80 * to realloc()). */
81void
82hindex_moved(struct hindex *hindex)
83{
84 if (!hindex->mask) {
85 hindex->buckets = &hindex->one;
86 }
87}
88
89/* Expands 'hindex', if necessary, to optimize the performance of searches. */
90void
91hindex_expand(struct hindex *hindex)
92{
93 size_t new_mask = hindex_calc_mask(hindex->n_unique);
94 if (new_mask > hindex->mask) {
95 COVERAGE_INC(hindex_expand);
96 hindex_resize(hindex, new_mask);
97 }
98}
99
100/* Shrinks 'hindex', if necessary, to optimize the performance of iteration. */
101void
102hindex_shrink(struct hindex *hindex)
103{
104 size_t new_mask = hindex_calc_mask(hindex->n_unique);
105 if (new_mask < hindex->mask) {
106 COVERAGE_INC(hindex_shrink);
107 hindex_resize(hindex, new_mask);
108 }
109}
110
111/* Expands 'hindex', if necessary, to optimize the performance of searches when
112 * it has up to 'n' unique hashes. (But iteration will be slow in a hash index
113 * whose allocated capacity is much higher than its current number of
114 * nodes.) */
115void
116hindex_reserve(struct hindex *hindex, size_t n)
117{
118 size_t new_mask = hindex_calc_mask(n);
119 if (new_mask > hindex->mask) {
120 COVERAGE_INC(hindex_reserve);
121 hindex_resize(hindex, new_mask);
122 }
123}
124
125/* Inserts 'node', with the given 'hash', into 'hindex'. Never automatically
126 * expands 'hindex' (use hindex_insert() instead if you want that). */
127void
128hindex_insert_fast(struct hindex *hindex,
129 struct hindex_node *node, size_t hash)
130{
131 struct hindex_node *head = hindex_node_with_hash(hindex, hash);
132 if (head) {
133 /* 'head' is an existing head with hash == 'hash'.
134 * Insert 'node' as a body node just below 'head'. */
135 node->s = head->s;
136 node->d = head;
137 if (node->s) {
138 node->s->d = node;
139 }
140 head->s = node;
141 } else {
142 /* No existing node has hash 'hash'. Insert 'node' as a new head in
143 * its bucket. */
144 struct hindex_node **bucket = &hindex->buckets[hash & hindex->mask];
145 node->s = NULL;
146 node->d = *bucket;
147 *bucket = node;
148 hindex->n_unique++;
149 }
150 node->hash = hash;
151}
152
153/* Inserts 'node', with the given 'hash', into 'hindex', and expands 'hindex'
154 * if necessary to optimize search performance. */
155void
156hindex_insert(struct hindex *hindex, struct hindex_node *node, size_t hash)
157{
158 hindex_insert_fast(hindex, node, hash);
159 if (hindex->n_unique / 2 > hindex->mask) {
160 hindex_expand(hindex);
161 }
162}
163
164/* Removes 'node' from 'hindex'. Does not shrink the hash index; call
165 * hindex_shrink() directly if desired. */
166void
167hindex_remove(struct hindex *hindex, struct hindex_node *node)
168{
169 if (!hindex_node_is_head(node)) {
170 node->d->s = node->s;
171 if (node->s) {
172 node->s->d = node->d;
173 }
174 } else {
175 struct hindex_node **head;
176
177 for (head = &hindex->buckets[node->hash & hindex->mask];
178 (*head)->hash != node->hash;
179 head = &(*head)->d)
180 {
181 continue;
182 }
183
184 if (node->s) {
185 *head = node->s;
186 node->s->d = node->d;
187 } else {
188 *head = node->d;
189 hindex->n_unique--;
190 }
191 }
192}
193\f
194/* Helper functions. */
195
196/* Returns true if 'node', which must be inserted into an hindex, is a "body"
197 * node, that is, it is not reachable from a bucket by following zero or more
198 * 'd' pointers. Returns false otherwise. */
199static bool
200hindex_node_is_body(const struct hindex_node *node)
201{
202 return node->d && node->d->hash == node->hash;
203}
204
205/* Returns true if 'node', which must be inserted into an hindex, is a "head"
206 * node, that is, if it is reachable from a bucket by following zero or more
207 * 'd' pointers. Returns false if 'node' is a body node (and therefore one
208 * must follow at least one 's' pointer to reach it). */
209static bool
210hindex_node_is_head(const struct hindex_node *node)
211{
212 return !hindex_node_is_body(node);
213}
214
215/* Reallocates 'hindex''s array of buckets to use bitwise mask 'new_mask'. */
216static void
217hindex_resize(struct hindex *hindex, size_t new_mask)
218{
219 struct hindex tmp;
220 size_t i;
221
222 ovs_assert(is_pow2(new_mask + 1));
223 ovs_assert(new_mask != SIZE_MAX);
224
225 hindex_init(&tmp);
226 if (new_mask) {
227 tmp.buckets = xmalloc(sizeof *tmp.buckets * (new_mask + 1));
228 tmp.mask = new_mask;
229 for (i = 0; i <= tmp.mask; i++) {
230 tmp.buckets[i] = NULL;
231 }
232 }
233 for (i = 0; i <= hindex->mask; i++) {
234 struct hindex_node *node, *next;
235 int count;
236
237 count = 0;
238 for (node = hindex->buckets[i]; node; node = next) {
239 struct hindex_node **head = &tmp.buckets[node->hash & tmp.mask];
240
241 next = node->d;
242 node->d = *head;
243 *head = node;
244 count++;
245 }
246 if (count > 5) {
247 COVERAGE_INC(hindex_pathological);
248 }
249 }
250 tmp.n_unique = hindex->n_unique;
251 hindex_swap(hindex, &tmp);
252 hindex_destroy(&tmp);
253}
254
255/* Returns the bitwise mask to use in struct hindex to support 'capacity'
256 * hindex_nodes with unique hashes. */
257static size_t
258hindex_calc_mask(size_t capacity)
259{
260 size_t mask = capacity / 2;
261 mask |= mask >> 1;
262 mask |= mask >> 2;
263 mask |= mask >> 4;
264 mask |= mask >> 8;
265 mask |= mask >> 16;
266#if SIZE_MAX > UINT32_MAX
267 mask |= mask >> 32;
268#endif
269
270 /* If we need to dynamically allocate buckets we might as well allocate at
271 * least 4 of them. */
272 mask |= (mask & 1) << 1;
273
274 return mask;
275}
276
30cc7d29
Z
277/* Returns the head node in 'hindex' with the given 'hash'. 'hindex' must
278 * contain a head node with the given hash. */
279static struct hindex_node *
280hindex_head_node(const struct hindex *hindex, size_t hash)
281{
282 struct hindex_node *node = hindex->buckets[hash & hindex->mask];
283
284 while (node->hash != hash) {
285 node = node->d;
286 }
287 return node;
288}
289
822b7f52
BP
290static struct hindex_node *
291hindex_next__(const struct hindex *hindex, size_t start)
292{
293 size_t i;
294 for (i = start; i <= hindex->mask; i++) {
295 struct hindex_node *node = hindex->buckets[i];
296 if (node) {
297 return node;
298 }
299 }
300 return NULL;
301}
302
303/* Returns the first node in 'hindex', in arbitrary order, or a null pointer if
304 * 'hindex' is empty. */
305struct hindex_node *
306hindex_first(const struct hindex *hindex)
307{
308 return hindex_next__(hindex, 0);
309}
310
311/* Returns the next node in 'hindex' following 'node', in arbitrary order, or a
312 * null pointer if 'node' is the last node in 'hindex'.
313 *
314 * If the hash index has been reallocated since 'node' was visited, some nodes
30cc7d29 315 * may be skipped or visited twice. */
822b7f52
BP
316struct hindex_node *
317hindex_next(const struct hindex *hindex, const struct hindex_node *node)
318{
30cc7d29
Z
319 struct hindex_node *head;
320
321 /* If there's a node with the same hash, return it. */
322 if (node->s) {
323 return node->s;
324 }
325
326 /* If there's another node in the same bucket, return it. */
327 head = hindex_head_node(hindex, node->hash);
328 if (head->d) {
329 return head->d;
330 }
331
332 /* Return the first node in the next (or later) bucket. */
333 return hindex_next__(hindex, (node->hash & hindex->mask) + 1);
822b7f52 334}