1 // $Id$
2
3 /*
4 Classic skip list library
5
6 > Created (Julienne Walker): April 11, 2004
7 > Updated (Julienne Walker): August 19, 2005
8
9 based on code released to the publicdata domain by Julienne Walker
10 */
11 #include "jsw_rand.h"
12 #include "jsw_slib.h"
13 #ifdef WITH_SHARED_TABLES
14 #include "shared.h"
15 #endif
16
17 #ifdef __cplusplus
18 #include <climits>
19 #include <cstdlib>
20
21 using std::size_t;
22 #else
23 #include <limits.h>
24 #include <stdlib.h>
25 #endif
26
27 #define DUMPER 1
28
29 typedef struct jsw_node {
30 ctable_BaseRow *row; /* Data row with combined key */
31 size_t height; /* Column height of this node */
32 struct jsw_node *next[]; /* Dynamic array of next links */
33 } jsw_node_t;
34
35 // dynamic shared elements
36 typedef struct jsw_pub {
37 jsw_node_t *head; /* Full height header node */
38 size_t curh; /* Tallest available column */
39 size_t size; /* Number of row at level 0 */
40 } jsw_pub_t;
41
42 // statically defined and private elements
43 struct jsw_skip {
44 int id; /* 0 for owner, pid for shared reader */
45 jsw_pub_t *publicdata; /* shared data */
46 jsw_node_t *curl; /* Current link for traversal */
47 size_t maxh; /* Tallest possible column */
48 cmp_f cmp; /* User defined row compare function */
49 #ifdef WITH_SHARED_TABLES
50 shm_t *share; /* Shared memory this table belongs to */
51 #else
52 void *share;
53 #endif
54 jsw_node_t **fix; /* Update array */
55 };
56
57 /*
58 Weighted random level with probability 1/2.
59 (For better distribution, modify with 1/3)
60
61 Implements a tuned bit stream algorithm.
62 */
63 INLINE
rlevel(size_t max)64 static size_t rlevel ( size_t max )
65 {
66 static size_t bits = 0;
67 static size_t reset = 0;
68 size_t h, found = 0;
69
70 for ( h = 0; !found; h++ ) {
71 if ( reset == 0 ) {
72 bits = jsw_rand();
73 // printf("bits %lx\n", bits);
74
75 // reset = sizeof ( size_t ) * CHAR_BIT - 1;
76 reset = 31;
77
78 // printf("bits %lx, reset %d\n", (long)bits, (int)reset);
79 }
80
81 /*
82 For 1/3 change to:
83
84 found = bits % 3;
85 bits = bits / 3;
86 */
87 found = bits & 1;
88 bits = bits >> 1;
89 --reset;
90 // printf("found %d bits %lx reset %d h %d\n", (int)found, (long)bits, (int)reset, (int)h);
91 }
92 // printf("rlh %d\n", (int) h);
93
94 if ( h >= max )
95 // printf("big h %d\n", (int) h);
96 h = max - 1;
97
98 return h;
99 }
100
101 //
102 // new_node - construct an empty new node, does not make a copy of the row
103 //
104 INLINE
new_node(ctable_BaseRow * row,size_t height,void * share)105 static jsw_node_t *new_node ( ctable_BaseRow *row, size_t height, void *share )
106 {
107 jsw_node_t *node;
108 size_t i;
109
110 #ifdef WITH_SHARED_TABLES
111 if(share) {
112 node = (jsw_node_t *)shmalloc ((shm_t*)share, sizeof (jsw_node_t) + height * sizeof (jsw_node_t *) );
113 if(!node) {
114 //if(DUMPER) shmdump(share);
115 Tcl_Panic("Can't allocate shared memory for skiplist");
116 }
117 } else
118 #endif
119 node = (jsw_node_t *)ckalloc ( sizeof (jsw_node_t) + height * sizeof (jsw_node_t *) );
120
121 node->row = row;
122
123 node->height = height;
124
125 for ( i = 0; i < height; i++ )
126 node->next[i] = NULL;
127
128 return node;
129 }
130
131 //
132 // free_node - free a skip list node but not the row associated with it
133 //
134 INLINE
free_node(jsw_node_t * node,void * share)135 static void free_node ( jsw_node_t *node, void *share )
136 {
137 #ifdef WITH_SHARED_TABLES
138 if(share)
139 shmfree((shm_t *)share, (char *)node);
140 else
141 #endif
142 ckfree ( (char *)node );
143 }
144
145 //
146 // locate - find an existing row, or the position before where it would be
147 //
148 INLINE
locate(jsw_skip_t * skip,ctable_BaseRow * row)149 static jsw_node_t *locate ( jsw_skip_t *skip, ctable_BaseRow *row )
150 {
151 jsw_node_t *p = skip->publicdata->head;
152 cmp_f cmp = skip->cmp;
153 size_t i;
154 jsw_node_t *next;
155
156 for ( i = skip->publicdata->curh; i < (size_t)-1; i-- ) {
157 while ( (next = p->next[i]) != NULL ) {
158 if ( cmp ( row, next->row ) <= 0 ) {
159 break;
160 }
161
162 p = next;
163 }
164
165 skip->fix[i] = p;
166 }
167
168 return p;
169 }
170
171 //
172 // jsw_private - return a private version of a skiplist in shared memory
173 //
jsw_private(jsw_skip_t * skip,size_t max,cmp_f cmp,void * share,int id)174 jsw_skip_t *jsw_private ( jsw_skip_t *skip, size_t max, cmp_f cmp, void *share, int id )
175 {
176 jsw_skip_t *new_skip = skip;
177
178 if(id == 0) {
179 // If this is a new skiplist, initialize skiplist structure
180 skip->id = id;
181 skip->fix = NULL;
182 skip->curl = NULL;
183 } else if(skip->id != id) {
184 // If this isn't my skiplist, allocate new skiplist structure
185 new_skip = (jsw_skip_t *)ckalloc(sizeof *skip);
186
187 new_skip->curl = skip->curl;
188 new_skip->id = id;
189 new_skip->fix = NULL;
190 new_skip->publicdata = skip->publicdata;
191
192 skip = new_skip;
193 }
194
195 // initialise dynamic private data if necessary
196 if (!skip->fix) {
197 skip->fix = (jsw_node_t **)ckalloc ( max * sizeof *skip->fix );
198 }
199
200 // Fill in static private data
201 skip->maxh = max;
202 #ifdef WITH_SHARED_TABLES
203 skip->share = (shm_t*) share;
204 #else
205 skip->share = share;
206 #endif
207 skip->cmp = cmp;
208 return skip;
209 }
210
211 //
212 // free private copy of skiplist only
213 //
jsw_free_private_copy(jsw_skip_t * skip)214 void jsw_free_private_copy(jsw_skip_t *skip)
215 {
216 if(skip->fix)
217 ckfree((char *)skip->fix);
218 ckfree((char *)skip);
219 }
220
221 //
222 // clone skiplist if needed
223 //
jsw_private_copy(jsw_skip_t * skip,int id,cmp_f cmp)224 jsw_skip_t *jsw_private_copy(jsw_skip_t *skip, int id, cmp_f cmp)
225 {
226 if(skip->id != id)
227 return jsw_private(skip, skip->maxh, cmp?cmp:skip->cmp, skip->share, id);
228 return NULL;
229 }
230
231 //
232 // jsw_sinit - initialize a skip list
233 //
jsw_sinit(jsw_skip_t * skip,size_t max,cmp_f cmp,void * share)234 void jsw_sinit ( jsw_skip_t *skip, size_t max, cmp_f cmp, void *share)
235 {
236 #ifdef WITH_SHARED_TABLES
237 if(share) {
238 skip->publicdata = (jsw_pub_t *)shmalloc ( (shm_t *)share, sizeof *skip->publicdata );
239 if(!skip) {
240 //if(DUMPER) shmdump(share);
241 Tcl_Panic("Can't allocate shared memory for skiplist");
242 }
243 } else
244 #endif
245 skip->publicdata = (jsw_pub_t *)ckalloc ( sizeof *skip->publicdata );
246
247 skip->publicdata->head = new_node ( NULL, ++max, share );
248
249 skip->publicdata->curh = 0;
250 skip->publicdata->size = 0;
251
252 // We're creating this skiplist, our "id" is zero
253 // (now fills in skip->maxh, skip->curl)
254 jsw_private(skip, max, cmp, share, 0);
255
256 jsw_seed ( jsw_time_seed() );
257 }
258
259 //
260 // jsw_snew - allocate and initialize a new skip list
261 //
jsw_snew(size_t max,cmp_f cmp,void * share)262 jsw_skip_t *jsw_snew ( size_t max, cmp_f cmp, void *share)
263 {
264 jsw_skip_t *skip;
265
266 #ifdef WITH_SHARED_TABLES
267 if(share) {
268 skip = (jsw_skip_t *)shmalloc ( (shm_t *)share, sizeof *skip );
269 if(!skip) {
270 //if(DUMPER) shmdump(share);
271 Tcl_Panic("Can't allocate shared memory for skiplist");
272 }
273 } else
274 #endif
275 skip = (jsw_skip_t *)ckalloc ( sizeof *skip );
276
277 jsw_sinit (skip, max, cmp, share);
278
279 return skip;
280 }
281
282 //
283 // jsw_sdelete_skiplist - delete the entire skip list
284 //
285 // you have to delete your own row data
286 //
jsw_sdelete_skiplist(jsw_skip_t * skip,int final)287 void jsw_sdelete_skiplist ( jsw_skip_t *skip, int final )
288 {
289 jsw_node_t *it = skip->publicdata->head->next[0];
290 jsw_node_t *save;
291
292 while ( it != NULL ) {
293 save = it->next[0];
294 #ifdef WITH_SHARED_TABLES
295 if(!final || !skip->share)
296 #endif
297 free_node ( it, skip->share );
298 it = save;
299 }
300
301 free_node ( skip->publicdata->head, skip->share );
302
303 ckfree ( (char *)skip->fix );
304
305 #ifdef WITH_SHARED_TABLES
306 if(skip->share) {
307 if(!final)
308 shmfree(skip->share, (char *)skip);
309 } else
310 #endif
311 ckfree ( (char *)skip );
312 }
313
314 //
315 // jsw_sfind - given a skip list and a row, return the corresponding
316 // skip list node pointer or NULL if none is found.
317 //
318 INLINE
jsw_sfind(jsw_skip_t * skip,ctable_BaseRow * row)319 void *jsw_sfind ( jsw_skip_t *skip, ctable_BaseRow *row )
320 {
321 jsw_node_t *p = locate ( skip, row )->next[0];
322
323 skip->curl = p;
324
325 if ( p != NULL && skip->cmp ( row, p->row ) == 0 )
326 return p;
327
328 return NULL;
329 }
330
331 //
332 // jsw_sfind_equal_or_greater - given a skip list and a row, return the
333 // corresponding skip list node pointer that matches the specified
334 // row or exceeds it.
335 //
336 INLINE
jsw_sfind_equal_or_greater(jsw_skip_t * skip,ctable_BaseRow * row)337 void *jsw_sfind_equal_or_greater ( jsw_skip_t *skip, ctable_BaseRow *row )
338 {
339 jsw_node_t *p = locate ( skip, row )->next[0];
340 cmp_f cmp = skip->cmp;
341
342 //printf("find_equal_or_greater row %8lx, p %8lx ", (long unsigned int)row, (long unsigned int)p);
343
344 while (p !=NULL && cmp (p->row, row) < 0) {
345 //printf("p->%8lx ", (long unsigned int)p);
346 p = p->next[0];
347 }
348
349 //printf(" *%d* ", cmp (p->row, row));
350 //printf("skip->curl %8lx\n", (long unsigned int)p);
351 skip->curl = p;
352
353 return p;
354 }
355
356 //
357 // jsw_findlast - find a row with the lexically highest key in the table
358 //
359 INLINE
jsw_findlast(jsw_skip_t * skip)360 void *jsw_findlast ( jsw_skip_t *skip)
361 {
362 jsw_node_t *p = skip->publicdata->head;
363 size_t i;
364 jsw_node_t *next;
365
366 for ( i = skip->publicdata->curh; i < (size_t)-1; i-- ) {
367 while ( (next = p->next[i]) != NULL ) {
368 p = next;
369 }
370 }
371
372 skip->curl = p;
373 return p;
374 }
375
376 //
377 // jsw_sinsert - insert row into the skip list if it's not already there
378 //
379 // forces there to be no duplicate row by failing if a matching row is found
380 //
381 INLINE
jsw_sinsert(jsw_skip_t * skip,ctable_BaseRow * row)382 int jsw_sinsert ( jsw_skip_t *skip, ctable_BaseRow *row )
383 {
384 // void *p = locate ( skip, row )->row;
385 jsw_node_t *p = locate ( skip, row )->next[0];
386 cmp_f cmp = skip->cmp;
387
388 // if we got something and it compares the same, it's already there
389 if ( p != NULL && cmp ( row, p->row ) == 0 ) {
390 return 0;
391 } else {
392 // it's new
393 size_t h = rlevel ( skip->maxh );
394 jsw_node_t *it;
395
396 it = new_node ( row, h, skip->share );
397
398 /* Raise height if necessary */
399 if ( h > skip->publicdata->curh ) {
400 // printf("raising the height from %d to %d, size %d\n", (int)skip->publicdata->curh, (int)h, (int)skip->publicdata->size);
401 h = ++skip->publicdata->curh;
402 skip->fix[h] = skip->publicdata->head;
403 }
404
405 /* Build skip links */
406 while ( --h < (size_t)-1 ) {
407 it->next[h] = skip->fix[h]->next[h];
408 skip->fix[h]->next[h] = it;
409 }
410 }
411
412 skip->publicdata->size++;
413 return 1;
414 }
415
416 //
417 // jsw_sinsert_linked - insert row into the skip list if it's not already
418 // there. if it is already there, link this row into the skip list.
419 //
420 INLINE
jsw_sinsert_linked(jsw_skip_t * skip,ctable_BaseRow * row,int nodeIdx,int unique)421 int jsw_sinsert_linked ( jsw_skip_t *skip, ctable_BaseRow *row , int nodeIdx, int unique)
422 {
423 // void *p = locate ( skip, row )->row;
424 jsw_node_t *p = locate ( skip, row )->next[0];
425
426 if ( p != NULL && skip->cmp ( row, p->row ) == 0 ) {
427 // we found a matching skip list entry
428
429 // if dups aren't allowed, don't do anything and return 0
430 if (unique) {
431 return 0;
432 }
433
434 // dups are allowed, insert this guy
435 ctable_ListInsertHead (&p->row, row, nodeIdx);
436 } else {
437 // no matching skip list entry
438
439 // insert the new node
440 size_t h = rlevel ( skip->maxh );
441 jsw_node_t *it;
442
443 // printf("h %d\n", (int)h);
444
445 it = new_node ( row, h, skip->share );
446
447 // Throw away the row we just inserted with new_node! Yes, we mean to do this.
448 ctable_ListInit (&it->row, __FILE__, __LINE__);
449
450 ctable_ListInsertHead (&it->row, row, nodeIdx);
451
452 /* Raise height if necessary */
453 if ( h > skip->publicdata->curh ) {
454 // printf("raising the height from %d to %d, size %d\n", (int)skip->publicdata->curh, (int)h, (int)skip->publicdata->size);
455 h = ++skip->publicdata->curh;
456 skip->fix[h] = skip->publicdata->head;
457 }
458
459 /* Build skip links */
460 while ( --h < (size_t)-1 ) {
461 it->next[h] = skip->fix[h]->next[h];
462 skip->fix[h]->next[h] = it;
463 }
464 }
465
466 skip->publicdata->size++;
467 return 1;
468 }
469
470 //
471 // jsw_serase - locate an row in the skip list. if it exists, delete it.
472 //
473 // return 1 if it deleted and 0 if no row matched
474 //
475 // you have to release the node externally to this
476 //
jsw_serase(jsw_skip_t * skip,ctable_BaseRow * row)477 int jsw_serase ( jsw_skip_t *skip, ctable_BaseRow *row )
478 {
479 jsw_node_t *p = locate ( skip, row )->next[0];
480
481 if ( p == NULL || skip->cmp ( row, p->row ) != 0 )
482 return 0;
483 else {
484 size_t i;
485
486 // fix skip list pointers that point directly to me by traversing
487 // the fix list of stuff from the locate
488 for ( i = 0; i < skip->publicdata->curh; i++ ) {
489 if ( skip->fix[i]->next[i] != p )
490 break;
491
492 skip->fix[i]->next[i] = p->next[i];
493 }
494
495 // free the node
496 free_node ( p, skip->share );
497 skip->publicdata->size--;
498
499 /* Lower height if necessary */
500 while ( skip->publicdata->curh > 0 ) {
501 if ( skip->publicdata->head->next[skip->publicdata->curh - 1] != NULL )
502 break;
503
504 --skip->publicdata->curh;
505 }
506 }
507
508 /* Erasure invalidates traversal markers */
509 jsw_sreset ( skip );
510
511 return 1;
512 }
513
514 void
jsw_dump_node(const char * s,jsw_skip_t * skip,jsw_node_t * p,int indexNumber)515 jsw_dump_node (const char *s, jsw_skip_t *skip, jsw_node_t *p, int indexNumber) {
516 int height;
517 int i;
518 ctable_BaseRow *walkRow;
519 cmp_f cmp = skip->cmp;
520
521 height = p->height;
522 printf("%8lx '%s' height %d\n list ", (long unsigned int)p, s, height);
523
524 if (indexNumber < 0) {
525 printf ("(head)");
526 } else {
527 CTABLE_LIST_FOREACH (p->row, walkRow, indexNumber) {
528 printf("%8lx ", (long unsigned int)walkRow);
529 if ( cmp ( p->row, walkRow ) != 0 ) {
530 Tcl_Panic ("index hosed - value in dup list doesn't match others, p->row == 0x%08lx, walkRow == 0x%08lx", (long)p->row, (long)walkRow);
531 }
532 }
533 }
534 printf("\n");
535
536 for ( i = 0; i < height; i++ ) {
537 printf ("%8lx ", (long unsigned int)p->next[i]);
538 }
539 printf("\n");
540 }
541
542 void
jsw_dump(const char * s,jsw_skip_t * skip,int indexNumber)543 jsw_dump (const char *s, jsw_skip_t *skip, int indexNumber) {
544 jsw_node_t *p = skip->curl;
545
546 jsw_dump_node (s, skip, p, indexNumber);
547 }
548
549 void
jsw_dump_head(jsw_skip_t * skip)550 jsw_dump_head (jsw_skip_t *skip) {
551 jsw_node_t *p = skip->publicdata->head;
552
553 jsw_dump_node ("HEAD", skip, p, -1);
554 }
555
556 //
557 // jsw_ssize - return the size of the skip table
558 //
jsw_ssize(jsw_skip_t * skip)559 size_t jsw_ssize ( jsw_skip_t *skip )
560 {
561 return skip->publicdata->size;
562 }
563
564 //
565 // jsw_reset - invalidate traversal markers by resetting the current link
566 // for traversal to the first element in the list
567 //
jsw_sreset(jsw_skip_t * skip)568 void jsw_sreset ( jsw_skip_t *skip )
569 {
570 skip->curl = skip->publicdata->head->next[0];
571 }
572
573 //
574 // jsw_sreset_head - like jsw_sreset except points to head instead of
575 // what head points to so jsw_snext can be called in a while loop to traverse
576 //
jsw_sreset_head(jsw_skip_t * skip)577 void jsw_sreset_head ( jsw_skip_t *skip )
578 {
579 skip->curl = skip->publicdata->head;
580 }
581
582 //
583 // jsw_srow - get row pointed to by the the current link or NULL if none
584 //
585 INLINE
jsw_srow(jsw_skip_t * skip)586 ctable_BaseRow *jsw_srow ( jsw_skip_t *skip )
587 {
588 return skip->curl == NULL ? NULL : skip->curl->row;
589 }
590
591 //
592 // jsw_snext - move the current link to the next row, returning 1 if there
593 // is a next row and a 0 if there isn't
594 //
595 INLINE int
jsw_snext(jsw_skip_t * skip)596 jsw_snext ( jsw_skip_t *skip )
597 {
598 jsw_node_t *curl = skip->curl;
599 jsw_node_t *next = curl->next[0];
600 return ( skip->curl = next ) != NULL;
601 }
602
603 // vim: set ts=8 sw=4 sts=4 noet :
604