]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/idl.c
Set lock detector to DEFAULT, not NORUN.
[openldap] / servers / slapd / back-bdb / idl.c
1 /* idl.c - ldap id list handling routines */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11 #include <ac/string.h>
12
13 #include "back-bdb.h"
14 #include "idl.h"
15
16 #define IDL_MAX(x,y)    ( x > y ? x : y )
17 #define IDL_MIN(x,y)    ( x < y ? x : y )
18
19 #define IDL_CMP(x,y)    ( x < y ? -1 : ( x > y ? 1 : 0 ) )
20
21 #ifndef IDL_DEBUG
22         /* enable basic checks for now */
23 #define IDL_DEBUG 1
24 #endif
25
26 #if IDL_DEBUG > 0
27 static void idl_check( ID *ids )
28 {
29         if( BDB_IDL_IS_RANGE( ids ) ) {
30                 assert( BDB_IDL_RANGE_FIRST(ids) <= BDB_IDL_RANGE_LAST(ids) );
31         } else {
32                 ID i;
33                 for( i=1; i < ids[0]; i++ ) {
34                         assert( ids[i+1] > ids[i] );
35                 }
36         }
37 }
38
39 #if IDL_DEBUG > 1
40 static void idl_dump( ID *ids )
41 {
42         if( BDB_IDL_IS_RANGE( ids ) ) {
43                 Debug( LDAP_DEBUG_ANY,
44                         "IDL: range ( %ld - %ld )\n",
45                         (long) BDB_IDL_RANGE_FIRST( ids ),
46                         (long) BDB_IDL_RANGE_LAST( ids ) );
47
48         } else {
49                 ID i;
50                 Debug( LDAP_DEBUG_ANY, "IDL: size %ld", (long) ids[0], 0, 0 );
51
52                 for( i=1; i<=ids[0]; i++ ) {
53                         if( i % 16 == 1 ) {
54                                 Debug( LDAP_DEBUG_ANY, "\n", 0, 0, 0 );
55                         }
56                         Debug( LDAP_DEBUG_ANY, "  %02lx", (long) ids[i], 0, 0 );
57                 }
58
59                 Debug( LDAP_DEBUG_ANY, "\n", 0, 0, 0 );
60         }
61
62         idl_check( ids );
63 }
64 #endif /* IDL_DEBUG > 1 */
65 #endif /* IDL_DEBUG > 0 */
66
67 unsigned bdb_idl_search( ID *ids, ID id )
68 {
69 #define IDL_BINARY_SEARCH 1
70 #ifdef IDL_BINARY_SEARCH
71         /*
72          * binary search of id in ids
73          * if found, returns position of id
74          * if not found, returns first postion greater than id
75          */
76         unsigned base = 0;
77         unsigned cursor = 0;
78         int val = 0;
79         unsigned n = ids[0];
80
81 #if IDL_DEBUG > 0
82         idl_check( ids );
83 #endif
84
85         while( 0 < n ) {
86                 int pivot = n >> 1;
87                 cursor = base + pivot;
88                 val = IDL_CMP( id, ids[cursor + 1] );
89
90                 if( val < 0 ) {
91                         n = pivot;
92
93                 } else if ( val > 0 ) {
94                         base = cursor + 1;
95                         n -= pivot + 1;
96
97                 } else {
98                         return cursor + 1;
99                 }
100         }
101         
102         if( val > 0 ) {
103                 return cursor + 2;
104         } else {
105                 return cursor + 1;
106         }
107
108 #else
109         /* (reverse) linear search */
110         int i;
111
112 #if IDL_DEBUG > 0
113         idl_check( ids );
114 #endif
115
116         for( i=ids[0]; i; i-- ) {
117                 if( id > ids[i] ) {
118                         break;
119                 }
120         }
121
122         return i+1;
123 #endif
124 }
125
126 int bdb_idl_insert( ID *ids, ID id )
127 {
128         unsigned x = bdb_idl_search( ids, id );
129
130 #if IDL_DEBUG > 1
131         Debug( LDAP_DEBUG_ANY, "insert: %04lx at %d\n", (long) id, x, 0 );
132         idl_dump( ids );
133 #elif IDL_DEBUG > 0
134         idl_check( ids );
135 #endif
136
137         assert( x > 0 );
138
139         if( x < 1 ) {
140                 /* internal error */
141                 return -2;
142         }
143
144         if ( x <= ids[0] && ids[x] == id ) {
145                 /* duplicate */
146                 return -1;
147         }
148
149         if ( ++ids[0] >= BDB_IDL_DB_MAX ) {
150                 if( id < ids[1] ) {
151                         ids[1] = id;
152                         ids[2] = ids[ids[0]-1];
153                 } else if ( ids[ids[0]-1] < id ) {
154                         ids[2] = id;
155                 } else {
156                         ids[2] = ids[ids[0]-1];
157                 }
158                 ids[0] = NOID;
159         
160         } else {
161                 /* insert id */
162                 AC_MEMCPY( &ids[x+1], &ids[x], (ids[0]-x) * sizeof(ID) );
163                 ids[x] = id;
164         }
165
166 #if IDL_DEBUG > 1
167         idl_dump( ids );
168 #elif IDL_DEBUG > 0
169         idl_check( ids );
170 #endif
171
172         return 0;
173 }
174
175 static int idl_delete( ID *ids, ID id )
176 {
177         unsigned x = bdb_idl_search( ids, id );
178
179 #if IDL_DEBUG > 1
180         Debug( LDAP_DEBUG_ANY, "delete: %04lx at %d\n", (long) id, x, 0 );
181         idl_dump( ids );
182 #elif IDL_DEBUG > 0
183         idl_check( ids );
184 #endif
185
186         assert( x > 0 );
187
188         if( x <= 0 ) {
189                 /* internal error */
190                 return -2;
191         }
192
193         if( x > ids[0] || ids[x] != id ) {
194                 /* not found */
195                 return -1;
196
197         } else if ( --ids[0] == 0 ) {
198                 if( x != 1 ) {
199                         return -3;
200                 }
201
202         } else {
203                 AC_MEMCPY( &ids[x], &ids[x+1], (1+ids[0]-x) * sizeof(ID) );
204         }
205
206 #if IDL_DEBUG > 1
207         idl_dump( ids );
208 #elif IDL_DEBUG > 0
209         idl_check( ids );
210 #endif
211
212         return 0;
213 }
214
215 int
216 bdb_idl_fetch_key(
217         BackendDB       *be,
218         DB                      *db,
219         DB_TXN          *tid,
220         DBT                     *key,
221         ID                      *ids )
222 {
223         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
224         int rc;
225         DBT data;
226
227         assert( ids != NULL );
228
229         DBTzero( &data );
230
231 #ifdef BDB_IDL_MULTI
232         {
233                 DBC *cursor;
234                 ID buf[BDB_IDL_UM_SIZE];
235                 ID *i, *j;
236                 void *ptr;
237                 size_t len;
238                 int rc2;
239                 int flags = bdb->bi_db_opflags | DB_MULTIPLE;
240                 data.data = buf;
241                 data.ulen = BDB_IDL_UM_SIZEOF;
242                 data.flags = DB_DBT_USERMEM;
243
244                 if ( tid )
245                         flags |= DB_RMW;
246
247                 rc = db->cursor( db, tid, &cursor, bdb->bi_db_opflags );
248                 if( rc != 0 ) {
249                         Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
250                                 "cursor failed: %s (%d)\n", db_strerror(rc), rc, 0 );
251                         return rc;
252                 }
253                 rc = cursor->c_get( cursor, key, &data, flags | DB_SET );
254                 if (rc == 0) {
255                         i = ids;
256                         while (rc == 0) {
257                                 DB_MULTIPLE_INIT( ptr, &data );
258                                 while (ptr) {
259                                         DB_MULTIPLE_NEXT(ptr, &data, j, len);
260                                         if (j) {
261                                                 ++i;
262                                                 AC_MEMCPY( i, j, sizeof(ID) );
263                                         }
264                                 }
265                                 rc = cursor->c_get( cursor, key, &data, flags | DB_NEXT_DUP );
266                         }
267                         if ( rc == DB_NOTFOUND ) rc = 0;
268                         ids[0] = i - ids;
269                         /* On disk, a range is denoted by 0 in the first element */
270                         if (ids[1] == 0) {
271                                 if (ids[0] != BDB_IDL_RANGE_SIZE) {
272                                         Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
273                                                 "range size mismatch: expected %ld, got %ld\n",
274                                                 BDB_IDL_RANGE_SIZE, ids[0], 0 );
275                                         cursor->c_close( cursor );
276                                         return -1;
277                                 }
278                                 BDB_IDL_RANGE( ids, ids[2], ids[3] );
279                         }
280                         data.size = BDB_IDL_SIZEOF(ids);
281                 }
282                 rc2 = cursor->c_close( cursor );
283                 if (rc2) {
284                         Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
285                                 "close failed: %s (%d)\n", db_strerror(rc2), rc2, 0 );
286                         return rc2;
287                 }
288         }
289 #else
290         data.data = ids;
291         data.ulen = BDB_IDL_UM_SIZEOF;
292         data.flags = DB_DBT_USERMEM;
293         /* fetch it */
294         rc = db->get( db, tid, key, &data, bdb->bi_db_opflags );
295 #endif
296
297         if( rc == DB_NOTFOUND ) {
298                 return rc;
299
300         } else if( rc != 0 ) {
301                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
302                         "get failed: %s (%d)\n",
303                         db_strerror(rc), rc, 0 );
304                 return rc;
305
306         } else if ( data.size == 0 || data.size % sizeof( ID ) ) {
307                 /* size not multiple of ID size */
308                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
309                         "odd size: expected %ld multiple, got %ld\n",
310                         (long) sizeof( ID ), (long) data.size, 0 );
311                 return -1;
312
313         } else if ( data.size != BDB_IDL_SIZEOF(ids) ) {
314                 /* size mismatch */
315                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
316                         "get size mismatch: expected %ld, got %ld\n",
317                         (long) ((1 + ids[0]) * sizeof( ID )), (long) data.size, 0 );
318                 return -1;
319         }
320
321         return rc;
322 }
323
324 int
325 bdb_idl_insert_key(
326         BackendDB       *be,
327         DB                      *db,
328         DB_TXN          *tid,
329         DBT                     *key,
330         ID                      id )
331 {
332         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
333         int     rc;
334         DBT data;
335 #ifndef BDB_IDL_MULTI
336         ID ids[BDB_IDL_DB_SIZE];
337 #endif
338
339         /* for printable keys only */
340         Debug( LDAP_DEBUG_ARGS,
341                 "=> bdb_idl_insert_key: %s %ld\n",
342                 (char *)key->data, (long) id, 0 );
343
344         assert( id != NOID );
345
346         DBTzero( &data );
347 #ifdef BDB_IDL_MULTI
348         {
349                 ID buf[BDB_IDL_DB_MAX];
350                 DBC *cursor;
351                 ID lo, hi;
352                 char *err;
353
354                 data.size = sizeof( ID );
355                 data.ulen = data.size;
356                 data.flags = DB_DBT_USERMEM;
357
358                 rc = bdb_idl_fetch_key( be, db, tid, key, buf );
359                 if ( rc && rc != DB_NOTFOUND )
360                         return rc;
361
362                 /* If it never existed, or there's room in the current key,
363                  * just store it.
364                  */
365                 if ( rc == DB_NOTFOUND || ( !BDB_IDL_IS_RANGE(buf) &&
366                         BDB_IDL_N(buf) < BDB_IDL_DB_MAX ) ) {
367                         data.data = &id;
368                         rc = db->put( db, tid, key, &data, DB_NODUPDATA );
369                 } else if ( BDB_IDL_IS_RANGE(buf) ) {
370                         /* If it's a range and we're outside the boundaries,
371                          * rewrite the range boundaries.
372                          */
373                         if ( id < BDB_IDL_RANGE_FIRST(buf) ||
374                                 id > BDB_IDL_RANGE_LAST(buf) ) {
375                                 rc = db->cursor( db, tid, &cursor, bdb->bi_db_opflags );
376                                 if ( rc != 0 ) {
377                                         Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
378                                                 "cursor failed: %s (%d)\n", db_strerror(rc), rc, 0 );
379                                         return rc;
380                                 }
381                                 if ( id < BDB_IDL_RANGE_FIRST(buf) ) {
382                                         data.data = buf+1;
383                                 } else {
384                                         data.data = buf+2;
385                                 }
386                                 rc = cursor->c_get( cursor, key, &data, DB_GET_BOTH | DB_RMW );
387                                 if ( rc != 0 ) {
388                                         err = "c_get";
389 fail:                           Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
390                                                 "%s failed: %s (%d)\n", err, db_strerror(rc), rc );
391                                         if ( cursor ) cursor->c_close( cursor );
392                                         return rc;
393                                 }
394                                 data.data = &id;
395                                 /* We should have been able to just overwrite the old
396                                  * value with the new, but apparently we have to delete
397                                  * it first.
398                                  */
399                                 rc = cursor->c_del( cursor, 0 );
400                                 if ( rc ) {
401                                         err = "c_del";
402                                         goto fail;
403                                 }
404                                 rc = cursor->c_put( cursor, key, &data, DB_KEYFIRST );
405                                 if ( rc ) {
406                                         err = "c_put";
407                                         goto fail;
408                                 }
409                                 rc = cursor->c_close( cursor );
410                                 if ( rc ) {
411                                         cursor = NULL;
412                                         err = "c_close";
413                                         goto fail;
414                                 }
415                         }
416                 } else {                /* convert to a range */
417                         lo = BDB_IDL_FIRST(buf);
418                         hi = BDB_IDL_LAST(buf);
419
420                         if (id < lo)
421                                 lo = id;
422                         else if (id > hi)
423                                 hi = id;
424
425                         cursor = NULL;
426
427                         /* Delete all of the old IDL so we can replace with a range */
428                         rc = db->del( db, tid, key, 0 );
429                         if ( rc ) {
430                                 err = "del";
431                                 goto fail;
432                         }
433
434                         /* Write the range */
435                         data.data = &id;
436                         id = 0;
437                         rc = db->put( db, tid, key, &data, 0 );
438                         if ( rc ) {
439                                 err = "put #1";
440                                 goto fail;
441                         }
442                         id = lo;
443                         rc = db->put( db, tid, key, &data, 0 );
444                         if ( rc ) {
445                                 err = "put #2";
446                                 goto fail;
447                         }
448                         id = hi;
449                         rc = db->put( db, tid, key, &data, 0 );
450                         if ( rc ) {
451                                 err = "put #3";
452                                 goto fail;
453                         }
454                 }
455         }
456 #else
457         data.data = ids;
458         data.ulen = sizeof ids;
459         data.flags = DB_DBT_USERMEM;
460
461         /* fetch the key for read/modify/write */
462         rc = db->get( db, tid, key, &data, DB_RMW | bdb->bi_db_opflags );
463
464         if( rc == DB_NOTFOUND ) {
465                 ids[0] = 1;
466                 ids[1] = id;
467                 data.size = 2 * sizeof( ID );
468
469         } else if ( rc != 0 ) {
470                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
471                         "get failed: %s (%d)\n",
472                         db_strerror(rc), rc, 0 );
473                 return rc;
474
475         } else if ( data.size == 0 || data.size % sizeof( ID ) ) {
476                 /* size not multiple of ID size */
477                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
478                         "odd size: expected %ld multiple, got %ld\n",
479                         (long) sizeof( ID ), (long) data.size, 0 );
480                 return -1;
481         
482         } else if ( data.size != BDB_IDL_SIZEOF(ids) ) {
483                 /* size mismatch */
484                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
485                         "get size mismatch: expected %ld, got %ld\n",
486                         (long) ((1 + ids[0]) * sizeof( ID )), (long) data.size, 0 );
487                 return -1;
488
489         } else if ( BDB_IDL_IS_RANGE(ids) ) {
490                 if( id < ids[1] ) {
491                         ids[1] = id;
492                 } else if ( ids[2] > id ) {
493                         ids[2] = id;
494                 } else {
495                         return 0;
496                 }
497
498         } else {
499                 rc = bdb_idl_insert( ids, id );
500
501                 if( rc == -1 ) {
502                         Debug( LDAP_DEBUG_TRACE, "=> bdb_idl_insert_key: dup\n",
503                                 0, 0, 0 );
504                         return 0;
505                 }
506                 if( rc != 0 ) {
507                         Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
508                                 "bdb_idl_insert failed (%d)\n",
509                                 rc, 0, 0 );
510                         
511                         return rc;
512                 }
513
514                 data.size = BDB_IDL_SIZEOF( ids );
515         }
516
517         /* store the key */
518         rc = db->put( db, tid, key, &data, 0 );
519 #endif
520         if( rc == DB_KEYEXIST ) rc = 0;
521
522         if( rc != 0 ) {
523                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
524                         "put failed: %s (%d)\n",
525                         db_strerror(rc), rc, 0 );
526         }
527         return rc;
528 }
529
530 int
531 bdb_idl_delete_key(
532         BackendDB       *be,
533         DB                      *db,
534         DB_TXN          *tid,
535         DBT                     *key,
536         ID                      id )
537 {
538         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
539         int     rc;
540         DBT data;
541 #ifndef BDB_IDL_MULTI
542         ID ids[BDB_IDL_DB_SIZE];
543 #endif
544
545         /* for printable keys only */
546         Debug( LDAP_DEBUG_ARGS,
547                 "=> bdb_idl_delete_key: %s %ld\n",
548                 (char *)key->data, (long) id, 0 );
549
550         assert( id != NOID );
551
552         DBTzero( &data );
553 #ifdef BDB_IDL_MULTI
554         {
555                 DBC *cursor;
556
557                 data.data = &id;
558                 data.size = sizeof( id );
559                 data.ulen = data.size;
560                 data.flags = DB_DBT_USERMEM;
561
562                 rc = db->cursor( db, tid, &cursor, bdb->bi_db_opflags );
563                 rc = cursor->c_get( cursor, key, &data, bdb->bi_db_opflags |
564                         DB_GET_BOTH | DB_RMW  );
565                 if (rc == 0)
566                         rc = cursor->c_del( cursor, 0 );
567                 rc = cursor->c_close( cursor );
568         }
569 #else
570         data.data = ids;
571         data.ulen = sizeof( ids );
572         data.flags = DB_DBT_USERMEM;
573
574         /* fetch the key for read/modify/write */
575         rc = db->get( db, tid, key, &data, DB_RMW | bdb->bi_db_opflags );
576
577         if ( rc != 0 ) {
578                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
579                         "get failed: %s (%d)\n",
580                         db_strerror(rc), rc, 0 );
581                 return rc;
582
583         } else if ( data.size == 0 || data.size % sizeof( ID ) ) {
584                 /* size not multiple of ID size */
585                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
586                         "odd size: expected %ld multiple, got %ld\n",
587                         (long) sizeof( ID ), (long) data.size, 0 );
588                 return -1;
589         
590         } else if ( BDB_IDL_IS_RANGE(ids) ) {
591                 return 0;
592
593         } else if ( data.size != (1 + ids[0]) * sizeof( ID ) ) {
594                 /* size mismatch */
595                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
596                         "get size mismatch: expected %ld, got %ld\n",
597                         (long) ((1 + ids[0]) * sizeof( ID )), (long) data.size, 0 );
598                 return -1;
599
600         } else {
601                 rc = idl_delete( ids, id );
602
603                 if( rc != 0 ) {
604                         Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
605                                 "idl_delete failed (%d)\n",
606                                 rc, 0, 0 );
607                         return rc;
608                 }
609
610                 if( ids[0] == 0 ) {
611                         /* delete the key */
612                         rc = db->del( db, tid, key, 0 );
613                         if( rc != 0 ) {
614                                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
615                                         "delete failed: %s (%d)\n",
616                                         db_strerror(rc), rc, 0 );
617                         }
618                         return rc;
619                 }
620
621                 data.size = (ids[0]+1) * sizeof( ID );
622         }
623
624         /* store the key */
625         rc = db->put( db, tid, key, &data, 0 );
626
627 #endif /* BDB_IDL_MULTI */
628
629         if( rc != 0 ) {
630                 Debug( LDAP_DEBUG_ANY,
631                         "=> bdb_idl_delete_key: put failed: %s (%d)\n",
632                         db_strerror(rc), rc, 0 );
633         }
634
635         return rc;
636 }
637
638
639 /*
640  * idl_intersection - return a = a intersection b
641  */
642 int
643 bdb_idl_intersection(
644         ID *a,
645         ID *b )
646 {
647         ID ida, idb;
648         ID idmax, idmin;
649         ID cursora = 0, cursorb = 0, cursorc;
650         int swap = 0;
651
652         if ( BDB_IDL_IS_ZERO( a ) || BDB_IDL_IS_ZERO( b ) ) {
653                 a[0] = 0;
654                 return 0;
655         }
656
657         idmin = IDL_MAX( BDB_IDL_FIRST(a), BDB_IDL_FIRST(b) );
658         idmax = IDL_MIN( BDB_IDL_LAST(a), BDB_IDL_LAST(b) );
659         if ( idmin > idmax ) {
660                 a[0] = 0;
661                 return 0;
662         } else if ( idmin == idmax ) {
663                 a[0] = 1;
664                 a[1] = idmin;
665                 return 0;
666         }
667
668         if ( BDB_IDL_IS_RANGE( a ) && BDB_IDL_IS_RANGE(b) ) {
669                 a[1] = idmin;
670                 a[2] = idmax;
671                 return 0;
672         }
673
674         if ( BDB_IDL_IS_RANGE( a ) ) {
675                 ID *tmp = a;
676                 a = b;
677                 b = tmp;
678                 swap = 1;
679         }
680
681         if ( BDB_IDL_IS_RANGE( b ) && BDB_IDL_FIRST( b ) <= idmin &&
682                 BDB_IDL_LAST( b ) >= idmax) {
683                 if (idmax - idmin + 1 == a[0])
684                 {
685                         a[0] = NOID;
686                         a[1] = idmin;
687                         a[2] = idmax;
688                 }
689                 goto done;
690         }
691
692         ida = bdb_idl_first( a, &cursora );
693         idb = bdb_idl_first( b, &cursorb );
694         cursorc = 0;
695
696         while( ida < idmin )
697                 ida = bdb_idl_next( a, &cursora );
698         while( idb < idmin )
699                 idb = bdb_idl_next( b, &cursorb );
700
701         while( ida <= idmax || idb <= idmax ) {
702                 if( ida == idb ) {
703                         a[++cursorc] = ida;
704                         ida = bdb_idl_next( a, &cursora );
705                         idb = bdb_idl_next( b, &cursorb );
706                 } else if ( ida < idb ) {
707                         ida = bdb_idl_next( a, &cursora );
708                 } else {
709                         idb = bdb_idl_next( b, &cursorb );
710                 }
711         }
712         a[0] = cursorc;
713 done:
714         if (swap)
715                 BDB_IDL_CPY( b, a );
716
717         return 0;
718 }
719
720
721 /*
722  * idl_union - return a = a union b
723  */
724 int
725 bdb_idl_union(
726         ID      *a,
727         ID      *b )
728 {
729         ID ida, idb;
730         ID cursora = 0, cursorb = 0, cursorc;
731
732         if ( BDB_IDL_IS_ZERO( b ) ) {
733                 return 0;
734         }
735
736         if ( BDB_IDL_IS_ZERO( a ) ) {
737                 BDB_IDL_CPY( a, b );
738                 return 0;
739         }
740
741         if ( BDB_IDL_IS_RANGE( a ) || BDB_IDL_IS_RANGE(b) ) {
742 over:           a[1] = IDL_MIN( BDB_IDL_FIRST(a), BDB_IDL_FIRST(b) );
743                 a[2] = IDL_MAX( BDB_IDL_LAST(a), BDB_IDL_LAST(b) );
744                 return 0;
745         }
746
747         ida = bdb_idl_first( a, &cursora );
748         idb = bdb_idl_first( b, &cursorb );
749
750         cursorc = b[0];
751
752         /* The distinct elements of a are cat'd to b */
753         while( ida != NOID || idb != NOID ) {
754                 if ( ida < idb ) {
755                         if( ++cursorc > BDB_IDL_UM_MAX ) {
756                                 a[0] = NOID;
757                                 goto over;
758                         }
759                         b[cursorc] = ida;
760                         ida = bdb_idl_next( a, &cursora );
761
762                 } else {
763                         if ( ida == idb )
764                                 ida = bdb_idl_next( a, &cursora );
765                         idb = bdb_idl_next( b, &cursorb );
766                 }
767         }
768
769         /* b is copied back to a in sorted order */
770         a[0] = cursorc;
771         cursora = 1;
772         cursorb = 1;
773         cursorc = b[0]+1;
774         while (cursorb <= b[0] || cursorc <= a[0]) {
775                 if (cursorc > a[0])
776                         idb = NOID;
777                 else
778                         idb = b[cursorc];
779                 if (b[cursorb] < idb)
780                         a[cursora++] = b[cursorb++];
781                 else {
782                         a[cursora++] = idb;
783                         cursorc++;
784                 }
785         }
786
787         return 0;
788 }
789
790
791 #if 0
792 /*
793  * bdb_idl_notin - return a intersection ~b (or a minus b)
794  */
795 int
796 bdb_idl_notin(
797         ID      *a,
798         ID      *b,
799         ID *ids )
800 {
801         ID ida, idb;
802         ID cursora = 0, cursorb = 0;
803
804         if( BDB_IDL_IS_ZERO( a ) ||
805                 BDB_IDL_IS_ZERO( b ) ||
806                 BDB_IDL_IS_RANGE( b ) )
807         {
808                 BDB_IDL_CPY( ids, a );
809                 return 0;
810         }
811
812         if( BDB_IDL_IS_RANGE( a ) ) {
813                 BDB_IDL_CPY( ids, a );
814                 return 0;
815         }
816
817         ida = bdb_idl_first( a, &cursora ),
818         idb = bdb_idl_first( b, &cursorb );
819
820         ids[0] = 0;
821
822         while( ida != NOID ) {
823                 if ( idb == NOID ) {
824                         /* we could shortcut this */
825                         ids[++ids[0]] = ida;
826                         ida = bdb_idl_next( a, &cursora );
827
828                 } else if ( ida < idb ) {
829                         ids[++ids[0]] = ida;
830                         ida = bdb_idl_next( a, &cursora );
831
832                 } else if ( ida > idb ) {
833                         idb = bdb_idl_next( b, &cursorb );
834
835                 } else {
836                         ida = bdb_idl_next( a, &cursora );
837                         idb = bdb_idl_next( b, &cursorb );
838                 }
839         }
840
841         return 0;
842 }
843 #endif
844
845 ID bdb_idl_first( ID *ids, ID *cursor )
846 {
847         ID pos;
848
849         if ( ids[0] == 0 ) {
850                 *cursor = NOID;
851                 return NOID;
852         }
853
854         if ( BDB_IDL_IS_RANGE( ids ) ) {
855                 if( *cursor < ids[1] ) {
856                         *cursor = ids[1];
857                 }
858                 return *cursor;
859         }
860
861         if ( *cursor == 0 )
862                 pos = 1;
863         else
864                 pos = bdb_idl_search( ids, *cursor );
865
866         if( pos > ids[0] ) {
867                 return NOID;
868         }
869
870         *cursor = pos;
871         return ids[pos];
872 }
873
874 ID bdb_idl_next( ID *ids, ID *cursor )
875 {
876         if ( BDB_IDL_IS_RANGE( ids ) ) {
877                 if( ids[2] < ++(*cursor) ) {
878                         return NOID;
879                 }
880                 return *cursor;
881         }
882
883         if ( ++(*cursor) <= ids[0] ) {
884                 return ids[*cursor];
885         }
886
887         return NOID;
888 }