aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/minizip/skipset.h187
1 files changed, 97 insertions, 90 deletions
diff --git a/contrib/minizip/skipset.h b/contrib/minizip/skipset.h
index 34e24438..ad09dd09 100644
--- a/contrib/minizip/skipset.h
+++ b/contrib/minizip/skipset.h
@@ -1,56 +1,59 @@
1/* skipset.h -- set operations using a skiplist 1/* skipset.h -- set operations using a skiplist
2// Copyright (C) 2024-2026 Mark Adler 2 Copyright (C) 2024-2026 Mark Adler
3// See MiniZip_info.txt for the license. 3 See MiniZip_info.txt for the license.
4 4 */
5// This implements a skiplist set, i.e. just keys, no data, with ~O(log n) time 5
6// insert and search operations. The application defines the type of a key, and 6/*
7// provides a function to compare two keys. 7 This implements a skiplist set, i.e. just keys, no data, with ~O(log n) time
8 8 insert and search operations. The application defines the type of a key, and
9// This header is not definitions of functions found in another source file -- 9 provides a function to compare two keys.
10// it creates the set functions, with the application's key type, right where 10
11// the #include is. Before this header is #included, these must be defined: 11 This header is not definitions of functions found in another source file --
12// 12 it creates the set functions, with the application's key type, right where
13// 1. A macro or typedef for set_key_t, the type of a key. 13 the #include is. Before this header is #included, these must be defined:
14// 2. A macro or function set_cmp(a, b) to compare two keys. The return values 14
15// are < 0 for a < b, 0 for a == b, and > 0 for a > b. 15 1. A macro or typedef for set_key_t, the type of a key.
16// 3. A macro or function set_drop(s, k) to release the key k's resources, if 16 2. A macro or function set_cmp(a, b) to compare two keys. The return values
17// any, when doing a set_end() or set_clear(). s is a pointer to the set 17 are < 0 for a < b, 0 for a == b, and > 0 for a > b.
18// that key is in, for use with set_free() if desired. 18 3. A macro or function set_drop(s, k) to release the key k's resources, if
19// 19 any, when doing a set_end() or set_clear(). s is a pointer to the set
20// Example usage: 20 that key is in, for use with set_free() if desired.
21// 21
22// typedef int set_key_t; 22 Example usage:
23// #define set_cmp(a, b) ((a) < (b) ? -1 : (a) == (b) ? 0 : 1) 23
24// #define set_drop(s, k) 24 typedef int set_key_t;
25// #include "skipset.h" 25 #define set_cmp(a, b) ((a) < (b) ? -1 : (a) == (b) ? 0 : 1)
26// 26 #define set_drop(s, k)
27// int test(void) { // return 0: good, 1: bad, -1: out of memory 27 #include "skipset.h"
28// set_t set; 28
29// if (setjmp(set.env)) 29 int test(void) { // return 0: good, 1: bad, -1: out of memory
30// return -1; 30 set_t set;
31// set_start(&set); 31 if (setjmp(set.env))
32// set_insert(&set, 2); 32 return -1;
33// set_insert(&set, 1); 33 set_start(&set);
34// set_insert(&set, 7); 34 set_insert(&set, 2);
35// int bad = !set_found(&set, 2); 35 set_insert(&set, 1);
36// bad = bad || set_found(&set, 5); 36 set_insert(&set, 7);
37// set_end(&set); 37 int bad = !set_found(&set, 2);
38// return bad; 38 bad = bad || set_found(&set, 5);
39// } 39 set_end(&set);
40// 40 return bad;
41// Interface summary (see more details below): 41 }
42// - set_t is the type of the set being operated on (a set_t pointer is passed) 42
43// - set_start() initializes a new, empty set (initialize set.env first) 43 Interface summary (see more details below):
44// - set_insert() inserts a new key into the set, or not if it's already there 44 - set_t is the type of the set being operated on (a set_t pointer is passed)
45// - set_found() determines whether or not a key is in the set 45 - set_start() initializes a new, empty set (initialize set.env first)
46// - set_end() ends the use of the set, freeing all memory 46 - set_insert() inserts a new key into the set, or not if it's already there
47// - set_clear() empties the set, equivalent to set_end() and then set_start() 47 - set_found() determines whether or not a key is in the set
48// - set_ok() checks if set appears to be usable, i.e. started and not ended 48 - set_end() ends the use of the set, freeing all memory
49// 49 - set_clear() empties the set, equivalent to set_end() and then set_start()
50// Auxiliary functions available to the application: 50 - set_ok() checks if set appears to be usable, i.e. started and not ended
51// - set_alloc() allocates memory with optional tracking (#define SET_TRACK) 51
52// - set_free() deallocates memory allocated by set_alloc() 52 Auxiliary functions available to the application:
53// - set_rand() returns 32 random bits (seeded by set_start()) */ 53 - set_alloc() allocates memory with optional tracking (#define SET_TRACK)
54 - set_free() deallocates memory allocated by set_alloc()
55 - set_rand() returns 32 random bits (seeded by set_start())
56 */
54 57
55#ifndef SKIPSET_H 58#ifndef SKIPSET_H
56#define SKIPSET_H 59#define SKIPSET_H
@@ -63,23 +66,27 @@
63#include <assert.h> /* assert.h */ 66#include <assert.h> /* assert.h */
64#include "ints.h" /* i16_t, ui32_t, ui64_t */ 67#include "ints.h" /* i16_t, ui32_t, ui64_t */
65 68
66/* Structures and functions below noted as "--private--" should not be used by 69/*
67// the application. set_t is partially private and partially public -- see the 70 Structures and functions below noted as "--private--" should not be used by
68// comments there. 71 the application. set_t is partially private and partially public -- see the
72 comments there.
69 73
70// There is no POSIX random() in MSVC, and rand() is awful. For portability, we 74 There is no POSIX random() in MSVC, and rand() is awful. For portability, we
71// cannot rely on a library function for random numbers. Instead we use the 75 cannot rely on a library function for random numbers. Instead we use the
72// fast and effective algorithm below, invented by Melissa O'Neill. 76 fast and effective algorithm below, invented by Melissa O'Neill.
77 */
73 78
74// *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / www.pcg-random.org 79/*
75// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website) 80 *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / www.pcg-random.org
76// --private-- Random number generator state. */ 81 Licensed under Apache License 2.0 (NO WARRANTY, etc. see website)
82 --private-- Random number generator state.
83 */
77typedef struct { 84typedef struct {
78 ui64_t state; /* 64-bit generator state */ 85 ui64_t state; /* 64-bit generator state */
79 ui64_t inc; /* 63-bit sequence id */ 86 ui64_t inc; /* 63-bit sequence id */
80} set_rand_t; 87} set_rand_t;
81/* --private-- Initialize the state *gen using seed and seq. seed seeds the 88/* --private-- Initialize the state *gen using seed and seq. seed seeds the
82// advancing 64-bit state. seq is a sequence selection constant. */ 89 advancing 64-bit state. seq is a sequence selection constant. */
83static void set_seed(set_rand_t *gen, ui64_t seed, ui64_t seq) { 90static void set_seed(set_rand_t *gen, ui64_t seed, ui64_t seq) {
84 gen->inc = (seq << 1) | 1; 91 gen->inc = (seq << 1) | 1;
85 gen->state = (seed + gen->inc) * 6364136223846793005ULL + gen->inc; 92 gen->state = (seed + gen->inc) * 6364136223846793005ULL + gen->inc;
@@ -111,7 +118,7 @@ struct set_node_s {
111}; 118};
112 119
113/* A set. The application sets env, may use gen with set_rand(), and may read 120/* A set. The application sets env, may use gen with set_rand(), and may read
114// allocs and memory. The remaining variables are --private-- . */ 121 allocs and memory. The remaining variables are --private-- . */
115typedef struct set_s { 122typedef struct set_s {
116 set_node_t *head; /* skiplist head -- no key, just links */ 123 set_node_t *head; /* skiplist head -- no key, just links */
117 set_node_t *path; /* right[] is path to key from set_found() */ 124 set_node_t *path; /* right[] is path to key from set_found() */
@@ -122,15 +129,15 @@ typedef struct set_s {
122 jmp_buf env; /* setjmp() environment for allocation errors */ 129 jmp_buf env; /* setjmp() environment for allocation errors */
123#ifdef SET_TRACK 130#ifdef SET_TRACK
124 size_t allocs; /* number of allocations */ 131 size_t allocs; /* number of allocations */
125 size_t memory; /* total amount of allocated memory (>= requests) */ 132 size_t memory; /* total size of allocated memory (>= requests) */
126#endif 133#endif
127} set_t; 134} set_t;
128 135
129/* Memory allocation and deallocation. set_alloc(set, ptr, size) returns a 136/* Memory allocation and deallocation. set_alloc(set, ptr, size) returns a
130// pointer to an allocation of size bytes if ptr is NULL, or the previous 137 pointer to an allocation of size bytes if ptr is NULL, or the previous
131// allocation ptr resized to size bytes. set_alloc() will never return NULL. 138 allocation ptr resized to size bytes. set_alloc() will never return NULL.
132// set_free(set, ptr) frees an allocation created by set_alloc(). These may be 139 set_free(set, ptr) frees an allocation created by set_alloc(). These may be
133// used by the application. e.g. if allocation tracking is desired. */ 140 used by the application. e.g. if allocation tracking is desired. */
134#ifdef SET_TRACK 141#ifdef SET_TRACK
135/* Track the number of allocations and the total backing memory size. */ 142/* Track the number of allocations and the total backing memory size. */
136# if defined(_WIN32) 143# if defined(_WIN32)
@@ -148,10 +155,10 @@ typedef struct set_s {
148# elif defined(__NetBSD__) 155# elif defined(__NetBSD__)
149# include <jemalloc/jemalloc.h> 156# include <jemalloc/jemalloc.h>
150# define SET_ALLOC_SIZE(ptr) malloc_usable_size(ptr) 157# define SET_ALLOC_SIZE(ptr) malloc_usable_size(ptr)
151# else // e.g. OpenBSD 158# else /* e.g. OpenBSD */
152# define SET_ALLOC_SIZE(ptr) 0 159# define SET_ALLOC_SIZE(ptr) 0
153# endif 160# endif
154// With tracking. 161/* With tracking. */
155static void *set_alloc(set_t *set, void *ptr, size_t size) { 162static void *set_alloc(set_t *set, void *ptr, size_t size) {
156 size_t had = ptr == NULL ? 0 : SET_ALLOC_SIZE(ptr); 163 size_t had = ptr == NULL ? 0 : SET_ALLOC_SIZE(ptr);
157 void *mem = realloc(ptr, size); 164 void *mem = realloc(ptr, size);
@@ -183,9 +190,9 @@ static void set_free(set_t *set, void *ptr) {
183#endif 190#endif
184 191
185/* --private-- Grow node's array right[] as needed to be able to hold at least 192/* --private-- Grow node's array right[] as needed to be able to hold at least
186// want links. If fill is true, assure that the first want links are filled in, 193 want links. If fill is true, assure that the first want links are filled in,
187// setting them to set->head if not previously filled in. Otherwise it is 194 setting them to set->head if not previously filled in. Otherwise it is
188// assumed that the first want links are about to be filled in. */ 195 assumed that the first want links are about to be filled in. */
189static void set_grow(set_t *set, set_node_t *node, int want, int fill) { 196static void set_grow(set_t *set, set_node_t *node, int want, int fill) {
190 int i; 197 int i;
191 if (node->size < want) { 198 if (node->size < want) {
@@ -224,11 +231,11 @@ static void set_sweep(set_t *set) {
224} 231}
225 232
226/* Initialize a new set. set->env must be initialized using setjmp() before 233/* Initialize a new set. set->env must be initialized using setjmp() before
227// set_start() is called. A longjmp(set->env, ENOMEM) will be used to handle a 234 set_start() is called. A longjmp(set->env, ENOMEM) will be used to handle a
228// memory allocation failure during any of the operations. (See setjmp.h and 235 memory allocation failure during any of the operations. (See setjmp.h and
229// errno.h.) The set can still be used if this happens, assuming that it didn't 236 errno.h.) The set can still be used if this happens, assuming that it didn't
230// happen during set_start(). Whether set_start() completed or not, set_end() 237 happen during set_start(). Whether set_start() completed or not, set_end()
231// can be used to free the set's memory after a longjmp(). */ 238 can be used to free the set's memory after a longjmp(). */
232SKIPSET_EXPORT void set_start(set_t *set) { 239SKIPSET_EXPORT void set_start(set_t *set) {
233#ifdef SET_TRACK 240#ifdef SET_TRACK
234 set->allocs = 0; 241 set->allocs = 0;
@@ -237,7 +244,7 @@ SKIPSET_EXPORT void set_start(set_t *set) {
237 set->head = set->path = set->node = NULL; /* in case set_node() fails */ 244 set->head = set->path = set->node = NULL; /* in case set_node() fails */
238 set->path = set_node(set); 245 set->path = set_node(set);
239 set->head = set_node(set); 246 set->head = set_node(set);
240 set_grow(set, set->head, 1, 1); /* one link back to head for an empty set */ 247 set_grow(set, set->head, 1, 1); /* one link back to head for empty set */
241 *(unsigned char *)&set->head->key = 137; /* set id */ 248 *(unsigned char *)&set->head->key = 137; /* set id */
242 set->depth = 0; 249 set->depth = 0;
243 set_uniq(&set->gen, set); 250 set_uniq(&set->gen, set);
@@ -245,7 +252,7 @@ SKIPSET_EXPORT void set_start(set_t *set) {
245} 252}
246 253
247/* Return true if *set appears to be in a usable state. If *set has been zeroed 254/* Return true if *set appears to be in a usable state. If *set has been zeroed
248// out, then set_ok(set) will be false and set_end(set) will be safe. */ 255 out, then set_ok(set) will be false and set_end(set) will be safe. */
249SKIPSET_EXPORT int set_ok(set_t *set) { 256SKIPSET_EXPORT int set_ok(set_t *set) {
250 return set->head != NULL && 257 return set->head != NULL &&
251 set->head->right != NULL && 258 set->head->right != NULL &&
@@ -254,7 +261,7 @@ SKIPSET_EXPORT int set_ok(set_t *set) {
254 261
255#if 0 /* not used in minizip */ 262#if 0 /* not used in minizip */
256/* Empty the set. This frees the memory used for the previous set contents. 263/* Empty the set. This frees the memory used for the previous set contents.
257// After set_clear(), *set is ready for use, as if after a set_start(). */ 264 After set_clear(), *set is ready for use, as if after a set_start(). */
258SKIPSET_EXPORT void set_clear(set_t *set) { 265SKIPSET_EXPORT void set_clear(set_t *set) {
259 assert(set_ok(set) && "improper use"); 266 assert(set_ok(set) && "improper use");
260 267
@@ -262,7 +269,7 @@ SKIPSET_EXPORT void set_clear(set_t *set) {
262 set_sweep(set); 269 set_sweep(set);
263 270
264 /* Leave the head and path allocations as is. Clear their contents, with 271 /* Leave the head and path allocations as is. Clear their contents, with
265 // head pointing to itself and setting depth to zero, for an empty set. */ 272 head pointing to itself and setting depth to zero, for an empty set. */
266 set->head->right[0] = set->head; 273 set->head->right[0] = set->head;
267 set->head->fill = 1; 274 set->head->fill = 1;
268 set->path->fill = 0; 275 set->path->fill = 0;
@@ -271,9 +278,9 @@ SKIPSET_EXPORT void set_clear(set_t *set) {
271#endif 278#endif
272 279
273/* Done using the set -- free all allocations. The only operation on *set 280/* Done using the set -- free all allocations. The only operation on *set
274// permitted after this is set_start(). Though another set_end() would do no 281 permitted after this is set_start(). Though another set_end() would do no
275// harm. This can be done at any time after a set_start(), or after a longjmp() 282 harm. This can be done at any time after a set_start(), or after a longjmp()
276// on any allocation failure, including during a set_start(). */ 283 on any allocation failure, including during a set_start(). */
277SKIPSET_EXPORT void set_end(set_t *set) { 284SKIPSET_EXPORT void set_end(set_t *set) {
278 if (set->head != NULL) { 285 if (set->head != NULL) {
279 /* Empty the set and free the head node. */ 286 /* Empty the set and free the head node. */
@@ -300,13 +307,13 @@ SKIPSET_EXPORT void set_end(set_t *set) {
300} 307}
301 308
302/* Look for key. Return 1 if found or 0 if not. This also puts the path to get 309/* Look for key. Return 1 if found or 0 if not. This also puts the path to get
303// there in set->path, for use by set_insert(). */ 310 there in set->path, for use by set_insert(). */
304SKIPSET_EXPORT int set_found(set_t *set, set_key_t key) { 311SKIPSET_EXPORT int set_found(set_t *set, set_key_t key) {
305 set_node_t *head, *here; 312 set_node_t *head, *here;
306 int i; 313 int i;
307 assert(set_ok(set) && "improper use"); 314 assert(set_ok(set) && "improper use");
308 315
309 /* Start at depth and work down and right as determined by key comparisons. */ 316 /* Start at depth. Work down and right as determined by key comparisons. */
310 head = set->head; 317 head = set->head;
311 here = head; 318 here = head;
312 i = set->depth; 319 i = set->depth;
@@ -323,7 +330,7 @@ SKIPSET_EXPORT int set_found(set_t *set, set_key_t key) {
323 return here != head && set_cmp(here->key, key) == 0; 330 return here != head && set_cmp(here->key, key) == 0;
324} 331}
325 332
326/* Insert the key key. Return 0 on success, or 1 if key is already in the set. */ 333/* Insert the key key. Return 0 on success, or 1 if it's already in the set. */
327SKIPSET_EXPORT int set_insert(set_t *set, set_key_t key) { 334SKIPSET_EXPORT int set_insert(set_t *set, set_key_t key) {
328 int level = 0; 335 int level = 0;
329 int bit; 336 int bit;
@@ -335,7 +342,7 @@ SKIPSET_EXPORT int set_insert(set_t *set, set_key_t key) {
335 return 1; 342 return 1;
336 343
337 /* Randomly generate a new level-- level 0 with probability 1/2, 1 with 344 /* Randomly generate a new level-- level 0 with probability 1/2, 1 with
338 // probability 1/4, 2 with probability 1/8, etc. */ 345 probability 1/4, 2 with probability 1/8, etc. */
339 for (;;) { 346 for (;;) {
340 if (set->ran == 1) 347 if (set->ran == 1)
341 /* Ran out. Get another 32 random bits. */ 348 /* Ran out. Get another 32 random bits. */
@@ -356,7 +363,7 @@ SKIPSET_EXPORT int set_insert(set_t *set, set_key_t key) {
356 } 363 }
357 364
358 /* Make a new node for the provided key, and insert it in the lists up to 365 /* Make a new node for the provided key, and insert it in the lists up to
359 // and including level. */ 366 and including level. */
360 set->node = set_node(set); 367 set->node = set_node(set);
361 set->node->key = key; 368 set->node->key = key;
362 set_grow(set, set->node, level + 1, 0); 369 set_grow(set, set->node, level + 1, 0);