/src/openvswitch/lib/skiplist.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2016 Hewlett Packard Enterprise Development LP |
2 | | * All Rights Reserved. |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); you may |
5 | | * not use this file except in compliance with the License. You may obtain |
6 | | * 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, WITHOUT |
12 | | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
13 | | * License for the specific language governing permissions and limitations |
14 | | * under the License. |
15 | | */ |
16 | | |
17 | | /* Skiplist implementation based on: |
18 | | * "Skip List: A Probabilistic Alternative to Balanced Trees", |
19 | | * by William Pugh. */ |
20 | | |
21 | | #include <config.h> |
22 | | #include <stdio.h> |
23 | | #include <stdbool.h> |
24 | | #include <stdint.h> |
25 | | #include <stdlib.h> |
26 | | #include <string.h> |
27 | | |
28 | | #include "skiplist.h" |
29 | | #include "random.h" |
30 | | #include "util.h" |
31 | | |
32 | | /* |
33 | | * A maximum height level of 32 should be more than sufficient for |
34 | | * anticipated use cases, delivering good expected performance with |
35 | | * up to 2**32 list nodes. Changes to this limit will also require |
36 | | * changes in skiplist_determine_level(). |
37 | | */ |
38 | 0 | #define SKIPLIST_MAX_LEVELS 32 |
39 | | |
40 | | /* Skiplist node container */ |
41 | | struct skiplist_node { |
42 | | const void *data; /* Pointer to saved data. */ |
43 | | struct skiplist_node *forward[]; /* Links to the next nodes. */ |
44 | | }; |
45 | | |
46 | | /* Skiplist container */ |
47 | | struct skiplist { |
48 | | struct skiplist_node *header; /* Pointer to head node (not first |
49 | | * data node). */ |
50 | | skiplist_comparator *cmp; /* Pointer to the skiplist's comparison |
51 | | * function. */ |
52 | | void *cfg; /* Pointer to optional comparison |
53 | | * configuration, used by the comparator. */ |
54 | | int level; /* Maximum level currently in use. */ |
55 | | uint32_t size; /* Current number of nodes in skiplist. */ |
56 | | }; |
57 | | |
58 | | /* Create a new skiplist_node with given level and data. */ |
59 | | static struct skiplist_node * |
60 | | skiplist_create_node(int level, const void *object) |
61 | 0 | { |
62 | 0 | struct skiplist_node *new_node; |
63 | 0 | size_t alloc_size = sizeof *new_node + |
64 | 0 | (level + 1) * sizeof new_node->forward[0]; |
65 | |
|
66 | 0 | new_node = xmalloc(alloc_size); |
67 | 0 | new_node->data = object; |
68 | 0 | memset(new_node->forward, 0, |
69 | 0 | (level + 1) * sizeof new_node->forward[0]); |
70 | |
|
71 | 0 | return new_node; |
72 | 0 | } |
73 | | |
74 | | /* |
75 | | * Create a new skiplist, configured with given data comparison function |
76 | | * and configuration. |
77 | | */ |
78 | | struct skiplist * |
79 | | skiplist_create(skiplist_comparator object_comparator, void *configuration) |
80 | 0 | { |
81 | 0 | random_init(); |
82 | 0 | struct skiplist *sl; |
83 | |
|
84 | 0 | sl = xmalloc(sizeof (struct skiplist)); |
85 | 0 | sl->cfg = configuration; |
86 | 0 | sl->size = 0; |
87 | 0 | sl->level = 0; |
88 | 0 | sl->cmp = object_comparator; |
89 | 0 | sl->header = skiplist_create_node(SKIPLIST_MAX_LEVELS, NULL); |
90 | |
|
91 | 0 | return sl; |
92 | 0 | } |
93 | | |
94 | | /* |
95 | | * Move the cursor forward to the first node with associated data greater than |
96 | | * or equal to "value". |
97 | | */ |
98 | | static struct skiplist_node * |
99 | | skiplist_forward_to_(struct skiplist *sl, const void *value, |
100 | | struct skiplist_node **update) |
101 | 0 | { |
102 | 0 | struct skiplist_node *x = sl->header; |
103 | 0 | int i; |
104 | | |
105 | | /* Loop invariant: x < value */ |
106 | 0 | for (i = sl->level; i >= 0; i--) { |
107 | 0 | while (x->forward[i] && |
108 | 0 | sl->cmp(x->forward[i]->data, value, sl->cfg) < 0) { |
109 | 0 | x = x->forward[i]; |
110 | 0 | } |
111 | | /* x < value <= x->forward[1] */ |
112 | 0 | if (update) { |
113 | 0 | update[i] = x; |
114 | 0 | } |
115 | 0 | } |
116 | | /* x < value <= x->forward[1] */ |
117 | 0 | x = x->forward[0]; |
118 | 0 | return x; |
119 | 0 | } |
120 | | |
121 | | struct skiplist_node * |
122 | | skiplist_forward_to(struct skiplist *sl, const void *value) |
123 | 0 | { |
124 | 0 | return skiplist_forward_to_(sl, value, NULL); |
125 | 0 | } |
126 | | |
127 | | /* Find the first exact match of value in the skiplist. */ |
128 | | struct skiplist_node * |
129 | | skiplist_find(struct skiplist *sl, const void *value) |
130 | 0 | { |
131 | 0 | struct skiplist_node *x = skiplist_forward_to(sl, value); |
132 | |
|
133 | 0 | return x && sl->cmp(x->data, value, sl->cfg) == 0 ? x : NULL; |
134 | 0 | } |
135 | | |
136 | | /* |
137 | | * Determine the level for a skiplist node by choosing a level N with |
138 | | * probability P(N) = 1/(2**(N+1)) in the range 0..32, with the returned |
139 | | * level clamped at the current skiplist height plus 1. |
140 | | */ |
141 | | static int |
142 | | skiplist_determine_level(struct skiplist *sl) |
143 | 0 | { |
144 | 0 | int lvl; |
145 | |
|
146 | 0 | lvl = clz32(random_uint32()); |
147 | |
|
148 | 0 | return MIN(lvl, sl->level + 1); |
149 | 0 | } |
150 | | |
151 | | /* Insert data into a skiplist. */ |
152 | | void |
153 | | skiplist_insert(struct skiplist *list, const void *value) |
154 | 0 | { |
155 | 0 | struct skiplist_node *update[SKIPLIST_MAX_LEVELS + 1]; |
156 | 0 | struct skiplist_node *x = skiplist_forward_to_(list, value, update); |
157 | 0 | int i, lvl; |
158 | |
|
159 | 0 | if (x && list->cmp(x->data, value, list->cfg) == 0) { |
160 | 0 | x->data = value; |
161 | 0 | } else { |
162 | 0 | lvl = skiplist_determine_level(list); |
163 | 0 | if (lvl > list->level) { |
164 | 0 | for (i = list->level + 1; i <= lvl; i++) { |
165 | 0 | update[i] = list->header; |
166 | 0 | } |
167 | 0 | list->level = lvl; |
168 | 0 | } |
169 | 0 | x = skiplist_create_node(lvl, value); |
170 | 0 | for (i = 0; i <= lvl; i++) { |
171 | 0 | x->forward[i] = update[i]->forward[i]; |
172 | 0 | update[i]->forward[i] = x; |
173 | 0 | } |
174 | 0 | list->size++; |
175 | 0 | } |
176 | 0 | } |
177 | | |
178 | | /* Remove first node with associated data equal to "value" from skiplist. */ |
179 | | void * |
180 | | skiplist_delete(struct skiplist *list, const void *value) |
181 | 0 | { |
182 | 0 | struct skiplist_node *update[SKIPLIST_MAX_LEVELS + 1]; |
183 | 0 | struct skiplist_node *x; |
184 | 0 | void *data = NULL; |
185 | 0 | int i; |
186 | |
|
187 | 0 | x = skiplist_forward_to_(list, value, update); |
188 | |
|
189 | 0 | if (x && list->cmp(x->data, value, list->cfg) == 0) { |
190 | 0 | for (i = 0; i <= list->level; i++) { |
191 | 0 | if (update[i]->forward[i] != x) { |
192 | 0 | break; |
193 | 0 | } |
194 | 0 | update[i]->forward[i] = x->forward[i]; |
195 | 0 | } |
196 | 0 | data = CONST_CAST(void *, x->data); |
197 | |
|
198 | 0 | free(x); |
199 | |
|
200 | 0 | while (list->level > 0 && !list->header->forward[list->level]) { |
201 | 0 | list->level--; |
202 | 0 | } |
203 | 0 | list->size--; |
204 | 0 | } |
205 | 0 | return data; |
206 | 0 | } |
207 | | |
208 | | /* Get the associated data value stored in a skiplist node. */ |
209 | | void * |
210 | | skiplist_get_data(struct skiplist_node *node) |
211 | 0 | { |
212 | 0 | return node ? CONST_CAST(void *, node->data) : NULL; |
213 | 0 | } |
214 | | |
215 | | /* Get the number of items in a skiplist. */ |
216 | | uint32_t |
217 | | skiplist_get_size(struct skiplist *sl) |
218 | 0 | { |
219 | 0 | return sl->size; |
220 | 0 | } |
221 | | |
222 | | /* Get the first node in a skiplist. */ |
223 | | struct skiplist_node * |
224 | | skiplist_first(struct skiplist *sl) |
225 | 0 | { |
226 | 0 | return sl->header->forward[0]; |
227 | 0 | } |
228 | | |
229 | | /* Get a node's successor in a skiplist. */ |
230 | | struct skiplist_node * |
231 | | skiplist_next(struct skiplist_node *node) |
232 | 0 | { |
233 | 0 | return node ? node->forward[0] : NULL; |
234 | 0 | } |
235 | | |
236 | | /* |
237 | | * Destroy a skiplist and free all nodes in the list. If the "data_destroy" |
238 | | * function pointer is non-NULL, it will be called for each node as it is |
239 | | * removed to allow any needed cleanups to be performed on the associated |
240 | | * data. |
241 | | */ |
242 | | void |
243 | | skiplist_destroy(struct skiplist *sl, void (*data_destroy)(void *)) |
244 | 0 | { |
245 | 0 | struct skiplist_node *node, *next; |
246 | |
|
247 | 0 | next = node = sl->header; |
248 | 0 | while (next != NULL) { |
249 | 0 | next = node->forward[0]; |
250 | 0 | if (data_destroy) { |
251 | 0 | data_destroy(CONST_CAST(void *, node->data)); |
252 | 0 | } |
253 | 0 | free(node); |
254 | 0 | node = next; |
255 | 0 | } |
256 | 0 | free(sl); |
257 | 0 | } |