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