]> git.proxmox.com Git - mirror_ovs.git/blob - lib/hmap.c
New functions hmap_moved(), shash_moved().
[mirror_ovs.git] / lib / hmap.c
1 /*
2 * Copyright (c) 2008, 2009, 2010 Nicira Networks.
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 "hmap.h"
19 #include <assert.h>
20 #include <stdint.h>
21 #include "coverage.h"
22 #include "util.h"
23
24 /* Initializes 'hmap' as an empty hash table. */
25 void
26 hmap_init(struct hmap *hmap)
27 {
28 hmap->buckets = &hmap->one;
29 hmap->one = NULL;
30 hmap->mask = 0;
31 hmap->n = 0;
32 }
33
34 /* Frees memory reserved by 'hmap'. It is the client's responsibility to free
35 * the nodes themselves, if necessary. */
36 void
37 hmap_destroy(struct hmap *hmap)
38 {
39 if (hmap && hmap->buckets != &hmap->one) {
40 free(hmap->buckets);
41 }
42 }
43
44 /* Exchanges hash maps 'a' and 'b'. */
45 void
46 hmap_swap(struct hmap *a, struct hmap *b)
47 {
48 struct hmap tmp = *a;
49 *a = *b;
50 *b = tmp;
51 hmap_moved(a);
52 hmap_moved(b);
53 }
54
55 /* Adjusts 'hmap' to compensate for having moved position in memory (e.g. due
56 * to realloc()). */
57 void
58 hmap_moved(struct hmap *hmap)
59 {
60 if (!hmap->mask) {
61 hmap->buckets = &hmap->one;
62 }
63 }
64
65 static void
66 resize(struct hmap *hmap, size_t new_mask)
67 {
68 struct hmap tmp;
69 size_t i;
70
71 assert(!(new_mask & (new_mask + 1)));
72 assert(new_mask != SIZE_MAX);
73
74 hmap_init(&tmp);
75 if (new_mask) {
76 tmp.buckets = xmalloc(sizeof *tmp.buckets * (new_mask + 1));
77 tmp.mask = new_mask;
78 for (i = 0; i <= tmp.mask; i++) {
79 tmp.buckets[i] = NULL;
80 }
81 }
82 for (i = 0; i <= hmap->mask; i++) {
83 struct hmap_node *node, *next;
84 int count = 0;
85 for (node = hmap->buckets[i]; node; node = next) {
86 next = node->next;
87 hmap_insert_fast(&tmp, node, node->hash);
88 count++;
89 }
90 if (count > 5) {
91 COVERAGE_INC(hmap_pathological);
92 }
93 }
94 hmap_swap(hmap, &tmp);
95 hmap_destroy(&tmp);
96 }
97
98 static size_t
99 calc_mask(size_t capacity)
100 {
101 size_t mask = capacity / 2;
102 mask |= mask >> 1;
103 mask |= mask >> 2;
104 mask |= mask >> 4;
105 mask |= mask >> 8;
106 mask |= mask >> 16;
107 #if SIZE_MAX > UINT32_MAX
108 mask |= mask >> 32;
109 #endif
110
111 /* If we need to dynamically allocate buckets we might as well allocate at
112 * least 4 of them. */
113 mask |= (mask & 1) << 1;
114
115 return mask;
116 }
117
118 /* Expands 'hmap', if necessary, to optimize the performance of searches. */
119 void
120 hmap_expand(struct hmap *hmap)
121 {
122 size_t new_mask = calc_mask(hmap->n);
123 if (new_mask > hmap->mask) {
124 COVERAGE_INC(hmap_expand);
125 resize(hmap, new_mask);
126 }
127 }
128
129 /* Shrinks 'hmap', if necessary, to optimize the performance of iteration. */
130 void
131 hmap_shrink(struct hmap *hmap)
132 {
133 size_t new_mask = calc_mask(hmap->n);
134 if (new_mask < hmap->mask) {
135 COVERAGE_INC(hmap_shrink);
136 resize(hmap, new_mask);
137 }
138 }
139
140 /* Expands 'hmap', if necessary, to optimize the performance of searches when
141 * it has up to 'n' elements. (But iteration will be slow in a hash map whose
142 * allocated capacity is much higher than its current number of nodes.) */
143 void
144 hmap_reserve(struct hmap *hmap, size_t n)
145 {
146 size_t new_mask = calc_mask(n);
147 if (new_mask > hmap->mask) {
148 COVERAGE_INC(hmap_reserve);
149 resize(hmap, new_mask);
150 }
151 }
152
153 /* Adjusts 'hmap' to compensate for 'old_node' having moved position in memory
154 * to 'node' (e.g. due to realloc()). */
155 void
156 hmap_node_moved(struct hmap *hmap,
157 struct hmap_node *old_node, struct hmap_node *node)
158 {
159 struct hmap_node **bucket = &hmap->buckets[node->hash & hmap->mask];
160 while (*bucket != old_node) {
161 bucket = &(*bucket)->next;
162 }
163 *bucket = node;
164 }
165