diff options
| -rw-r--r-- | contrib/minizip/skipset.h | 187 |
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 | */ | ||
| 77 | typedef struct { | 84 | typedef 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. */ |
| 83 | static void set_seed(set_rand_t *gen, ui64_t seed, ui64_t seq) { | 90 | static 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-- . */ |
| 115 | typedef struct set_s { | 122 | typedef 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. */ |
| 155 | static void *set_alloc(set_t *set, void *ptr, size_t size) { | 162 | static 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. */ |
| 189 | static void set_grow(set_t *set, set_node_t *node, int want, int fill) { | 196 | static 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(). */ |
| 232 | SKIPSET_EXPORT void set_start(set_t *set) { | 239 | SKIPSET_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. */ |
| 249 | SKIPSET_EXPORT int set_ok(set_t *set) { | 256 | SKIPSET_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(). */ |
| 258 | SKIPSET_EXPORT void set_clear(set_t *set) { | 265 | SKIPSET_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(). */ |
| 277 | SKIPSET_EXPORT void set_end(set_t *set) { | 284 | SKIPSET_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(). */ |
| 304 | SKIPSET_EXPORT int set_found(set_t *set, set_key_t key) { | 311 | SKIPSET_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. */ |
| 327 | SKIPSET_EXPORT int set_insert(set_t *set, set_key_t key) { | 334 | SKIPSET_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); |
