]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/idl.c
another round replacing dn_parent ...
[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                 data.data = buf;
240                 data.ulen = BDB_IDL_UM_SIZEOF;
241                 data.flags = DB_DBT_USERMEM;
242
243                 rc = db->cursor( db, tid, &cursor, bdb->bi_db_opflags );
244                 if( rc != 0 ) {
245                         Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
246                                 "cursor failed: %s (%d)\n", db_strerror(rc), rc, 0 );
247                         return rc;
248                 }
249                 rc = cursor->c_get( cursor, key, &data, bdb->bi_db_opflags |
250                         DB_SET | DB_MULTIPLE );
251                 if (rc == 0) {
252                         i = ids;
253                         while (rc == 0) {
254                                 DB_MULTIPLE_INIT( ptr, &data );
255                                 while (ptr) {
256                                         DB_MULTIPLE_NEXT(ptr, &data, j, len);
257                                         if (j) {
258                                                 ++i;
259                                                 AC_MEMCPY( i, j, sizeof(ID) );
260                                         }
261                                 }
262                                 rc = cursor->c_get( cursor, key, &data, bdb->bi_db_opflags |
263                                         DB_NEXT_DUP | DB_MULTIPLE );
264                         }
265                         if ( rc == DB_NOTFOUND ) rc = 0;
266                         ids[0] = i - ids;
267                         /* On disk, a range is denoted by 0 in the first element */
268                         if (ids[1] == 0) {
269                                 if (ids[0] != BDB_IDL_RANGE_SIZE) {
270                                         Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
271                                                 "range size mismatch: expected %ld, got %ld\n",
272                                                 BDB_IDL_RANGE_SIZE, ids[0], 0 );
273                                         cursor->c_close( cursor );
274                                         return -1;
275                                 }
276                                 BDB_IDL_RANGE( ids, ids[2], ids[3] );
277                         }
278                         data.size = BDB_IDL_SIZEOF(ids);
279                 }
280                 rc2 = cursor->c_close( cursor );
281                 if (rc2) {
282                         Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
283                                 "close failed: %s (%d)\n", db_strerror(rc2), rc2, 0 );
284                         return rc2;
285                 }
286         }
287 #else
288         data.data = ids;
289         data.ulen = BDB_IDL_UM_SIZEOF;
290         data.flags = DB_DBT_USERMEM;
291         /* fetch it */
292         rc = db->get( db, tid, key, &data, bdb->bi_db_opflags );
293 #endif
294
295         if( rc == DB_NOTFOUND ) {
296                 return rc;
297
298         } else if( rc != 0 ) {
299                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
300                         "get failed: %s (%d)\n",
301                         db_strerror(rc), rc, 0 );
302                 return rc;
303
304         } else if ( data.size == 0 || data.size % sizeof( ID ) ) {
305                 /* size not multiple of ID size */
306                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
307                         "odd size: expected %ld multiple, got %ld\n",
308                         (long) sizeof( ID ), (long) data.size, 0 );
309                 return -1;
310
311         } else if ( data.size != BDB_IDL_SIZEOF(ids) ) {
312                 /* size mismatch */
313                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
314                         "get size mismatch: expected %ld, got %ld\n",
315                         (long) ((1 + ids[0]) * sizeof( ID )), (long) data.size, 0 );
316                 return -1;
317         }
318
319         return rc;
320 }
321
322 int
323 bdb_idl_insert_key(
324         BackendDB       *be,
325         DB                      *db,
326         DB_TXN          *tid,
327         DBT                     *key,
328         ID                      id )
329 {
330         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
331         int     rc;
332         DBT data;
333 #ifndef BDB_IDL_MULTI
334         ID ids[BDB_IDL_DB_SIZE];
335 #endif
336
337         /* for printable keys only */
338         Debug( LDAP_DEBUG_ARGS,
339                 "=> bdb_idl_insert_key: %s %ld\n",
340                 (char *)key->data, (long) id, 0 );
341
342         assert( id != NOID );
343
344         DBTzero( &data );
345 #ifdef BDB_IDL_MULTI
346         {
347                 ID buf[BDB_IDL_DB_MAX];
348                 DBC *cursor;
349                 ID lo, hi;
350                 char *err;
351
352                 data.size = sizeof( ID );
353                 data.ulen = data.size;
354                 data.flags = DB_DBT_USERMEM;
355
356                 rc = bdb_idl_fetch_key( be, db, tid, key, buf );
357                 if ( rc && rc != DB_NOTFOUND )
358                         return rc;
359
360                 /* If it never existed, or there's room in the current key,
361                  * just store it.
362                  */
363                 if ( rc == DB_NOTFOUND || ( !BDB_IDL_IS_RANGE(buf) &&
364                         BDB_IDL_N(buf) < BDB_IDL_DB_MAX ) ) {
365                         data.data = &id;
366                         rc = db->put( db, tid, key, &data, DB_NODUPDATA );
367                 } else if ( BDB_IDL_IS_RANGE(buf) ) {
368                         /* If it's a range and we're outside the boundaries,
369                          * rewrite the range boundaries.
370                          */
371                         if ( id < BDB_IDL_RANGE_FIRST(buf) ||
372                                 id > BDB_IDL_RANGE_LAST(buf) ) {
373                                 rc = db->cursor( db, tid, &cursor, bdb->bi_db_opflags );
374                                 if ( rc != 0 ) {
375                                         Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
376                                                 "cursor failed: %s (%d)\n", db_strerror(rc), rc, 0 );
377                                         return rc;
378                                 }
379                                 if ( id < BDB_IDL_RANGE_FIRST(buf) ) {
380                                         data.data = buf+1;
381                                 } else {
382                                         data.data = buf+2;
383                                 }
384                                 rc = cursor->c_get( cursor, key, &data, DB_GET_BOTH );
385                                 if ( rc != 0 ) {
386                                         err = "c_get";
387 fail:                           Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
388                                                 "%s failed: %s (%d)\n", err, db_strerror(rc), rc );
389                                         if ( cursor ) cursor->c_close( cursor );
390                                         return rc;
391                                 }
392                                 data.data = &id;
393                                 /* We should have been able to just overwrite the old
394                                  * value with the new, but apparently we have to delete
395                                  * it first.
396                                  */
397                                 rc = cursor->c_del( cursor, 0 );
398                                 if ( rc ) {
399                                         err = "c_del";
400                                         goto fail;
401                                 }
402                                 rc = cursor->c_put( cursor, key, &data, DB_KEYFIRST );
403                                 if ( rc ) {
404                                         err = "c_put";
405                                         goto fail;
406                                 }
407                                 rc = cursor->c_close( cursor );
408                                 if ( rc ) {
409                                         cursor = NULL;
410                                         err = "c_close";
411                                         goto fail;
412                                 }
413                         }
414                 } else {                /* convert to a range */
415                         lo = BDB_IDL_FIRST(buf);
416                         hi = BDB_IDL_LAST(buf);
417
418                         if (id < lo)
419                                 lo = id;
420                         else if (id > hi)
421                                 hi = id;
422
423                         cursor = NULL;
424
425                         /* Delete all of the old IDL so we can replace with a range */
426                         rc = db->del( db, tid, key, 0 );
427                         if ( rc ) {
428                                 err = "del";
429                                 goto fail;
430                         }
431
432                         /* Write the range */
433                         data.data = &id;
434                         id = 0;
435                         rc = db->put( db, tid, key, &data, 0 );
436                         if ( rc ) {
437                                 err = "put #1";
438                                 goto fail;
439                         }
440                         id = lo;
441                         rc = db->put( db, tid, key, &data, 0 );
442                         if ( rc ) {
443                                 err = "put #2";
444                                 goto fail;
445                         }
446                         id = hi;
447                         rc = db->put( db, tid, key, &data, 0 );
448                         if ( rc ) {
449                                 err = "put #3";
450                                 goto fail;
451                         }
452                 }
453         }
454 #else
455         data.data = ids;
456         data.ulen = sizeof ids;
457         data.flags = DB_DBT_USERMEM;
458
459         /* fetch the key for read/modify/write */
460         rc = db->get( db, tid, key, &data, DB_RMW | bdb->bi_db_opflags );
461
462         if( rc == DB_NOTFOUND ) {
463                 ids[0] = 1;
464                 ids[1] = id;
465                 data.size = 2 * sizeof( ID );
466
467         } else if ( rc != 0 ) {
468                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
469                         "get failed: %s (%d)\n",
470                         db_strerror(rc), rc, 0 );
471                 return rc;
472
473         } else if ( data.size == 0 || data.size % sizeof( ID ) ) {
474                 /* size not multiple of ID size */
475                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
476                         "odd size: expected %ld multiple, got %ld\n",
477                         (long) sizeof( ID ), (long) data.size, 0 );
478                 return -1;
479         
480         } else if ( data.size != BDB_IDL_SIZEOF(ids) ) {
481                 /* size mismatch */
482                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
483                         "get size mismatch: expected %ld, got %ld\n",
484                         (long) ((1 + ids[0]) * sizeof( ID )), (long) data.size, 0 );
485                 return -1;
486
487         } else if ( BDB_IDL_IS_RANGE(ids) ) {
488                 if( id < ids[1] ) {
489                         ids[1] = id;
490                 } else if ( ids[2] > id ) {
491                         ids[2] = id;
492                 } else {
493                         return 0;
494                 }
495
496         } else {
497                 rc = bdb_idl_insert( ids, id );
498
499                 if( rc == -1 ) {
500                         Debug( LDAP_DEBUG_TRACE, "=> bdb_idl_insert_key: dup\n",
501                                 0, 0, 0 );
502                         return 0;
503                 }
504                 if( rc != 0 ) {
505                         Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
506                                 "bdb_idl_insert failed (%d)\n",
507                                 rc, 0, 0 );
508                         
509                         return rc;
510                 }
511
512                 data.size = BDB_IDL_SIZEOF( ids );
513         }
514
515         /* store the key */
516         rc = db->put( db, tid, key, &data, 0 );
517 #endif
518         if( rc == DB_KEYEXIST ) rc = 0;
519
520         if( rc != 0 ) {
521                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
522                         "put failed: %s (%d)\n",
523                         db_strerror(rc), rc, 0 );
524         }
525         return rc;
526 }
527
528 int
529 bdb_idl_delete_key(
530         BackendDB       *be,
531         DB                      *db,
532         DB_TXN          *tid,
533         DBT                     *key,
534         ID                      id )
535 {
536         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
537         int     rc;
538         DBT data;
539 #ifndef BDB_IDL_MULTI
540         ID ids[BDB_IDL_DB_SIZE];
541 #endif
542
543         /* for printable keys only */
544         Debug( LDAP_DEBUG_ARGS,
545                 "=> bdb_idl_delete_key: %s %ld\n",
546                 (char *)key->data, (long) id, 0 );
547
548         assert( id != NOID );
549
550         DBTzero( &data );
551 #ifdef BDB_IDL_MULTI
552         {
553                 DBC *cursor;
554
555                 data.data = &id;
556                 data.size = sizeof( id );
557                 data.ulen = data.size;
558                 data.flags = DB_DBT_USERMEM;
559
560                 rc = db->cursor( db, tid, &cursor, bdb->bi_db_opflags );
561                 rc = cursor->c_get( cursor, key, &data, bdb->bi_db_opflags |
562                         DB_GET_BOTH | DB_RMW  );
563                 if (rc == 0)
564                         rc = cursor->c_del( cursor, 0 );
565                 rc = cursor->c_close( cursor );
566         }
567 #else
568         data.data = ids;
569         data.ulen = sizeof( ids );
570         data.flags = DB_DBT_USERMEM;
571
572         /* fetch the key for read/modify/write */
573         rc = db->get( db, tid, key, &data, DB_RMW | bdb->bi_db_opflags );
574
575         if ( rc != 0 ) {
576                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
577                         "get failed: %s (%d)\n",
578                         db_strerror(rc), rc, 0 );
579                 return rc;
580
581         } else if ( data.size == 0 || data.size % sizeof( ID ) ) {
582                 /* size not multiple of ID size */
583                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
584                         "odd size: expected %ld multiple, got %ld\n",
585                         (long) sizeof( ID ), (long) data.size, 0 );
586                 return -1;
587         
588         } else if ( BDB_IDL_IS_RANGE(ids) ) {
589                 return 0;
590
591         } else if ( data.size != (1 + ids[0]) * sizeof( ID ) ) {
592                 /* size mismatch */
593                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
594                         "get size mismatch: expected %ld, got %ld\n",
595                         (long) ((1 + ids[0]) * sizeof( ID )), (long) data.size, 0 );
596                 return -1;
597
598         } else {
599                 rc = idl_delete( ids, id );
600
601                 if( rc != 0 ) {
602                         Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
603                                 "idl_delete failed (%d)\n",
604                                 rc, 0, 0 );
605                         return rc;
606                 }
607
608                 if( ids[0] == 0 ) {
609                         /* delete the key */
610                         rc = db->del( db, tid, key, 0 );
611                         if( rc != 0 ) {
612                                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
613                                         "delete failed: %s (%d)\n",
614                                         db_strerror(rc), rc, 0 );
615                         }
616                         return rc;
617                 }
618
619                 data.size = (ids[0]+1) * sizeof( ID );
620         }
621
622         /* store the key */
623         rc = db->put( db, tid, key, &data, 0 );
624
625 #endif /* BDB_IDL_MULTI */
626
627         if( rc != 0 ) {
628                 Debug( LDAP_DEBUG_ANY,
629                         "=> bdb_idl_delete_key: put failed: %s (%d)\n",
630                         db_strerror(rc), rc, 0 );
631         }
632
633         return rc;
634 }
635
636
637 /*
638  * idl_intersection - return a = a intersection b
639  */
640 int
641 bdb_idl_intersection(
642         ID *a,
643         ID *b )
644 {
645         ID ida, idb;
646         ID idmax, idmin;
647         ID cursora = 0, cursorb = 0, cursorc;
648         int swap = 0;
649
650         if ( BDB_IDL_IS_ZERO( a ) || BDB_IDL_IS_ZERO( b ) ) {
651                 a[0] = 0;
652                 return 0;
653         }
654
655         idmin = IDL_MAX( BDB_IDL_FIRST(a), BDB_IDL_FIRST(b) );
656         idmax = IDL_MIN( BDB_IDL_LAST(a), BDB_IDL_LAST(b) );
657         if ( idmin > idmax ) {
658                 a[0] = 0;
659                 return 0;
660         } else if ( idmin == idmax ) {
661                 a[0] = 1;
662                 a[1] = idmin;
663                 return 0;
664         }
665
666         if ( BDB_IDL_IS_RANGE( a ) && BDB_IDL_IS_RANGE(b) ) {
667                 a[1] = idmin;
668                 a[2] = idmax;
669                 return 0;
670         }
671
672         if ( BDB_IDL_IS_RANGE( a ) ) {
673                 ID *tmp = a;
674                 a = b;
675                 b = tmp;
676                 swap = 1;
677         }
678
679         if ( BDB_IDL_IS_RANGE( b ) && BDB_IDL_FIRST( b ) <= idmin &&
680                 BDB_IDL_LAST( b ) >= idmax) {
681                 if (idmax - idmin + 1 == a[0])
682                 {
683                         a[0] = NOID;
684                         a[1] = idmin;
685                         a[2] = idmax;
686                 }
687                 goto done;
688         }
689
690         ida = bdb_idl_first( a, &cursora );
691         idb = bdb_idl_first( b, &cursorb );
692         cursorc = 0;
693
694         while( ida < idmin )
695                 ida = bdb_idl_next( a, &cursora );
696         while( idb < idmin )
697                 idb = bdb_idl_next( b, &cursorb );
698
699         while( ida <= idmax || idb <= idmax ) {
700                 if( ida == idb ) {
701                         a[++cursorc] = ida;
702                         ida = bdb_idl_next( a, &cursora );
703                         idb = bdb_idl_next( b, &cursorb );
704                 } else if ( ida < idb ) {
705                         ida = bdb_idl_next( a, &cursora );
706                 } else {
707                         idb = bdb_idl_next( b, &cursorb );
708                 }
709         }
710         a[0] = cursorc;
711 done:
712         if (swap)
713                 BDB_IDL_CPY( b, a );
714
715         return 0;
716 }
717
718
719 /*
720  * idl_union - return a = a union b
721  */
722 int
723 bdb_idl_union(
724         ID      *a,
725         ID      *b )
726 {
727         ID ida, idb;
728         ID cursora = 0, cursorb = 0, cursorc;
729
730         if ( BDB_IDL_IS_ZERO( b ) ) {
731                 return 0;
732         }
733
734         if ( BDB_IDL_IS_ZERO( a ) ) {
735                 BDB_IDL_CPY( a, b );
736                 return 0;
737         }
738
739         if ( BDB_IDL_IS_RANGE( a ) || BDB_IDL_IS_RANGE(b) ) {
740 over:           a[1] = IDL_MIN( BDB_IDL_FIRST(a), BDB_IDL_FIRST(b) );
741                 a[2] = IDL_MAX( BDB_IDL_LAST(a), BDB_IDL_LAST(b) );
742                 return 0;
743         }
744
745         ida = bdb_idl_first( a, &cursora );
746         idb = bdb_idl_first( b, &cursorb );
747
748         cursorc = b[0];
749
750         /* The distinct elements of a are cat'd to b */
751         while( ida != NOID || idb != NOID ) {
752                 if ( ida < idb ) {
753                         if( ++cursorc > BDB_IDL_UM_MAX ) {
754                                 a[0] = NOID;
755                                 goto over;
756                         }
757                         b[cursorc] = ida;
758                         ida = bdb_idl_next( a, &cursora );
759
760                 } else {
761                         if ( ida == idb )
762                                 ida = bdb_idl_next( a, &cursora );
763                         idb = bdb_idl_next( b, &cursorb );
764                 }
765         }
766
767         /* b is copied back to a in sorted order */
768         a[0] = cursorc;
769         cursora = 1;
770         cursorb = 1;
771         cursorc = b[0]+1;
772         while (cursorb <= b[0] || cursorc <= a[0]) {
773                 if (cursorc > a[0])
774                         idb = NOID;
775                 else
776                         idb = b[cursorc];
777                 if (b[cursorb] < idb)
778                         a[cursora++] = b[cursorb++];
779                 else {
780                         a[cursora++] = idb;
781                         cursorc++;
782                 }
783         }
784
785         return 0;
786 }
787
788
789 #if 0
790 /*
791  * bdb_idl_notin - return a intersection ~b (or a minus b)
792  */
793 int
794 bdb_idl_notin(
795         ID      *a,
796         ID      *b,
797         ID *ids )
798 {
799         ID ida, idb;
800         ID cursora = 0, cursorb = 0;
801
802         if( BDB_IDL_IS_ZERO( a ) ||
803                 BDB_IDL_IS_ZERO( b ) ||
804                 BDB_IDL_IS_RANGE( b ) )
805         {
806                 BDB_IDL_CPY( ids, a );
807                 return 0;
808         }
809
810         if( BDB_IDL_IS_RANGE( a ) ) {
811                 BDB_IDL_CPY( ids, a );
812                 return 0;
813         }
814
815         ida = bdb_idl_first( a, &cursora ),
816         idb = bdb_idl_first( b, &cursorb );
817
818         ids[0] = 0;
819
820         while( ida != NOID ) {
821                 if ( idb == NOID ) {
822                         /* we could shortcut this */
823                         ids[++ids[0]] = ida;
824                         ida = bdb_idl_next( a, &cursora );
825
826                 } else if ( ida < idb ) {
827                         ids[++ids[0]] = ida;
828                         ida = bdb_idl_next( a, &cursora );
829
830                 } else if ( ida > idb ) {
831                         idb = bdb_idl_next( b, &cursorb );
832
833                 } else {
834                         ida = bdb_idl_next( a, &cursora );
835                         idb = bdb_idl_next( b, &cursorb );
836                 }
837         }
838
839         return 0;
840 }
841 #endif
842
843 ID bdb_idl_first( ID *ids, ID *cursor )
844 {
845         ID pos;
846
847         if ( ids[0] == 0 ) {
848                 *cursor = NOID;
849                 return NOID;
850         }
851
852         if ( BDB_IDL_IS_RANGE( ids ) ) {
853                 if( *cursor < ids[1] ) {
854                         *cursor = ids[1];
855                 }
856                 return *cursor;
857         }
858
859         if ( *cursor == 0 )
860                 pos = 1;
861         else
862                 pos = bdb_idl_search( ids, *cursor );
863
864         if( pos > ids[0] ) {
865                 return NOID;
866         }
867
868         *cursor = pos;
869         return ids[pos];
870 }
871
872 ID bdb_idl_next( ID *ids, ID *cursor )
873 {
874         if ( BDB_IDL_IS_RANGE( ids ) ) {
875                 if( ids[2] < ++(*cursor) ) {
876                         return NOID;
877                 }
878                 return *cursor;
879         }
880
881         if ( ++(*cursor) <= ids[0] ) {
882                 return ids[*cursor];
883         }
884
885         return NOID;
886 }