Coverage Report

Created: 2025-08-26 06:43

/src/opensc/src/common/simclist.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2007,2008,2009,2010 Mij <mij@bitchx.it>
3
 *
4
 * Permission to use, copy, modify, and distribute this software for any
5
 * purpose with or without fee is hereby granted, provided that the above
6
 * copyright notice and this permission notice appear in all copies.
7
 *
8
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
 */
16
17
18
/*
19
 * SimCList library. See http://mij.oltrelinux.com/devel/simclist
20
 */
21
22
/* SimCList implementation, version 1.5, with local modifications */
23
24
#include <stdlib.h>
25
#include <string.h>
26
#include <errno.h>      /* for setting errno */
27
#include <sys/types.h>
28
#if !defined(_WIN32)
29
#include <arpa/inet.h>  /* for htons() */
30
#include <unistd.h>
31
#ifdef HAVE_SYS_TIME_H
32
#include <sys/time.h>   /* for gettimeofday() */
33
#endif
34
#include <stdint.h>
35
#else
36
#include <winsock2.h>
37
#endif
38
#ifdef SIMCLIST_DUMPRESTORE
39
#ifndef _WIN32
40
#include <sys/uio.h>    /* for READ_ERRCHECK() and write() */
41
#endif
42
#include <fcntl.h>      /* for open() etc */
43
#endif
44
#include <time.h>       /* for time() for random seed */
45
#include <sys/stat.h>   /* for open()'s access modes S_IRUSR etc */
46
#include <limits.h>
47
48
49
#ifdef SIMCLIST_DUMPRESTORE
50
/* convert 64bit integers from host to network format */
51
#define hton64(x)       (\
52
        htons(1) == 1 ?                                         \
53
            (uint64_t)x      /* big endian */                   \
54
        :       /* little endian */                             \
55
        ((uint64_t)((((uint64_t)(x) & 0xff00000000000000ULL) >> 56) | \
56
            (((uint64_t)(x) & 0x00ff000000000000ULL) >> 40) | \
57
            (((uint64_t)(x) & 0x0000ff0000000000ULL) >> 24) | \
58
            (((uint64_t)(x) & 0x000000ff00000000ULL) >>  8) | \
59
            (((uint64_t)(x) & 0x00000000ff000000ULL) <<  8) | \
60
            (((uint64_t)(x) & 0x0000000000ff0000ULL) << 24) | \
61
            (((uint64_t)(x) & 0x000000000000ff00ULL) << 40) | \
62
            (((uint64_t)(x) & 0x00000000000000ffULL) << 56)))   \
63
        )
64
65
/* convert 64bit integers from network to host format */
66
#define ntoh64(x)       (hton64(x))
67
#endif
68
69
/* some OSes don't have EPROTO (eg OpenBSD) */
70
#ifndef EPROTO
71
#define EPROTO  EIO
72
#endif
73
74
/* disable asserts */
75
#ifndef SIMCLIST_DEBUG
76
#ifndef NDEBUG
77
#define NDEBUG
78
#endif
79
#endif
80
81
#include <assert.h>
82
83
#ifdef SIMCLIST_WITH_THREADS
84
/* limit (approx) to the number of threads running
85
 * for threaded operations. Only meant when
86
 * SIMCLIST_WITH_THREADS is defined */
87
#define SIMCLIST_MAXTHREADS   2
88
#endif
89
90
/*
91
 * how many elems to keep as spare. During a deletion, an element
92
 * can be saved in a "free-list", not free()d immediately. When
93
 * latter insertions are performed, spare elems can be used instead
94
 * of malloc()ing new elems.
95
 *
96
 * about this param, some values for appending
97
 * 10 million elems into an empty list:
98
 * (#, time[sec], gain[%], gain/no[%])
99
 * 0    2,164   0,00    0,00    <-- feature disabled
100
 * 1    1,815   34,9    34,9
101
 * 2    1,446   71,8    35,9    <-- MAX gain/no
102
 * 3    1,347   81,7    27,23
103
 * 5    1,213   95,1    19,02
104
 * 8    1,064   110,0   13,75
105
 * 10   1,015   114,9   11,49   <-- MAX gain w/ likely sol
106
 * 15   1,019   114,5   7,63
107
 * 25   0,985   117,9   4,72
108
 * 50   1,088   107,6   2,15
109
 * 75   1,016   114,8   1,53
110
 * 100  0,988   117,6   1,18
111
 * 150  1,022   114,2   0,76
112
 * 200  0,939   122,5   0,61    <-- MIN time
113
 */
114
#ifndef SIMCLIST_MAX_SPARE_ELEMS
115
871k
#define SIMCLIST_MAX_SPARE_ELEMS        5
116
#endif
117
118
119
#ifdef SIMCLIST_WITH_THREADS
120
#include <pthread.h>
121
#endif
122
123
#include "simclist.h"
124
125
126
/* minimum number of elements for sorting with quicksort instead of insertion */
127
0
#define SIMCLIST_MINQUICKSORTELS        24
128
129
130
/* list dump declarations */
131
#define SIMCLIST_DUMPFORMAT_VERSION     1   /* (short integer) version of fileformat managed by _dump* and _restore* functions */
132
133
#define SIMCLIST_DUMPFORMAT_HEADERLEN   30  /* length of the header */
134
135
/* header for a list dump */
136
struct list_dump_header_s {
137
    uint16_t ver;               /* version */
138
    int64_t timestamp;          /* dump timestamp */
139
    int32_t rndterm;            /* random value terminator -- terminates the data sequence */
140
141
    uint32_t totlistlen;        /* sum of every element' size, bytes */
142
    uint32_t numels;            /* number of elements */
143
    uint32_t elemlen;           /* bytes length of an element, for constant-size lists, <= 0 otherwise */
144
    int32_t listhash;           /* hash of the list at the time of dumping, or 0 if to be ignored */
145
};
146
147
148
149
/* deletes tmp from list, with care wrt its position (head, tail, middle) */
150
static int list_drop_elem(list_t *simclist_restrict l, struct list_entry_s *tmp, unsigned int pos);
151
152
/* set default values for initialized lists */
153
static int list_attributes_setdefaults(list_t *simclist_restrict l);
154
155
#ifndef NDEBUG
156
/* check whether the list internal REPresentation is valid -- Costs O(n) */
157
static int list_repOk(const list_t *simclist_restrict l);
158
159
/* check whether the list attribute set is valid -- Costs O(1) */
160
static int list_attrOk(const list_t *simclist_restrict l);
161
#endif
162
163
/* do not inline, this is recursive */
164
static void list_sort_quicksort(list_t *simclist_restrict l, int versus,
165
        unsigned int first, struct list_entry_s *fel,
166
        unsigned int last, struct list_entry_s *lel);
167
168
static simclist_inline void list_sort_selectionsort(list_t *simclist_restrict l, int versus,
169
        unsigned int first, struct list_entry_s *fel,
170
        unsigned int last, struct list_entry_s *lel);
171
172
static void *list_get_minmax(const list_t *simclist_restrict l, int versus);
173
174
static simclist_inline struct list_entry_s *list_findpos(const list_t *simclist_restrict l, int posstart);
175
176
#ifdef SIMCLIST_DUMPRESTORE
177
/* write() decorated with error checking logic */
178
#define WRITE_ERRCHECK(fd, msgbuf, msglen)      do {                                                    \
179
                                                    if (write(fd, msgbuf, msglen) < 0) return -1;       \
180
                                                } while (0);
181
/* READ_ERRCHECK() decorated with error checking logic */
182
#define READ_ERRCHECK(fd, msgbuf, msglen)      do {                                                     \
183
                                                    if (read(fd, msgbuf, msglen) != msglen) {           \
184
                                                        /*errno = EPROTO;*/                             \
185
                                                        free(buf);                                      \
186
                                                        return -1;                                      \
187
                                                    }                                                   \
188
                                                } while (0);
189
#endif
190
191
/*
192
 * Random Number Generator
193
 *
194
 * The user is expected to seed the RNG (ie call srand()) if
195
 * SIMCLIST_SYSTEM_RNG is defined.
196
 *
197
 * Otherwise, a self-contained RNG based on LCG is used; see
198
 * http://en.wikipedia.org/wiki/Linear_congruential_generator .
199
 *
200
 * Facts pro local RNG:
201
 * 1. no need for the user to call srand() on his own
202
 * 2. very fast, possibly faster than OS
203
 * 3. avoid interference with user's RNG
204
 *
205
 * Facts pro system RNG:
206
 * 1. may be more accurate (irrelevant for SimCList random purposes)
207
 * 2. why reinvent the wheel
208
 *
209
 * Default to local RNG for user's ease of use.
210
 */
