Coverage Report

Created: 2025-08-26 06:20

/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
}