Merge "master" into "next".
[cascardo/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