211
212
#ifdef SIMCLIST_SYSTEM_RNG
213
/* keep track whether we initialized already (non-0) or not (0) */
214
static unsigned random_seed = 0;
215
216
/* use local RNG */
217
static simclist_inline void seed_random() {
218
    if (random_seed == 0)
219
        random_seed = (unsigned)getpid() ^ (unsigned)time(NULL);
220
}
221
222
static simclist_inline long get_random() {
223
    random_seed = (1664525 * random_seed + 1013904223);
224
    return random_seed;
225
}
226
227
#else
228
/* use OS's random generator */
229
#   define  seed_random()
230
0
#   define  get_random()        (rand())
231
#endif
232
233
234
/* list initialization */
235
205k
int list_init(list_t *simclist_restrict l) {
236
205k
    if (l == NULL) {
237
0
        return -1;
238
0
    }
239
240
205k
    memset(l, 0, sizeof *l);
241
242
205k
    seed_random();
243
244
205k
    l->numels = 0;
245
246
    /* head/tail sentinels and mid pointer */
247
205k
    l->head_sentinel = (struct list_entry_s *)malloc(sizeof(struct list_entry_s));
248
205k
    l->tail_sentinel = (struct list_entry_s *)malloc(sizeof(struct list_entry_s));
249
205k
    if (l->tail_sentinel == NULL || l->head_sentinel == NULL) {
250
0
        return -1;
251
0
    }
252
205k
    l->head_sentinel->next = l->tail_sentinel;
253
205k
    l->tail_sentinel->prev = l->head_sentinel;
254
205k
    l->head_sentinel->prev = l->tail_sentinel->next = l->mid = NULL;
255
205k
    l->head_sentinel->data = l->tail_sentinel->data = NULL;
256
257
    /* iteration attributes */
258
205k
    l->iter_active = 0;
259
205k
    l->iter_pos = 0;
260
205k
    l->iter_curentry = NULL;
261
262
    /* free-list attributes */
263
205k
    l->spareelsnum = 0;
264
205k
    l->spareels = (struct list_entry_s **)malloc(SIMCLIST_MAX_SPARE_ELEMS * sizeof(struct list_entry_s *));
265
205k
    if (l->spareels == NULL) {
266
0
        return -1;
267
0
    }
268
269
#ifdef SIMCLIST_WITH_THREADS
270
    l->threadcount = 0;
271
#endif
272
273
205k
    if (0 != list_attributes_setdefaults(l)) {
274
0
        return -1;
275
0
    }
276
277
205k
    assert(list_repOk(l));
278
205k
    assert(list_attrOk(l));
279
280
205k
    return 0;
281
205k
}
282
283
205k
void list_destroy(list_t *simclist_restrict l) {
284
205k
    unsigned int i;
285
286
205k
    list_clear(l);
287
382k
    for (i = 0; i < l->spareelsnum; i++) {
288
177k
        free(l->spareels[i]);
289
177k
    }
290
205k
    free(l->spareels);
291
205k
    free(l->head_sentinel);
292
205k
    free(l->tail_sentinel);
293
205k
}
294
295
205k
int list_attributes_setdefaults(list_t *simclist_restrict l) {
296
205k
    l->attrs.comparator = NULL;
297
205k
    l->attrs.seeker = NULL;
298
299
    /* also free() element data when removing and element from the list */
300
205k
    l->attrs.meter = NULL;
301
205k
    l->attrs.copy_data = 0;
302
303
205k
    l->attrs.hasher = NULL;
304
305
    /* serializer/unserializer */
306
205k
    l->attrs.serializer = NULL;
307
205k
    l->attrs.unserializer = NULL;
308
309
205k
    assert(list_attrOk(l));
310
311
205k
    return 0;
312
205k
}
313
314
/* setting list properties */
315
22.9k
int list_attributes_comparator(list_t *simclist_restrict l, element_comparator comparator_fun) {
316
22.9k
    if (l == NULL) return -1;
317
318
22.9k
    l->attrs.comparator = comparator_fun;
319
320
22.9k
    assert(list_attrOk(l));
321
322
22.9k
    return 0;
323
22.9k
}
324
325
162k
int list_attributes_seeker(list_t *simclist_restrict l, element_seeker seeker_fun) {
326
162k
    if (l == NULL) return -1;
327
328
162k
    l->attrs.seeker = seeker_fun;
329
162k
    assert(list_attrOk(l));
330
331
162k
    return 0;
332
162k
}
333
334
32.5k
int list_attributes_copy(list_t *simclist_restrict l, element_meter metric_fun, int copy_data) {
335
32.5k
    if (l == NULL || (metric_fun == NULL && copy_data != 0)) return -1;
336
337
32.5k
    l->attrs.meter = metric_fun;
338
32.5k
    l->attrs.copy_data = copy_data;
339
340
32.5k
    assert(list_attrOk(l));
341
342
32.5k
    return 0;
343
32.5k
}
344
345
0
int list_attributes_hash_computer(list_t *simclist_restrict l, element_hash_computer hash_computer_fun) {
346
0
    if (l == NULL) return -1;
347
348
0
    l->attrs.hasher = hash_computer_fun;
349
0
    assert(list_attrOk(l));
350
0
    return 0;
351
0
}
352
353
0
int list_attributes_serializer(list_t *simclist_restrict l, element_serializer serializer_fun) {
354
0
    if (l == NULL) return -1;
355
356
0
    l->attrs.serializer = serializer_fun;
357
0
    assert(list_attrOk(l));
358
0
    return 0;
359
0
}
360
361
0
int list_attributes_unserializer(list_t *simclist_restrict l, element_unserializer unserializer_fun) {
362
0
    if (l == NULL) return -1;
363
364
0
    l->attrs.unserializer = unserializer_fun;
365
0
    assert(list_attrOk(l));
366
0
    return 0;
367
0
}
368
369
368k
int list_append(list_t *simclist_restrict l, const void *data) {
370
368k
    return list_insert_at(l, data, l->numels);
371
368k
}
372
373
0
int list_prepend(list_t *simclist_restrict l, const void *data) {
374
0
    return list_insert_at(l, data, 0);
375
0
}
376
377
91.0k
void *list_fetch(list_t *simclist_restrict l) {
378
91.0k
    return list_extract_at(l, 0);
379
91.0k
}
380
381
333k
void *list_get_at(const list_t *simclist_restrict l, unsigned int pos) {
382
333k
    struct list_entry_s *tmp;
383
384
333k
    tmp = list_findpos(l, pos);
385
386
333k
    return (tmp != NULL ? tmp->data : NULL);
387
333k
}
388
389
0
void *list_get_max(const list_t *simclist_restrict l) {
390
0
    return list_get_minmax(l, +1);
391
0
}
392
393
0
void *list_get_min(const list_t *simclist_restrict l) {
394
0
    return list_get_minmax(l, -1);
395
0
}
396
397
/* REQUIRES {list->numels >= 1}
398
 * return the min (versus < 0) or max value (v > 0) in l */
399
0
static void *list_get_minmax(const list_t *simclist_restrict l, int versus) {
400
0
    void *curminmax;
401
0
    struct list_entry_s *s;
402
403
0
    if (l->attrs.comparator == NULL || l->numels == 0)
404
0
        return NULL;
405
406
0
    curminmax = l->head_sentinel->next->data;
407
0
    for (s = l->head_sentinel->next->next; s != l->tail_sentinel; s = s->next) {
408
0
        if (l->attrs.comparator(curminmax, s->data) * versus > 0)
409
0
            curminmax = s->data;
410
0
    }
411
412
0
    return curminmax;
413
0
}
414
415
/* set tmp to point to element at index posstart in l */
416
863k
static simclist_inline struct list_entry_s *list_findpos(const list_t *simclist_restrict l, int posstart) {
417
863k
    struct list_entry_s *ptr;
418
863k
    float x;
419
863k
    int i;
420
421
863k
    if (l->head_sentinel == NULL || l->tail_sentinel == NULL) return NULL;
422
423
    /* accept 1 slot overflow for fetching head and tail sentinels */
424
863k
    if (posstart < -1 || posstart > (int)l->numels) return NULL;
425
426
863k
    x = l->numels ? (float)(posstart+1) / l->numels : 0;
427
863k
    if (x <= 0.25) {
428
        /* first quarter: get to posstart from head */
429
230k
        for (i = -1, ptr = l->head_sentinel; i < posstart; ptr = ptr->next, i++);
430
684k
    } else if (x < 0.5) {
431
        /* second quarter: get to posstart from mid */
432
44.3k
        for (i = (l->numels-1)/2, ptr = l->mid; i > posstart; ptr = ptr->prev, i--);
433
673k
    } else if (x <= 0.75) {
434
        /* third quarter: get to posstart from mid */
435
37.2k
        for (i = (l->numels-1)/2, ptr = l->mid; i < posstart; ptr = ptr->next, i++);
436
660k
    } else {
437
        /* fourth quarter: get to posstart from tail */
438
1.34M
        for (i = l->numels, ptr = l->tail_sentinel; i > posstart; ptr = ptr->prev, i--);
439
660k
    }
440
441
863k
    return ptr;
442
863k
}
443
444
91.0k
void *list_extract_at(list_t *simclist_restrict l, unsigned int pos) {
445
91.0k
    struct list_entry_s *tmp;
446
91.0k
    void *data;
447
448
91.0k
    if (l->iter_active || pos >= l->numels) return NULL;
449
450
42.8k
    tmp = list_findpos(l, pos);
451
42.8k
    if (tmp == NULL) {
452
0
        return NULL;
453
0
    }
454
42.8k
    data = tmp->data;
455
456
42.8k
    tmp->data = NULL;   /* save data from list_drop_elem() free() */
457
42.8k
    list_drop_elem(l, tmp, pos);
458
42.8k
    l->numels--;
459
460
42.8k
    assert(list_repOk(l));
461
462
42.8k
    return data;
463
42.8k
}
464
465
368k
int list_insert_at(list_t *simclist_restrict l, const void *data, unsigned int pos) {
466
368k
    struct list_entry_s *lent, *succ, *prec;
467
468
368k
    if (l->iter_active || pos > l->numels) return -1;
469
470
    /* this code optimizes malloc() with a free-list */
471
368k
    if (l->spareelsnum > 0) {
472
0
        lent = l->spareels[l->spareelsnum-1];
473
0
        l->spareelsnum--;
474
368k
    } else {
475
368k
        lent = (struct list_entry_s *)malloc(sizeof(struct list_entry_s));
476
368k
        if (lent == NULL) {
477
0
            return -1;
478
0
        }
479
368k
    }
480
481
368k
    if (l->attrs.copy_data) {
482
        /* make room for user' data (has to be copied) */
483
207k
        size_t datalen = l->attrs.meter(data);
484
207k
        lent->data = (struct list_entry_s *)malloc(datalen);
485
207k
        if (lent->data == NULL) {
486
0
            if (!(l->spareelsnum > 0)) {
487
0
                free(lent);
488
0
            }
489
0
            return -1;
490
0
        }
491
207k
        memcpy(lent->data, data, datalen);
492
207k
    } else {
493
161k
        lent->data = (void*)data;
494
161k
    }
495
496
    /* actually append element */
497
368k
    prec = list_findpos(l, pos-1);
498
368k
    if (prec == NULL) {
499
0
        if (l->attrs.copy_data) {
500
0
            free(lent->data);
501
0
        }
502
0
        if (!(l->spareelsnum > 0)) {
503
0
            free(lent);
504
0
        }
505
0
        return -1;
506
0
    }
507
368k
    succ = prec->next;
508
509
368k
    prec->next = lent;
510
368k
    lent->prev = prec;
511
368k
    lent->next = succ;
512
368k
    succ->prev = lent;
513
514
368k
    l->numels++;
515
516
    /* fix mid pointer */
517
368k
    if (l->numels == 1) { /* first element, set pointer */
518
150k
        l->mid = lent;
519
218k
    } else if (l->numels % 2) {    /* now odd */
520
106k
        if (pos >= (l->numels-1)/2) l->mid = l->mid->next;
521
111k
    } else {                /* now even */
522
111k
        if (pos <= (l->numels-1)/2) l->mid = l->mid->prev;
523
111k
    }
524
525
368k
    assert(list_repOk(l));
526
527
368k
    return 1;
528
368k
}
529
530
118k
int list_delete(list_t *simclist_restrict l, const void *data) {
531
118k
  int pos, r;
532
533
118k
  pos = list_locate(l, data);
534
118k
  if (pos < 0)
535
0
    return -1;
536
537
118k
  r = list_delete_at(l, pos);
538
118k
  if (r < 0)
539
0
    return -1;
540
541
118k
    assert(list_repOk(l));
542
543
118k
  return 0;
544
118k
}
545
546
118k
int list_delete_at(list_t *simclist_restrict l, unsigned int pos) {
547
118k
    struct list_entry_s *delendo;
548
549
550
118k
    if (l->iter_active || pos >= l->numels) return -1;
551
552
118k
    delendo = list_findpos(l, pos);
553
554
118k
    list_drop_elem(l, delendo, pos);
555
556
118k
    l->numels--;
557
558
559
118k
    assert(list_repOk(l));
560
561
118k
    return  0;
562
118k
}
563
564
0
int list_delete_range(list_t *simclist_restrict l, unsigned int posstart, unsigned int posend) {
565
0
    struct list_entry_s *lastvalid, *tmp, *tmp2;
566
0
    unsigned int i;
567
0
    int movedx;
568
0
    unsigned int numdel, midposafter;
569
570
0
    if (l->iter_active || posend < posstart || posend >= l->numels) return -1;
571
572
0
    tmp = list_findpos(l, posstart);    /* first el to be deleted */
573
0
    if (tmp == NULL) {
574
0
        return -1;
575
0
    }
576
0
    lastvalid = tmp->prev;              /* last valid element */
577
578
0
    numdel = posend - posstart + 1;
579
0
    midposafter = (l->numels-1-numdel)/2;
580
581
0
    midposafter = midposafter < posstart ? midposafter : midposafter+numdel;
582
0
    movedx = midposafter - (l->numels-1)/2;
583
584
0
    if (movedx > 0) { /* move right */
585
0
        for (i = 0; i < (unsigned int)movedx; l->mid = l->mid->next, i++);
586
0
    } else {    /* move left */
587
0
        movedx = -movedx;
588
0
        for (i = 0; i < (unsigned int)movedx; l->mid = l->mid->prev, i++);
589
0
    }
590
591
0
    assert(posstart == 0 || lastvalid != l->head_sentinel);
592
0
    i = posstart;
593
0
    if (l->attrs.copy_data) {
594
        /* also free element data */
595
0
        for (; i <= posend; i++) {
596
0
            tmp2 = tmp;
597
0
            tmp = tmp->next;
598
0
            if (tmp2->data != NULL) free(tmp2->data);
599
0
            if (l->spareelsnum < SIMCLIST_MAX_SPARE_ELEMS) {
600
0
                l->spareels[l->spareelsnum++] = tmp2;
601
0
            } else {
602
0
                free(tmp2);
603
0
            }
604
0
        }
605
0
    } else {
606
        /* only free containers */
607
0
        for (; i <= posend; i++) {
608
0
            tmp2 = tmp;
609
0
            tmp = tmp->next;
610
0
            if (l->spareelsnum < SIMCLIST_MAX_SPARE_ELEMS) {
611
0
                l->spareels[l->spareelsnum++] = tmp2;
612
0
            } else {
613
0
                free(tmp2);
614
0
            }
615
0
        }
616
0
    }
617
0
    assert(i == posend+1 && (posend != l->numels || tmp == l->tail_sentinel));
618
619
0
    lastvalid->next = tmp;
620
0
    tmp->prev = lastvalid;
621
622
0
    l->numels -= posend - posstart + 1;
623
624
0
    assert(list_repOk(l));
625
626
0
    return 0;
627
0
}
628
629
221k
int list_clear(list_t *simclist_restrict l) {
630
221k
    struct list_entry_s *s;
631
632
221k
    if (l->iter_active) return -1;
633
634
221k
    if (l->head_sentinel && l->tail_sentinel) {
635
221k
        if (l->attrs.copy_data) {        /* also free user data */
636
            /* spare a loop conditional with two loops: spareing elems and freeing elems */
637
63.7k
            for (s = l->head_sentinel->next; l->spareelsnum < SIMCLIST_MAX_SPARE_ELEMS && s != l->tail_sentinel; s = s->next) {
638
                /* move elements as spares as long as there is room */
639
31.2k
                if (s->data != NULL) free(s->data);
640
31.2k
                l->spareels[l->spareelsnum++] = s;
641
31.2k
            }
642
208k
            while (s != l->tail_sentinel) {
643
                /* free the remaining elems */
644
176k
                if (s->data != NULL) free(s->data);
645
176k
                s = s->next;
646
176k
                free(s->prev);
647
176k
            }
648
32.5k
            l->head_sentinel->next = l->tail_sentinel;
649
32.5k
            l->tail_sentinel->prev = l->head_sentinel;
650
188k
        } else { /* only free element containers */
651
            /* spare a loop conditional with two loops: spareing elems and freeing elems */
652
188k
            for (s = l->head_sentinel->next; l->spareelsnum < SIMCLIST_MAX_SPARE_ELEMS && s != l->tail_sentinel; s = s->next) {
653
                /* move elements as spares as long as there is room */
654
0
                l->spareels[l->spareelsnum++] = s;
655
0
            }
656
188k
            while (s != l->tail_sentinel) {
657
                /* free the remaining elems */
658
0
                s = s->next;
659
0
                free(s->prev);
660
0
            }
661
188k
            l->head_sentinel->next = l->tail_sentinel;
662
188k
            l->tail_sentinel->prev = l->head_sentinel;
663
188k
        }
664
221k
    }
665
221k
    l->numels = 0;
666
221k
    l->mid = NULL;
667
668
221k
    assert(list_repOk(l));
669
670
221k
    return 0;
671
221k
}
672
673
548k
unsigned int list_size(const list_t *simclist_restrict l) {
674
548k
    return l->numels;
675
548k
}
676
677
0
int list_empty(const list_t *simclist_restrict l) {
678
0
    return (l->numels == 0);
679
0
}
680
681
283k
int list_locate(const list_t *simclist_restrict l, const void *data) {
682
283k
    struct list_entry_s *el;
683
283k
    int pos = 0;
684
685
283k
    if (l->head_sentinel == NULL || l->tail_sentinel == NULL) return -1;
686
687
283k
    if (l->attrs.comparator != NULL) {
688
        /* use comparator */
689
1.02M
        for (el = l->head_sentinel->next; el != l->tail_sentinel; el = el->next, pos++) {
690
949k
            if (l->attrs.comparator(data, el->data) == 0) break;
691
949k
        }
692
162k
    } else {
693
        /* compare references */
694
444k
        for (el = l->head_sentinel->next; el != l->tail_sentinel; el = el->next, pos++) {
695
418k
            if (el->data == data) break;
696
418k
        }
697
162k
    }
698
283k
    if (el == l->tail_sentinel) return -1;
699
700
181k
    return pos;
701
283k
}
702
703
73.3k
void *list_seek(list_t *simclist_restrict l, const void *indicator) {
704
73.3k
    const struct list_entry_s *iter;
705
706
73.3k
    if (l->attrs.seeker == NULL) return NULL;
707
708
73.3k
    if (l->head_sentinel == NULL || l->tail_sentinel == NULL) return NULL;
709
710
78.2k
    for (iter = l->head_sentinel->next; iter != l->tail_sentinel; iter = iter->next) {
711
63.1k
        if (l->attrs.seeker(iter->data, indicator) != 0) return iter->data;
712
63.1k
    }
713
714
15.0k
    return NULL;
715
73.3k
}
716
717
81.1k
int list_contains(const list_t *simclist_restrict l, const void *data) {
718
81.1k
    return (list_locate(l, data) >= 0);
719
81.1k
}
720
721
0
int list_concat(const list_t *l1, const list_t *l2, list_t *simclist_restrict dest) {
722
0
    struct list_entry_s *el, *srcel;
723
0
    unsigned int cnt;
724
0
    int err;
725
726
0
    if (l1 == NULL || l2 == NULL || dest == NULL || l1 == dest || l2 == dest)
727
0
        return -1;
728
729
0
    if (l1->head_sentinel == NULL || l1->tail_sentinel == NULL
730
0
            || l2->head_sentinel == NULL || l2->tail_sentinel == NULL) return -1;
731
732
0
    if (0 != list_init(dest)) {
733
0
        return -1;
734
0
    }
735
736
0
    dest->numels = l1->numels + l2->numels;
737
0
    if (dest->numels == 0)
738
0
        return 0;
739
740
    /* copy list1 */
741
0
    srcel = l1->head_sentinel->next;
742
0
    el = dest->head_sentinel;
743
0
    while (srcel != l1->tail_sentinel) {
744
0
        el->next = (struct list_entry_s *)malloc(sizeof(struct list_entry_s));
745
0
        if (el->next == NULL) {
746
0
            return -1;
747
0
        }
748
0
        el->next->prev = el;
749
0
        el = el->next;
750
0
        el->data = srcel->data;
751
0
        srcel = srcel->next;
752
0
    }
753
0
    dest->mid = el;     /* approximate position (adjust later) */
754
    /* copy list 2 */
755
0
    srcel = l2->head_sentinel->next;
756
0
    while (srcel != l2->tail_sentinel) {
757
0
        el->next = (struct list_entry_s *)malloc(sizeof(struct list_entry_s));
758
0
        if (el->next == NULL) {
759
0
            return -1;
760
0
        }
761
0
        el->next->prev = el;
762
0
        el = el->next;
763
0
        el->data = srcel->data;
764
0
        srcel = srcel->next;
765
0
    }
766
0
    el->next = dest->tail_sentinel;
767
0
    dest->tail_sentinel->prev = el;
768
769
    /* fix mid pointer */
770
0
    err = l2->numels - l1->numels;
771
0
    if ((err+1)/2 > 0) {        /* correct pos RIGHT (err-1)/2 moves */
772
0
        err = (err+1)/2;
773
0
        for (cnt = 0; dest->mid && cnt < (unsigned int)err; cnt++) dest->mid = dest->mid->next;
774
0
    } else if (err/2 < 0) { /* correct pos LEFT (err/2)-1 moves */
775
0
        err = -err/2;
776
0
        for (cnt = 0; dest->mid && cnt < (unsigned int)err; cnt++) dest->mid = dest->mid->prev;
777
0
    }
778
779
0
    assert(!(list_repOk(l1) && list_repOk(l2)) || list_repOk(dest));
780
781
0
    return 0;
782
0
}
783
784
0
int list_sort(list_t *simclist_restrict l, int versus) {
785
0
    if (l->iter_active || l->attrs.comparator == NULL) /* cannot modify list in the middle of an iteration */
786
0
        return -1;
787
788
0
    if (l->numels <= 1)
789
0
        return 0;
790
791
0
    if (l->head_sentinel == NULL || l->tail_sentinel == NULL) return -1;
792
793
0
    list_sort_quicksort(l, versus, 0, l->head_sentinel->next, l->numels-1, l->tail_sentinel->prev);
794
0
    assert(list_repOk(l));
795
0
    return 0;
796
0
}
797
798
#ifdef SIMCLIST_WITH_THREADS
799
struct list_sort_wrappedparams {
800
    list_t *simclist_restrict l;
801
    int versus;
802
    unsigned int first, last;
803
    struct list_entry_s *fel, *lel;
804
};
805
806
static void *list_sort_quicksort_threadwrapper(void *wrapped_params) {
807
    struct list_sort_wrappedparams *wp = (struct list_sort_wrappedparams *)wrapped_params;
808
    list_sort_quicksort(wp->l, wp->versus, wp->first, wp->fel, wp->last, wp->lel);
809
    free(wp);
810
    pthread_exit(NULL);
811
    return NULL;
812
}
813
#endif
814
815
static simclist_inline void list_sort_selectionsort(list_t *simclist_restrict l, int versus,
816
        unsigned int first, struct list_entry_s *fel,
817
0
        unsigned int last, struct list_entry_s *lel) {
818
0
    struct list_entry_s *cursor, *toswap, *firstunsorted;
819
0
    void *tmpdata;
820
821
0
    if (last <= first) /* <= 1-element lists are always sorted */
822
0
        return;
823
824
0
    for (firstunsorted = fel; firstunsorted != lel; firstunsorted = firstunsorted->next) {
825
        /* find min or max in the remainder of the list */
826
0
        for (toswap = firstunsorted, cursor = firstunsorted->next; cursor != lel->next; cursor = cursor->next)
827
0
            if (l->attrs.comparator(toswap->data, cursor->data) * -versus > 0) toswap = cursor;
828
0
        if (toswap != firstunsorted) { /* swap firstunsorted with toswap */
829
0
            tmpdata = firstunsorted->data;
830
0
            firstunsorted->data = toswap->data;
831
0
            toswap->data = tmpdata;
832
0
        }
833
0
    }
834
0
}
835
836
static void list_sort_quicksort(list_t *simclist_restrict l, int versus,
837
        unsigned int first, struct list_entry_s *fel,
838
0
        unsigned int last, struct list_entry_s *lel) {
839
0
    unsigned int pivotid;
840
0
    unsigned int i;
841
0
    register struct list_entry_s *pivot;
842
0
    struct list_entry_s *left, *right;
843
0
    void *tmpdata;
844
#ifdef SIMCLIST_WITH_THREADS
845
    pthread_t tid;
846
    int traised;
847
#endif
848
849
850
0
    if (last <= first)      /* <= 1-element lists are always sorted */
851
0
        return;
852
853
0
    if (last - first+1 <= SIMCLIST_MINQUICKSORTELS) {
854
0
        list_sort_selectionsort(l, versus, first, fel, last, lel);
855
0
        return;
856
0
    }
857
858
    /* base of iteration: one element list */
859
0
    if (! (last > first)) return;
860
861
0
    pivotid = (get_random() % (last - first + 1));
862
    /* pivotid = (last - first + 1) / 2; */
863
864
    /* find pivot */
865
0
    if (pivotid < (last - first + 1)/2) {
866
0
        for (i = 0, pivot = fel; i < pivotid; pivot = pivot->next, i++);
867
0
    } else {
868
0
        for (i = last - first, pivot = lel; i > pivotid; pivot = pivot->prev, i--);
869
0
    }
870
871
    /* smaller PIVOT bigger */
872
0
    left = fel;
873
0
    right = lel;
874
    /* iterate     --- left ---> PIV <--- right --- */
875
0
    while (left != pivot && right != pivot) {
876
0
        for (; left != pivot && (l->attrs.comparator(left->data, pivot->data) * -versus <= 0); left = left->next);
877
        /* left points to a smaller element, or to pivot */
878
0
        for (; right != pivot && (l->attrs.comparator(right->data, pivot->data) * -versus >= 0); right = right->prev);
879
        /* right points to a bigger element, or to pivot */
880
0
        if (left != pivot && right != pivot) {
881
            /* swap, then move iterators */
882
0
            tmpdata = left->data;
883
0
            left->data = right->data;
884
0
            right->data = tmpdata;
885
886
0
            left = left->next;
887
0
            right = right->prev;
888
0
        }
889
0
    }
890
891
    /* now either left points to pivot (end run), or right */
892
0
    if (right == pivot) {    /* left part longer */
893
0
        while (left != pivot) {
894
0
            if (l->attrs.comparator(left->data, pivot->data) * -versus > 0) {
895
0
                tmpdata = left->data;
896
0
                left->data = pivot->prev->data;
897
0
                pivot->prev->data = pivot->data;
898
0
                pivot->data = tmpdata;
899
0
                pivot = pivot->prev;
900
0
                pivotid--;
901
0
                if (pivot == left) break;
902
0
            } else {
903
0
                left = left->next;
904
0
            }
905
0
        }
906
0
    } else {                /* right part longer */
907
0
        while (right != pivot) {
908
0
            if (l->attrs.comparator(right->data, pivot->data) * -versus < 0) {
909
                /* move current right before pivot */
910
0
                tmpdata = right->data;
911
0
                right->data = pivot->next->data;
912
0
                pivot->next->data = pivot->data;
913
0
                pivot->data = tmpdata;
914
0
                pivot = pivot->next;
915
0
                pivotid++;
916
0
                if (pivot == right) break;
917
0
            } else {
918
0
                right = right->prev;
919
0
            }
920
0
        }
921
0
    }
922
923
    /* sort sublists A and B :       |---A---| pivot |---B---| */
924
925
#ifdef SIMCLIST_WITH_THREADS
926
    traised = 0;
927
    if (pivotid > 0) {
928
        /* prepare wrapped args, then start thread */
929
        if (l->threadcount < SIMCLIST_MAXTHREADS-1) {
930
            struct list_sort_wrappedparams *wp = (struct list_sort_wrappedparams *)malloc(sizeof(struct list_sort_wrappedparams));
931
            if (wp == NULL) {
932
                return -1;
933
            }
934
            l->threadcount++;
935
            traised = 1;
936
            wp->l = l;
937
            wp->versus = versus;
938
            wp->first = first;
939
            wp->fel = fel;
940
            wp->last = first+pivotid-1;
941
            wp->lel = pivot->prev;
942
            if (pthread_create(&tid, NULL, list_sort_quicksort_threadwrapper, wp) != 0) {
943
                free(wp);
944
                traised = 0;
945
                list_sort_quicksort(l, versus, first, fel, first+pivotid-1, pivot->prev);
946
            }
947
        } else {
948
            list_sort_quicksort(l, versus, first, fel, first+pivotid-1, pivot->prev);
949
        }
950
    }
951
    if (first + pivotid < last) list_sort_quicksort(l, versus, first+pivotid+1, pivot->next, last, lel);
952
    if (traised) {
953
        pthread_join(tid, (void **)NULL);
954
        l->threadcount--;
955
    }
956
#else
957
0
    if (pivotid > 0) list_sort_quicksort(l, versus, first, fel, first+pivotid-1, pivot->prev);
958
0
    if (first + pivotid < last) list_sort_quicksort(l, versus, first+pivotid+1, pivot->next, last, lel);
959
0
#endif
960
0
}
961
962
35.7k
int list_iterator_start(list_t *simclist_restrict l) {
963
35.7k
    if (l->iter_active) return 0;
964
35.7k
    if (l->head_sentinel == NULL) return -1;
965
35.7k
    l->iter_pos = 0;
966
35.7k
    l->iter_active = 1;
967
35.7k
    l->iter_curentry = l->head_sentinel->next;
968
35.7k
    return 1;
969
35.7k
}
970
971
300k
void *list_iterator_next(list_t *simclist_restrict l) {
972
300k
    void *toret;
973
974
300k
    if (! l->iter_active) return NULL;
975
976
300k
    toret = l->iter_curentry->data;
977
300k
    l->iter_curentry = l->iter_curentry->next;
978
300k
    l->iter_pos++;
979
980
300k
    return toret;
981
300k
}
982
983
270k
int list_iterator_hasnext(const list_t *simclist_restrict l) {
984
270k
    if (! l->iter_active) return 0;
985
270k
    return (l->iter_pos < l->numels);
986
270k
}
987
988
35.7k
int list_iterator_stop(list_t *simclist_restrict l) {
989
35.7k
    if (! l->iter_active) return 0;
990
35.7k
    l->iter_pos = 0;
991
35.7k
    l->iter_active = 0;
992
35.7k
    return 1;
993
35.7k
}
994
995
0
int list_hash(const list_t *simclist_restrict l, list_hash_t *simclist_restrict hash) {
996
0
    struct list_entry_s *x;
997
0
    list_hash_t tmphash;
998
999
0
    assert(hash != NULL);
1000
1001
0
    tmphash = l->numels * 2 + 100;
1002
0
    if (l->attrs.hasher == NULL) {
1003
#ifdef SIMCLIST_ALLOW_LOCATIONBASED_HASHES
1004
        /* ENABLE WITH CARE !! */
1005
#warning "Memlocation-based hash is consistent only for testing modification in the same program run."
1006
        int i;
1007
1008
        /* only use element references */
1009
        for (x = l->head_sentinel->next; x != l->tail_sentinel; x = x->next) {
1010
            for (i = 0; i < sizeof(x->data); i++) {
1011
                tmphash += (tmphash ^ (uintptr_t)x->data);
1012
            }
1013
            tmphash += tmphash % l->numels;
1014
        }
1015
#else
1016
0
        return -1;
1017
0
#endif
1018
0
    } else {
1019
        /* hash each element with the user-given function */
1020
0
        for (x = l->head_sentinel->next; x != l->tail_sentinel; x = x->next) {
1021
0
            tmphash += tmphash ^ l->attrs.hasher(x->data);
1022
0
            tmphash +=* hash % l->numels;
1023
0
        }
1024
0
    }
1025
1026
0
    *hash = tmphash;
1027
1028
0
    return 0;
1029
0
}
1030
1031
#ifdef SIMCLIST_DUMPRESTORE
1032
/* Workaround for a missing gettimeofday on Windows */
1033
#if defined(_MSC_VER) || defined(__MINGW32__)
1034
int gettimeofday(struct timeval* tp, void* tzp) {
1035
    DWORD t;
1036
    t = timeGetTime();
1037
    tp->tv_sec = t / 1000;
1038
    tp->tv_usec = t % 1000;
1039
    return 0;
1040
}
1041
#endif
1042
int list_dump_getinfo_filedescriptor(int fd, list_dump_info_t *simclist_restrict info) {
1043
    int32_t terminator_head, terminator_tail;
1044
    uint32_t elemlen;
1045
    off_t hop;
1046
1047
1048
    /* version */
1049
    READ_ERRCHECK(fd, & info->version, sizeof(info->version));
1050
    info->version = ntohs(info->version);
1051
    if (info->version > SIMCLIST_DUMPFORMAT_VERSION) {
1052
        errno = EILSEQ;
1053
        return -1;
1054
    }
1055
1056
    /* timestamp */
1057
    READ_ERRCHECK(fd, & info->timestamp, sizeof(info->timestamp));
1058
    info->timestamp = hton64(info->timestamp);
1059
1060
    /* list terminator (to check thereafter) */
1061
    READ_ERRCHECK(fd, & terminator_head, sizeof(terminator_head));
1062
    terminator_head = ntohl(terminator_head);
1063
1064
    /* list size */
1065
    READ_ERRCHECK(fd, & info->list_size, sizeof(info->list_size));
1066
    info->list_size = ntohl(info->list_size);
1067
1068
    /* number of elements */
1069
    READ_ERRCHECK(fd, & info->list_numels, sizeof(info->list_numels));
1070
    info->list_numels = ntohl(info->list_numels);
1071
1072
    /* length of each element (for checking for consistency) */
1073
    READ_ERRCHECK(fd, & elemlen, sizeof(elemlen));
1074
    elemlen = ntohl(elemlen);
1075
1076
    /* list hash */
1077
    READ_ERRCHECK(fd, & info->list_hash, sizeof(info->list_hash));
1078
    info->list_hash = ntohl(info->list_hash);
1079
1080
    /* check consistency */
1081
    if (elemlen > 0) {
1082
        /* constant length, hop by size only */
1083
        hop = info->list_size;
1084
    } else {
1085
        /* non-constant length, hop by size + all element length blocks */
1086
        hop = info->list_size + elemlen*info->list_numels;
1087
    }
1088
    if (lseek(fd, hop, SEEK_CUR) == -1) {
1089
        return -1;
1090
    }
1091
1092
    /* read the trailing value and compare with terminator_head */
1093
    READ_ERRCHECK(fd, & terminator_tail, sizeof(terminator_tail));
1094
    terminator_tail = ntohl(terminator_tail);
1095
1096
    if (terminator_head == terminator_tail)
1097
        info->consistent = 1;
1098
    else
1099
        info->consistent = 0;
1100
1101
    return 0;
1102
}
1103
1104
int list_dump_getinfo_file(const char *simclist_restrict filename, list_dump_info_t *simclist_restrict info) {
1105
    int fd, ret;
1106
1107
    fd = open(filename, O_RDONLY, 0);
1108
    if (fd < 0) return -1;
1109
1110
    ret = list_dump_getinfo_filedescriptor(fd, info);
1111
    close(fd);
1112
1113
    return ret;
1114
}
1115
1116
int list_dump_filedescriptor(const list_t *simclist_restrict l, int fd, size_t *simclist_restrict len) {
1117
    struct list_entry_s *x;
1118
    void *ser_buf;
1119
    uint32_t bufsize;
1120
    struct timeval timeofday;
1121
    struct list_dump_header_s header;
1122
1123
    if (l->attrs.meter == NULL && l->attrs.serializer == NULL) {
1124
        errno = ENOTTY;
1125
        return -1;
1126
    }
1127
1128
    /****       DUMP FORMAT      ****
1129
1130
    [ ver   timestamp   |  totlen   numels  elemlen     hash    |   DATA ]
1131
1132
    where DATA can be:
1133
    @ for constant-size list (element size is constant; elemlen > 0)
1134
    [ elem    elem    ...    elem ]
1135
    @ for other lists (element size dictated by element_meter each time; elemlen <= 0)
1136
    [ size elem     size elem       ...     size elem ]
1137
1138
    all integers are encoded in NETWORK BYTE FORMAT
1139
    *****/
1140
1141
1142
    /* prepare HEADER */
1143
    /* version */
1144
    header.ver = htons( SIMCLIST_DUMPFORMAT_VERSION );
1145
1146
    /* timestamp */
1147
    gettimeofday(&timeofday, NULL);
1148
    header.timestamp = (int64_t)timeofday.tv_sec * 1000000 + (int64_t)timeofday.tv_usec;
1149
    header.timestamp = hton64(header.timestamp);
1150
1151
    header.rndterm = htonl((int32_t)get_random());
1152
1153
    /* total list size is postprocessed afterwards */
1154
1155
    /* number of elements */
1156
    header.numels = htonl(l->numels);
1157
1158
    /* include an hash, if possible */
1159
    if (l->attrs.hasher != NULL) {
1160
        if (htonl(list_hash(l, & header.listhash)) != 0) {
1161
            /* could not compute list hash! */
1162
            return -1;
1163
        }
1164
    } else {
1165
        header.listhash = htonl(0);
1166
    }
1167
1168
    header.totlistlen = header.elemlen = 0;
1169
1170
    /* leave room for the header at the beginning of the file */
1171
    if (lseek(fd, SIMCLIST_DUMPFORMAT_HEADERLEN, SEEK_SET) < 0) {
1172
        /* errno set by lseek() */
1173
        return -1;
1174
    }
1175
1176
    /* write CONTENT */
1177
    if (l->numels > 0) {
1178
        /* SPECULATE that the list has constant element size */
1179
1180
        if (l->attrs.serializer != NULL) {  /* user user-specified serializer */
1181
            /* get preliminary length of serialized element in header.elemlen */
1182
            ser_buf = l->attrs.serializer(l->head_sentinel->next->data, & header.elemlen);
1183
            free(ser_buf);
1184
            /* request custom serialization of each element */
1185
            for (x = l->head_sentinel->next; x != l->tail_sentinel; x = x->next) {
1186
                ser_buf = l->attrs.serializer(x->data, &bufsize);
1187
                header.totlistlen += bufsize;
1188
                if (header.elemlen != 0) {      /* continue on speculation */
1189
                    if (header.elemlen != bufsize) {
1190
                        free(ser_buf);
1191
                        /* constant element length speculation broken! */
1192
                        header.elemlen = 0;
1193
                        header.totlistlen = 0;
1194
                        x = l->head_sentinel;
1195
                        if (lseek(fd, SIMCLIST_DUMPFORMAT_HEADERLEN, SEEK_SET) < 0) {
1196
                            /* errno set by lseek() */
1197
                            return -1;
1198
                        }
1199
                        /* restart from the beginning */
1200
                        continue;
1201
                    }
1202
                    /* speculation confirmed */
1203
                    WRITE_ERRCHECK(fd, ser_buf, bufsize);
1204
                } else {                        /* speculation found broken */
1205
                    WRITE_ERRCHECK(fd, & bufsize, sizeof(size_t));
1206
                    WRITE_ERRCHECK(fd, ser_buf, bufsize);
1207
                }
1208
                free(ser_buf);
1209
            }
1210
        } else if (l->attrs.meter != NULL) {
1211
            header.elemlen = (uint32_t)l->attrs.meter(l->head_sentinel->next->data);
1212
1213
            /* serialize the element straight from its data */
1214
            for (x = l->head_sentinel->next; x != l->tail_sentinel; x = x->next) {
1215
                bufsize = l->attrs.meter(x->data);
1216
                header.totlistlen += bufsize;
1217
                if (header.elemlen != 0) {
1218
                    if (header.elemlen != bufsize) {
1219
                        /* constant element length speculation broken! */
1220
                        header.elemlen = 0;
1221
                        header.totlistlen = 0;
1222
                        x = l->head_sentinel;
1223
                        /* restart from the beginning */
1224
                        continue;
1225
                    }
1226
                    WRITE_ERRCHECK(fd, x->data, bufsize);
1227
                } else {
1228
                    WRITE_ERRCHECK(fd, &bufsize, sizeof(size_t));
1229
                    WRITE_ERRCHECK(fd, x->data, bufsize);
1230
                }
1231
            }
1232
        }
1233
        /* adjust endianness */
1234
        header.elemlen = htonl(header.elemlen);
1235
        header.totlistlen = htonl(header.totlistlen);
1236
    }
1237
1238
    /* write random terminator */
1239
    WRITE_ERRCHECK(fd, & header.rndterm, sizeof(header.rndterm));        /* list terminator */
1240
1241
1242
    /* write header */
1243
    lseek(fd, 0, SEEK_SET);
1244
1245
    WRITE_ERRCHECK(fd, & header.ver, sizeof(header.ver));                        /* version */
1246
    WRITE_ERRCHECK(fd, & header.timestamp, sizeof(header.timestamp));            /* timestamp */
1247
    WRITE_ERRCHECK(fd, & header.rndterm, sizeof(header.rndterm));                /* random terminator */
1248
1249
    WRITE_ERRCHECK(fd, & header.totlistlen, sizeof(header.totlistlen));          /* total length of elements */
1250
    WRITE_ERRCHECK(fd, & header.numels, sizeof(header.numels));                  /* number of elements */
1251
    WRITE_ERRCHECK(fd, & header.elemlen, sizeof(header.elemlen));                /* size of each element, or 0 for independent */
1252
    WRITE_ERRCHECK(fd, & header.listhash, sizeof(header.listhash));             /* list hash, or 0 for "ignore" */
1253
1254
1255
    /* possibly store total written length in "len" */
1256
    if (len != NULL) {
1257
        *len = sizeof(header) + ntohl(header.totlistlen);
1258
    }
1259
1260
    return 0;
1261
}
1262
1263
int list_restore_filedescriptor(list_t *simclist_restrict l, int fd, size_t *simclist_restrict len) {
1264
    struct list_dump_header_s header;
1265
    unsigned long cnt;
1266
    void *buf = NULL;
1267
    uint32_t elsize, totreadlen, totmemorylen;
1268
1269
    memset(& header, 0, sizeof(header));
1270
1271
    /* read header */
1272
1273
    /* version */
1274
    READ_ERRCHECK(fd, &header.ver, sizeof(header.ver));
1275
    header.ver = ntohs(header.ver);
1276
    if (header.ver != SIMCLIST_DUMPFORMAT_VERSION) {
1277
        errno = EILSEQ;
1278
        return -1;
1279
    }
1280
1281
    /* timestamp */
1282
    READ_ERRCHECK(fd, & header.timestamp, sizeof(header.timestamp));
1283
1284
    /* list terminator */
1285
    READ_ERRCHECK(fd, & header.rndterm, sizeof(header.rndterm));
1286
1287
    header.rndterm = ntohl(header.rndterm);
1288
1289
    /* total list size */
1290
    READ_ERRCHECK(fd, & header.totlistlen, sizeof(header.totlistlen));
1291
    header.totlistlen = ntohl(header.totlistlen);
1292
1293
    /* number of elements */
1294
    READ_ERRCHECK(fd, & header.numels, sizeof(header.numels));
1295
    header.numels = ntohl(header.numels);
1296
1297
    /* length of every element, or '0' = variable */
1298
    READ_ERRCHECK(fd, & header.elemlen, sizeof(header.elemlen));
1299
    header.elemlen = ntohl(header.elemlen);
1300
1301
    /* list hash, or 0 = 'ignore' */
1302
    READ_ERRCHECK(fd, & header.listhash, sizeof(header.listhash));
1303
    header.listhash = ntohl(header.listhash);
1304
1305
1306
    /* read content */
1307
    totreadlen = totmemorylen = 0;
1308
    if (header.elemlen > 0) {
1309
        /* elements have constant size = header.elemlen */
1310
        if (l->attrs.unserializer != NULL) {
1311
            /* use unserializer */
1312
            buf = malloc(header.elemlen);
1313
            if (buf == NULL) {
1314
                return -1;
1315
            }
1316
            for (cnt = 0; cnt < header.numels; cnt++) {
1317
                READ_ERRCHECK(fd, buf, header.elemlen);
1318
                list_append(l, l->attrs.unserializer(buf, & elsize));
1319
                totmemorylen += elsize;
1320
            }
1321
        } else {
1322
            /* copy verbatim into memory */
1323
            for (cnt = 0; cnt < header.numels; cnt++) {
1324
                buf = malloc(header.elemlen);
1325
                if (buf == NULL) {
1326
                    return -1;
1327
                }
1328
                READ_ERRCHECK(fd, buf, header.elemlen);
1329
                list_append(l, buf);
1330
            }
1331
            totmemorylen = header.numels * header.elemlen;
1332
        }
1333
        totreadlen = header.numels * header.elemlen;
1334
    } else {
1335
        /* elements have variable size. Each element is preceded by its size */
1336
        if (l->attrs.unserializer != NULL) {
1337
            /* use unserializer */
1338
            for (cnt = 0; cnt < header.numels; cnt++) {
1339
                READ_ERRCHECK(fd, & elsize, sizeof(elsize));
1340
                buf = malloc((size_t)elsize);
1341
                if (buf == NULL) {
1342
                    return -1;
1343
                }
1344
                READ_ERRCHECK(fd, buf, elsize);
1345
                totreadlen += elsize;
1346
                list_append(l, l->attrs.unserializer(buf, & elsize));
1347
                totmemorylen += elsize;
1348
            }
1349
        } else {
1350
            /* copy verbatim into memory */
1351
            for (cnt = 0; cnt < header.numels; cnt++) {
1352
                READ_ERRCHECK(fd, & elsize, sizeof(elsize));
1353
                buf = malloc(elsize);
1354
                if (buf == NULL) {
1355
                    return -1;
1356
                }
1357
                READ_ERRCHECK(fd, buf, elsize);
1358
                totreadlen += elsize;
1359
                list_append(l, buf);
1360
            }
1361
            totmemorylen = totreadlen;
1362
        }
1363
    }
1364
1365
    READ_ERRCHECK(fd, &elsize, sizeof(elsize));  /* read list terminator */
1366
    elsize = ntohl(elsize);
1367
1368
    /* possibly verify the list consistency */
1369
    /* wrt hash */
1370
    /* don't do that
1371
    if (header.listhash != 0 && header.listhash != list_hash(l)) {
1372
        errno = ECANCELED;
1373
        return -1;
1374
    }
1375
    */
1376
1377
    /* wrt header */
1378
    if (totreadlen != header.totlistlen && (int32_t)elsize == header.rndterm) {
1379
        errno = EPROTO;
1380
        return -1;
1381
    }
1382
1383
    /* wrt file */
1384
    if (lseek(fd, 0, SEEK_CUR) != lseek(fd, 0, SEEK_END)) {
1385
        errno = EPROTO;
1386
        return -1;
1387
    }
1388
1389
    if (len != NULL) {
1390
        *len = totmemorylen;
1391
    }
1392
1393
    return 0;
1394
}
1395
1396
int list_dump_file(const list_t *simclist_restrict l, const char *simclist_restrict filename, size_t *simclist_restrict len) {
1397
    int fd, mode;
1398
    size_t sizetoret;
1399
    mode = O_RDWR | O_CREAT | O_TRUNC;
1400
#ifndef _WIN32
1401
    mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1402
#endif
1403
    fd = open(filename, mode);
1404
    if (fd < 0) return -1;
1405
1406
    sizetoret = list_dump_filedescriptor(l, fd, len);
1407
    close(fd);
1408
1409
    return sizetoret;
1410
}
1411
1412
int list_restore_file(list_t *simclist_restrict l, const char *simclist_restrict filename, size_t *simclist_restrict len) {
1413
    int fd;
1414
    size_t totdata;
1415
1416
    fd = open(filename, O_RDONLY, 0);
1417
    if (fd < 0) return -1;
1418
1419
    totdata = list_restore_filedescriptor(l, fd, len);
1420
    close(fd);
1421
1422
    return totdata;
1423
}
1424
#endif /* ifdef SIMCLIST_DUMPRESTORE */
1425
1426
1427
161k
static int list_drop_elem(list_t *simclist_restrict l, struct list_entry_s *tmp, unsigned int pos) {
1428
161k
    if (tmp == NULL) return -1;
1429
1430
    /* fix mid pointer. This is wrt the PRE situation */
1431
161k
    if (l->numels % 2) {    /* now odd */
1432
        /* sort out the base case by hand */
1433
149k
        if (l->numels == 1) l->mid = NULL;
1434
9.89k
        else if (pos >= l->numels/2) l->mid = l->mid->prev;
1435
149k
    } else {                /* now even */
1436
11.4k
        if (pos < l->numels/2) l->mid = l->mid->next;
1437
11.4k
    }
1438
1439
161k
    tmp->prev->next = tmp->next;
1440
161k
    tmp->next->prev = tmp->prev;
1441
1442
    /* free what's to be freed */
1443
161k
    if (l->attrs.copy_data && tmp->data != NULL)
1444
0
        free(tmp->data);
1445
1446
161k
    if (l->spareels != NULL && l->spareelsnum < SIMCLIST_MAX_SPARE_ELEMS) {
1447
146k
        l->spareels[l->spareelsnum++] = tmp;
1448
146k
    } else {
1449
15.1k
        free(tmp);
1450
15.1k
    }
1451
1452
161k
    return 0;
1453
161k
}
1454
1455
/* ready-made comparators and meters */
1456
0
#define SIMCLIST_NUMBER_COMPARATOR(type)     int list_comparator_##type(const void *a, const void *b) { return( *(type *)a < *(type *)b) - (*(type *)a > *(type *)b); }
Unexecuted instantiation: list_comparator_int8_t
Unexecuted instantiation: list_comparator_int16_t
Unexecuted instantiation: list_comparator_int32_t
Unexecuted instantiation: list_comparator_int64_t
Unexecuted instantiation: list_comparator_uint8_t
Unexecuted instantiation: list_comparator_uint16_t
Unexecuted instantiation: list_comparator_uint32_t
Unexecuted instantiation: list_comparator_uint64_t
Unexecuted instantiation: list_comparator_float
Unexecuted instantiation: list_comparator_double
1457
1458
SIMCLIST_NUMBER_COMPARATOR(int8_t)
1459
SIMCLIST_NUMBER_COMPARATOR(int16_t)
1460
SIMCLIST_NUMBER_COMPARATOR(int32_t)
1461
SIMCLIST_NUMBER_COMPARATOR(int64_t)
1462
1463
SIMCLIST_NUMBER_COMPARATOR(uint8_t)
1464
SIMCLIST_NUMBER_COMPARATOR(uint16_t)
1465
SIMCLIST_NUMBER_COMPARATOR(uint32_t)
1466
SIMCLIST_NUMBER_COMPARATOR(uint64_t)
1467
1468
SIMCLIST_NUMBER_COMPARATOR(float)
1469
SIMCLIST_NUMBER_COMPARATOR(double)
1470
1471
0
int list_comparator_string(const void *a, const void *b) { return strcmp((const char *)b, (const char *)a); }
1472
1473
/* ready-made metric functions */
1474
0
#define SIMCLIST_METER(type)        size_t list_meter_##type(const void *el) { if (el) { /* kill compiler whinge */ } return sizeof(type); }
Unexecuted instantiation: list_meter_int8_t
Unexecuted instantiation: list_meter_int16_t
Unexecuted instantiation: list_meter_int32_t
Unexecuted instantiation: list_meter_int64_t
Unexecuted instantiation: list_meter_uint8_t
Unexecuted instantiation: list_meter_uint16_t
Unexecuted instantiation: list_meter_uint32_t
Unexecuted instantiation: list_meter_uint64_t
Unexecuted instantiation: list_meter_float
Unexecuted instantiation: list_meter_double
1475
1476
SIMCLIST_METER(int8_t)
1477
SIMCLIST_METER(int16_t)
1478
SIMCLIST_METER(int32_t)
1479
SIMCLIST_METER(int64_t)
1480
1481
SIMCLIST_METER(uint8_t)
1482
SIMCLIST_METER(uint16_t)
1483
SIMCLIST_METER(uint32_t)
1484
SIMCLIST_METER(uint64_t)
1485
1486
SIMCLIST_METER(float)
1487
SIMCLIST_METER(double)
1488
1489
0
size_t list_meter_string(const void *el) { return strlen((const char *)el) + 1; }
1490
1491
/* ready-made hashing functions */
1492
0
#define SIMCLIST_HASHCOMPUTER(type)    list_hash_t list_hashcomputer_##type(const void *el) { return (list_hash_t)(*(type *)el); }
Unexecuted instantiation: list_hashcomputer_int8_t
Unexecuted instantiation: list_hashcomputer_int16_t
Unexecuted instantiation: list_hashcomputer_int32_t
Unexecuted instantiation: list_hashcomputer_int64_t
Unexecuted instantiation: list_hashcomputer_uint8_t
Unexecuted instantiation: list_hashcomputer_uint16_t
Unexecuted instantiation: list_hashcomputer_uint32_t
Unexecuted instantiation: list_hashcomputer_uint64_t
Unexecuted instantiation: list_hashcomputer_float
Unexecuted instantiation: list_hashcomputer_double
1493
1494
SIMCLIST_HASHCOMPUTER(int8_t)
1495
SIMCLIST_HASHCOMPUTER(int16_t)
1496
SIMCLIST_HASHCOMPUTER(int32_t)
1497
SIMCLIST_HASHCOMPUTER(int64_t)
1498
1499
SIMCLIST_HASHCOMPUTER(uint8_t)
1500
SIMCLIST_HASHCOMPUTER(uint16_t)
1501
SIMCLIST_HASHCOMPUTER(uint32_t)
1502
SIMCLIST_HASHCOMPUTER(uint64_t)
1503
1504
SIMCLIST_HASHCOMPUTER(float)
1505
SIMCLIST_HASHCOMPUTER(double)
1506
1507
0
list_hash_t list_hashcomputer_string(const void *el) {
1508
0
    size_t l;
1509
0
    list_hash_t hash = 123;
1510
0
    const char *str = (const char *)el;
1511
0
    char plus;
1512
1513
0
    for (l = 0; str[l] != '\0'; l++) {
1514
0
        if (l) plus = hash ^ str[l];
1515
0
        else plus = hash ^ (str[l] - str[0]);
1516
0
        hash += (plus << (CHAR_BIT * (l % sizeof(list_hash_t))));
1517
0
    }
1518
1519
0
    return hash;
1520
0
}
1521
1522
1523
#ifndef NDEBUG
1524
static int list_repOk(const list_t *simclist_restrict l) {
1525
    int ok, i;
1526
    struct list_entry_s *s;
1527
1528
    ok = (l != NULL) && (
1529
            /* head/tail checks */
1530
            (l->head_sentinel != NULL && l->tail_sentinel != NULL) &&
1531
                (l->head_sentinel != l->tail_sentinel) && (l->head_sentinel->prev == NULL && l->tail_sentinel->next == NULL) &&
1532
            /* empty list */
1533
            (l->numels > 0 || (l->mid == NULL && l->head_sentinel->next == l->tail_sentinel && l->tail_sentinel->prev == l->head_sentinel)) &&
1534
            /* spare elements checks */
1535
            l->spareelsnum <= SIMCLIST_MAX_SPARE_ELEMS
1536
         );
1537
1538
    if (!ok) return 0;
1539
1540
    if (l->numels >= 1) {
1541
        /* correct referencing */
1542
        for (i = -1, s = l->head_sentinel; i < (int)(l->numels-1)/2 && s->next != NULL; i++, s = s->next) {
1543
            if (s->next->prev != s) break;
1544
        }
1545
        ok = (i == (int)(l->numels-1)/2 && l->mid == s);
1546
        if (!ok) return 0;
1547
        for (; s->next != NULL; i++, s = s->next) {
1548
            if (s->next->prev != s) break;
1549
        }
1550
        ok = (i == (int)l->numels && s == l->tail_sentinel);
1551
    }
1552
1553
    return ok;
1554
}
1555
1556
static int list_attrOk(const list_t *simclist_restrict l) {
1557
    int ok;
1558
1559
    ok = (l->attrs.copy_data == 0 || l->attrs.meter != NULL);
1560
    return ok;
1561
}
1562
1563
#endif
1564