]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/idl.c
SLAPD compiles. Needs LDBM work to link.
[openldap] / servers / slapd / back-ldbm / idl.c
1 /* idl.c - ldap id list handling routines */
2
3 #include "portable.h"
4
5 #include <stdio.h>
6
7 #include <ac/string.h>
8 #include <ac/socket.h>
9
10 #include "slap.h"
11 #include "ldapconfig.h"
12 #include "back-ldbm.h"
13
14 extern Datum    ldbm_cache_fetch();
15
16 IDList *
17 idl_alloc( int nids )
18 {
19         IDList  *new;
20
21         /* nmax + nids + space for the ids */
22         new = (IDList *) ch_calloc( (2 + nids), sizeof(ID) );
23         new->b_nmax = nids;
24         new->b_nids = 0;
25
26         return( new );
27 }
28
29 IDList  *
30 idl_allids( Backend *be )
31 {
32         IDList  *idl;
33
34         idl = idl_alloc( 0 );
35         idl->b_nmax = ALLIDSBLOCK;
36         idl->b_nids = next_id_get( be );
37
38         return( idl );
39 }
40
41 void
42 idl_free( IDList *idl )
43 {
44         if ( idl == NULL ) {
45                 return;
46         }
47
48         free( (char *) idl );
49 }
50
51 static IDList *
52 idl_fetch_one(
53     Backend             *be,
54     struct dbcache      *db,
55     Datum               key
56 )
57 {
58         Datum   data, k2;
59         IDList  *idl;
60         IDList  **tmp;
61         char    *kstr;
62         int     i, nids;
63
64 #ifdef HAVE_BERKELEY_DB2
65         memset( &k2, 0, sizeof( k2 ) );
66         memset( &data, 0, sizeof( data ) );
67 #endif
68
69         /* Debug( LDAP_DEBUG_TRACE, "=> idl_fetch_one\n", 0, 0, 0 ); */
70
71         data = ldbm_cache_fetch( db, key );
72
73         idl = (IDList *) data.dptr;
74
75         return( idl );
76 }
77
78 IDList *
79 idl_fetch(
80     Backend             *be,
81     struct dbcache      *db,
82     Datum               key
83 )
84 {
85         Datum   data, k2;
86         IDList  *idl;
87         IDList  **tmp;
88         char    *kstr;
89         int     i, nids;
90
91 #ifdef HAVE_BERKELEY_DB2
92         memset( &k2, 0, sizeof( k2 ) );
93         memset( &data, 0, sizeof( data ) );
94 #endif
95
96         /* Debug( LDAP_DEBUG_TRACE, "=> idl_fetch\n", 0, 0, 0 ); */
97
98         data = ldbm_cache_fetch( db, key );
99
100         if ( (idl = (IDList *) data.dptr) == NULL ) {
101                 return( NULL );
102         }
103
104         /* regular block */
105         if ( ! INDIRECT_BLOCK( idl ) ) {
106                 /*
107                 Debug( LDAP_DEBUG_TRACE, "<= idl_fetch %d ids (%d max)\n",
108                     idl->b_nids, idl->b_nmax, 0 );
109                 */
110
111                 /* make sure we have the current value of highest id */
112                 if ( idl->b_nmax == ALLIDSBLOCK ) {
113                         idl_free( idl );
114                         idl = idl_allids( be );
115                 }
116                 return( idl );
117         }
118
119         /*
120          * this is an indirect block which points to other blocks.
121          * we need to read in all the blocks it points to and construct
122          * a big id list containing all the ids, which we will return.
123          */
124
125         /* count the number of blocks & allocate space for pointers to them */
126         for ( i = 0; idl->b_ids[i] != NOID; i++ )
127                 ;       /* NULL */
128         tmp = (IDList **) ch_malloc( (i + 1) * sizeof(IDList *) );
129
130         /* read in all the blocks */
131         kstr = (char *) ch_malloc( key.dsize + 20 );
132         nids = 0;
133         for ( i = 0; idl->b_ids[i] != NOID; i++ ) {
134                 sprintf( kstr, "%c%s%d", CONT_PREFIX, key.dptr, idl->b_ids[i] );
135                 k2.dptr = kstr;
136                 k2.dsize = strlen( kstr ) + 1;
137
138                 if ( (tmp[i] = idl_fetch_one( be, db, k2 )) == NULL ) {
139                         Debug( LDAP_DEBUG_ANY,
140                             "idl_fetch of (%s) returns NULL\n", k2.dptr, 0, 0 );
141                         continue;
142                 }
143
144                 nids += tmp[i]->b_nids;
145         }
146         tmp[i] = NULL;
147         idl_free( idl );
148
149         /* allocate space for the big block */
150         idl = idl_alloc( nids );
151         idl->b_nids = nids;
152         nids = 0;
153
154         /* copy in all the ids from the component blocks */
155         for ( i = 0; tmp[i] != NULL; i++ ) {
156                 if ( tmp[i] == NULL ) {
157                         continue;
158                 }
159
160                 SAFEMEMCPY( (char *) &idl->b_ids[nids], (char *) tmp[i]->b_ids,
161                     tmp[i]->b_nids * sizeof(ID) );
162                 nids += tmp[i]->b_nids;
163
164                 idl_free( tmp[i] );
165         }
166         free( (char *) tmp );
167
168         Debug( LDAP_DEBUG_TRACE, "<= idl_fetch %d ids (%d max)\n", idl->b_nids,
169             idl->b_nmax, 0 );
170         return( idl );
171 }
172
173 static int
174 idl_store(
175     Backend             *be,
176     struct dbcache      *db,
177     Datum               key, 
178     IDList              *idl
179 )
180 {
181         int     rc, flags;
182         Datum   data;
183         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
184
185 #ifdef HAVE_BERKELEY_DB2
186         memset( &data, 0, sizeof( data ) );
187 #endif
188
189         /* Debug( LDAP_DEBUG_TRACE, "=> idl_store\n", 0, 0, 0 ); */
190
191         data.dptr = (char *) idl;
192         data.dsize = (2 + idl->b_nmax) * sizeof(ID);
193         
194 #ifdef LDBM_DEBUG
195         Statslog( LDAP_DEBUG_STATS, "<= idl_store(): rc=%d\n",
196                 rc, 0, 0, 0, 0 );
197 #endif
198
199         flags = LDBM_REPLACE;
200         if( li->li_flush_wrt ) flags |= LDBM_SYNC;
201         rc = ldbm_cache_store( db, key, data, flags );
202
203         /* Debug( LDAP_DEBUG_TRACE, "<= idl_store %d\n", rc, 0, 0 ); */
204         return( rc );
205 }
206
207 static void
208 idl_split_block(
209     IDList      *b,
210     ID          id,
211     IDList      **n1,
212     IDList      **n2
213 )
214 {
215         int     i;
216
217         /* find where to split the block */
218         for ( i = 0; i < b->b_nids && id > b->b_ids[i]; i++ )
219                 ;       /* NULL */
220
221         *n1 = idl_alloc( i == 0 ? 1 : i );
222         *n2 = idl_alloc( b->b_nids - i + (i == 0 ? 0 : 1));
223
224         /*
225          * everything before the id being inserted in the first block
226          * unless there is nothing, in which case the id being inserted
227          * goes there.
228          */
229         SAFEMEMCPY( (char *) &(*n1)->b_ids[0], (char *) &b->b_ids[0],
230             i * sizeof(ID) );
231         (*n1)->b_nids = (i == 0 ? 1 : i);
232
233         if ( i == 0 ) {
234                 (*n1)->b_ids[0] = id;
235         } else {
236                 (*n2)->b_ids[0] = id;
237         }
238
239         /* the id being inserted & everything after in the second block */
240         SAFEMEMCPY( (char *) &(*n2)->b_ids[i == 0 ? 0 : 1],
241             (char *) &b->b_ids[i], (b->b_nids - i) * sizeof(ID) );
242         (*n2)->b_nids = b->b_nids - i + (i == 0 ? 0 : 1);
243 }
244
245 /*
246  * idl_change_first - called when an indirect block's first key has
247  * changed, meaning it needs to be stored under a new key, and the
248  * header block pointing to it needs updating.
249  */
250
251 static int
252 idl_change_first(
253     Backend             *be,
254     struct dbcache      *db,
255     Datum               hkey,           /* header block key     */
256     IDList              *h,             /* header block         */
257     int                 pos,            /* pos in h to update   */
258     Datum               bkey,           /* data block key       */
259     IDList              *b              /* data block           */
260 )
261 {
262         int     rc;
263
264         /* Debug( LDAP_DEBUG_TRACE, "=> idl_change_first\n", 0, 0, 0 ); */
265
266         /* delete old key block */
267         if ( (rc = ldbm_cache_delete( db, bkey )) != 0 ) {
268                 Debug( LDAP_DEBUG_ANY,
269                     "ldbm_delete of (%s) returns %d\n", bkey.dptr, rc,
270                     0 );
271                 return( rc );
272         }
273
274         /* write block with new key */
275         sprintf( bkey.dptr, "%c%s%d", CONT_PREFIX, hkey.dptr, b->b_ids[0] );
276         bkey.dsize = strlen( bkey.dptr ) + 1;
277         if ( (rc = idl_store( be, db, bkey, b )) != 0 ) {
278                 Debug( LDAP_DEBUG_ANY,
279                     "idl_store of (%s) returns %d\n", bkey.dptr, rc, 0 );
280                 return( rc );
281         }
282
283         /* update + write indirect header block */
284         h->b_ids[pos] = b->b_ids[0];
285         if ( (rc = idl_store( be, db, hkey, h )) != 0 ) {
286                 Debug( LDAP_DEBUG_ANY,
287                     "idl_store of (%s) returns %d\n", hkey.dptr, rc, 0 );
288                 return( rc );
289         }
290
291         return( 0 );
292 }
293
294 int
295 idl_insert_key(
296     Backend             *be,
297     struct dbcache      *db,
298     Datum               key,
299     ID                  id
300 )
301 {
302         int     i, j, first, rc;
303         IDList  *idl, *tmp, *tmp2, *tmp3;
304         char    *kstr;
305         Datum   k2;
306
307 #ifdef HAVE_BERKELEY_DB2
308         memset( &k2, 0, sizeof( k2 ) );
309 #endif
310
311         if ( (idl = idl_fetch_one( be, db, key )) == NULL ) {
312 #ifdef LDBM_DEBUG
313                 Statslog( LDAP_DEBUG_STATS, "=> idl_insert_key(): no key yet\n",
314                         0, 0, 0, 0, 0 );
315 #endif
316
317                 idl = idl_alloc( 1 );
318                 idl->b_ids[idl->b_nids++] = id;
319                 rc = idl_store( be, db, key, idl );
320
321                 idl_free( idl );
322                 return( rc );
323         }
324
325         /* regular block */
326         if ( ! INDIRECT_BLOCK( idl ) ) {
327                 switch ( idl_insert( &idl, id, db->dbc_maxids ) ) {
328                 case 0:         /* id inserted - store the updated block */
329                 case 1:
330                         rc = idl_store( be, db, key, idl );
331                         break;
332
333                 case 2:         /* id already there - nothing to do */
334                         rc = 0;
335                         break;
336
337                 case 3:         /* id not inserted - block must be split */
338                         /* check threshold for marking this an all-id block */
339                         if ( db->dbc_maxindirect < 2 ) {
340                                 idl_free( idl );
341                                 idl = idl_allids( be );
342                                 rc = idl_store( be, db, key, idl );
343                                 idl_free( idl );
344
345                                 return( rc );
346                         }
347
348                         idl_split_block( idl, id, &tmp, &tmp2 );
349                         idl_free( idl );
350
351                         /* create the header indirect block */
352                         idl = idl_alloc( 3 );
353                         idl->b_nmax = 3;
354                         idl->b_nids = INDBLOCK;
355                         idl->b_ids[0] = tmp->b_ids[0];
356                         idl->b_ids[1] = tmp2->b_ids[0];
357                         idl->b_ids[2] = NOID;
358
359                         /* store it */
360                         rc = idl_store( be, db, key, idl );
361
362                         /* store the first id block */
363                         kstr = (char *) ch_malloc( key.dsize + 20 );
364                         sprintf( kstr, "%c%s%d", CONT_PREFIX, key.dptr,
365                             tmp->b_ids[0] );
366                         k2.dptr = kstr;
367                         k2.dsize = strlen( kstr ) + 1;
368                         rc = idl_store( be, db, k2, tmp );
369
370                         /* store the second id block */
371                         sprintf( kstr, "%c%s%d", CONT_PREFIX, key.dptr,
372                             tmp2->b_ids[0] );
373                         k2.dptr = kstr;
374                         k2.dsize = strlen( kstr ) + 1;
375                         rc = idl_store( be, db, k2, tmp2 );
376
377                         free( kstr );
378                         idl_free( tmp );
379                         idl_free( tmp2 );
380                         break;
381                 }
382
383                 idl_free( idl );
384                 return( rc );
385         }
386
387         /*
388          * this is an indirect block which points to other blocks.
389          * we need to read in the block into which the id should be
390          * inserted, then insert the id and store the block.  we might
391          * have to split the block if it is full, which means we also
392          * need to write a new "header" block.
393          */
394
395         /* select the block to try inserting into */
396         for ( i = 0; idl->b_ids[i] != NOID && id > idl->b_ids[i]; i++ )
397                 ;       /* NULL */
398         if ( i != 0 ) {
399                 i--;
400                 first = 0;
401         } else {
402                 first = 1;
403         }
404
405         /* get the block */
406         kstr = (char *) ch_malloc( key.dsize + 20 );
407         sprintf( kstr, "%c%s%d", CONT_PREFIX, key.dptr, idl->b_ids[i] );
408         k2.dptr = kstr;
409         k2.dsize = strlen( kstr ) + 1;
410         if ( (tmp = idl_fetch_one( be, db, k2 )) == NULL ) {
411                 Debug( LDAP_DEBUG_ANY, "nonexistent continuation block (%s)\n",
412                     k2.dptr, 0, 0 );
413                 return( -1 );
414         }
415
416         /* insert the id */
417         switch ( idl_insert( &tmp, id, db->dbc_maxids ) ) {
418         case 0:         /* id inserted ok */
419                 if ( (rc = idl_store( be, db, k2, tmp )) != 0 ) {
420                         Debug( LDAP_DEBUG_ANY,
421                             "idl_store of (%s) returns %d\n", k2.dptr, rc, 0 );
422                 }
423                 break;
424
425         case 1:         /* id inserted - first id in block has changed */
426                 /*
427                  * key for this block has changed, so we have to
428                  * write the block under the new key, delete the
429                  * old key block + update and write the indirect
430                  * header block.
431                  */
432
433                 rc = idl_change_first( be, db, key, idl, i, k2, tmp );
434                 break;
435
436         case 2:         /* id not inserted - already there */
437                 break;
438
439         case 3:         /* id not inserted - block is full */
440                 /*
441                  * first, see if it will fit in the next block,
442                  * without splitting, unless we're trying to insert
443                  * into the beginning of the first block.
444                  */
445
446                 /* is there a next block? */
447                 if ( !first && idl->b_ids[i + 1] != NOID ) {
448                         /* read it in */
449                         sprintf( kstr, "%c%s%d", CONT_PREFIX, key.dptr,
450                             idl->b_ids[i + 1] );
451                         k2.dptr = kstr;
452                         k2.dsize = strlen( kstr ) + 1;
453                         if ( (tmp2 = idl_fetch_one( be, db, k2 )) == NULL ) {
454                                 Debug( LDAP_DEBUG_ANY,
455                                     "idl_fetch_one (%s) returns NULL\n",
456                                     k2.dptr, 0, 0 );
457                                 break;
458                         }
459
460                         switch ( (rc = idl_insert( &tmp2, id,
461                             db->dbc_maxids )) ) {
462                         case 1:         /* id inserted first in block */
463                                 rc = idl_change_first( be, db, key, idl,
464                                     i + 1, k2, tmp2 );
465                                 /* FALL */
466
467                         case 2:         /* id already there - how? */
468                         case 0:         /* id inserted */
469                                 if ( rc == 2 ) {
470                                         Debug( LDAP_DEBUG_ANY,
471                                             "id %d already in next block\n",
472                                             id, 0, 0 );
473                                 }
474                                 free( kstr );
475                                 idl_free( tmp );
476                                 idl_free( tmp2 );
477                                 idl_free( idl );
478                                 return( 0 );
479
480                         case 3:         /* split the original block */
481                                 idl_free( tmp2 );
482                                 break;
483                         }
484
485                 }
486
487                 /*
488                  * must split the block, write both new blocks + update
489                  * and write the indirect header block.
490                  */
491
492                 /* count how many indirect blocks */
493                 for ( j = 0; idl->b_ids[j] != NOID; j++ )
494                         ;       /* NULL */
495
496                 /* check it against all-id thresholed */
497                 if ( j + 1 > db->dbc_maxindirect ) {
498                         /*
499                          * we've passed the all-id threshold, meaning
500                          * that this set of blocks should be replaced
501                          * by a single "all-id" block.  our job: delete
502                          * all the indirect blocks, and replace the header
503                          * block by an all-id block.
504                          */
505
506                         /* delete all indirect blocks */
507                         for ( j = 0; idl->b_ids[j] != NOID; j++ ) {
508                                 sprintf( kstr, "%c%s%d", CONT_PREFIX, key.dptr,
509                                     idl->b_ids[j] );
510                                 k2.dptr = kstr;
511                                 k2.dsize = strlen( kstr ) + 1;
512
513                                 rc = ldbm_cache_delete( db, k2 );
514                         }
515
516                         /* store allid block in place of header block */
517                         idl_free( idl );
518                         idl = idl_allids( be );
519                         rc = idl_store( be, db, key, idl );
520
521                         free( kstr );
522                         idl_free( idl );
523                         idl_free( tmp );
524                         return( rc );
525                 }
526
527                 idl_split_block( tmp, id, &tmp2, &tmp3 );
528                 idl_free( tmp );
529
530                 /* create a new updated indirect header block */
531                 tmp = idl_alloc( idl->b_nmax + 1 );
532                 tmp->b_nids = INDBLOCK;
533                 /* everything up to the split block */
534                 SAFEMEMCPY( (char *) tmp->b_ids, (char *) idl->b_ids,
535                     i * sizeof(ID) );
536                 /* the two new blocks */
537                 tmp->b_ids[i] = tmp2->b_ids[0];
538                 tmp->b_ids[i + 1] = tmp3->b_ids[0];
539                 /* everything after the split block */
540                 SAFEMEMCPY( (char *) &tmp->b_ids[i + 2], (char *)
541                     &idl->b_ids[i + 1], (idl->b_nmax - i - 1) * sizeof(ID) );
542
543                 /* store the header block */
544                 rc = idl_store( be, db, key, tmp );
545
546                 /* store the first id block */
547                 sprintf( kstr, "%c%s%d", CONT_PREFIX, key.dptr,
548                     tmp2->b_ids[0] );
549                 k2.dptr = kstr;
550                 k2.dsize = strlen( kstr ) + 1;
551                 rc = idl_store( be, db, k2, tmp2 );
552
553                 /* store the second id block */
554                 sprintf( kstr, "%c%s%d", CONT_PREFIX, key.dptr,
555                     tmp3->b_ids[0] );
556                 k2.dptr = kstr;
557                 k2.dsize = strlen( kstr ) + 1;
558                 rc = idl_store( be, db, k2, tmp3 );
559
560                 idl_free( tmp2 );
561                 idl_free( tmp3 );
562                 break;
563         }
564
565         free( kstr );
566         idl_free( tmp );
567         idl_free( idl );
568         return( rc );
569 }
570
571 /*
572  * idl_insert - insert an id into an id list.
573  * returns      0       id inserted
574  *              1       id inserted, first id in block has changed
575  *              2       id not inserted, already there
576  *              3       id not inserted, block must be split
577  */
578
579 int
580 idl_insert( IDList **idl, ID id, int maxids )
581 {
582         int     i, j;
583
584         if ( ALLIDS( *idl ) ) {
585                 return( 2 );    /* already there */
586         }
587
588         /* is it already there? XXX bin search XXX */
589         for ( i = 0; i < (*idl)->b_nids && id > (*idl)->b_ids[i]; i++ ) {
590                 ;       /* NULL */
591         }
592         if ( i < (*idl)->b_nids && (*idl)->b_ids[i] == id ) {
593                 return( 2 );    /* already there */
594         }
595
596         /* do we need to make room for it? */
597         if ( (*idl)->b_nids == (*idl)->b_nmax ) {
598                 /* make room or indicate block needs splitting */
599                 if ( (*idl)->b_nmax == maxids ) {
600                         return( 3 );    /* block needs splitting */
601                 }
602
603                 (*idl)->b_nmax *= 2;
604                 if ( (*idl)->b_nmax > maxids ) {
605                         (*idl)->b_nmax = maxids;
606                 }
607                 *idl = (IDList *) ch_realloc( (char *) *idl,
608                     ((*idl)->b_nmax + 2) * sizeof(ID) );
609         }
610
611         /* make a slot for the new id */
612         for ( j = (*idl)->b_nids; j != i; j-- ) {
613                 (*idl)->b_ids[j] = (*idl)->b_ids[j-1];
614         }
615         (*idl)->b_ids[i] = id;
616         (*idl)->b_nids++;
617         (void) memset( (char *) &(*idl)->b_ids[(*idl)->b_nids], '\0',
618             ((*idl)->b_nmax - (*idl)->b_nids) * sizeof(ID) );
619
620         return( i == 0 ? 1 : 0 );       /* inserted - first id changed or not */
621 }
622
623 static IDList *
624 idl_dup( IDList *idl )
625 {
626         IDList  *new;
627
628         if ( idl == NULL ) {
629                 return( NULL );
630         }
631
632         new = idl_alloc( idl->b_nmax );
633         SAFEMEMCPY( (char *) new, (char *) idl, (idl->b_nmax + 2)
634             * sizeof(ID) );
635
636         return( new );
637 }
638
639 static IDList *
640 idl_min( IDList *a, IDList *b )
641 {
642         return( a->b_nids > b->b_nids ? b : a );
643 }
644
645 /*
646  * idl_intersection - return a intersection b
647  */
648
649 IDList *
650 idl_intersection(
651     Backend     *be,
652     IDList      *a,
653     IDList      *b
654 )
655 {
656         int     ai, bi, ni;
657         IDList  *n;
658
659         if ( a == NULL || b == NULL ) {
660                 return( NULL );
661         }
662         if ( ALLIDS( a ) ) {
663                 return( idl_dup( b ) );
664         }
665         if ( ALLIDS( b ) ) {
666                 return( idl_dup( a ) );
667         }
668
669         n = idl_dup( idl_min( a, b ) );
670
671         for ( ni = 0, ai = 0, bi = 0; ai < a->b_nids; ai++ ) {
672                 for ( ; bi < b->b_nids && b->b_ids[bi] < a->b_ids[ai]; bi++ )
673                         ;       /* NULL */
674
675                 if ( bi == b->b_nids ) {
676                         break;
677                 }
678
679                 if ( b->b_ids[bi] == a->b_ids[ai] ) {
680                         n->b_ids[ni++] = a->b_ids[ai];
681                 }
682         }
683
684         if ( ni == 0 ) {
685                 idl_free( n );
686                 return( NULL );
687         }
688         n->b_nids = ni;
689
690         return( n );
691 }
692
693 /*
694  * idl_union - return a union b
695  */
696
697 IDList *
698 idl_union(
699     Backend     *be,
700     IDList      *a,
701     IDList      *b
702 )
703 {
704         int     ai, bi, ni;
705         IDList  *n;
706
707         if ( a == NULL ) {
708                 return( idl_dup( b ) );
709         }
710         if ( b == NULL ) {
711                 return( idl_dup( a ) );
712         }
713         if ( ALLIDS( a ) || ALLIDS( b ) ) {
714                 return( idl_allids( be ) );
715         }
716
717         if ( b->b_nids < a->b_nids ) {
718                 n = a;
719                 a = b;
720                 b = n;
721         }
722
723         n = idl_alloc( a->b_nids + b->b_nids );
724
725         for ( ni = 0, ai = 0, bi = 0; ai < a->b_nids && bi < b->b_nids; ) {
726                 if ( a->b_ids[ai] < b->b_ids[bi] ) {
727                         n->b_ids[ni++] = a->b_ids[ai++];
728                 } else if ( b->b_ids[bi] < a->b_ids[ai] ) {
729                         n->b_ids[ni++] = b->b_ids[bi++];
730                 } else {
731                         n->b_ids[ni++] = a->b_ids[ai];
732                         ai++, bi++;
733                 }
734         }
735
736         for ( ; ai < a->b_nids; ai++ ) {
737                 n->b_ids[ni++] = a->b_ids[ai];
738         }
739         for ( ; bi < b->b_nids; bi++ ) {
740                 n->b_ids[ni++] = b->b_ids[bi];
741         }
742         n->b_nids = ni;
743
744         return( n );
745 }
746
747 /*
748  * idl_notin - return a intersection ~b (or a minus b)
749  */
750
751 IDList *
752 idl_notin(
753     Backend     *be,
754     IDList      *a,
755     IDList      *b
756 )
757 {
758         int     ni, ai, bi;
759         IDList  *n;
760
761         if ( a == NULL ) {
762                 return( NULL );
763         }
764         if ( b == NULL || ALLIDS( b )) {
765                 return( idl_dup( a ) );
766         }
767
768         if ( ALLIDS( a ) ) {
769                 n = idl_alloc( SLAPD_LDBM_MIN_MAXIDS );
770                 ni = 0;
771
772                 for ( ai = 1, bi = 0; ai < a->b_nids && ni < n->b_nmax &&
773                     bi < b->b_nmax; ai++ ) {
774                         if ( b->b_ids[bi] == ai ) {
775                                 bi++;
776                         } else {
777                                 n->b_ids[ni++] = ai;
778                         }
779                 }
780
781                 for ( ; ai < a->b_nids && ni < n->b_nmax; ai++ ) {
782                         n->b_ids[ni++] = ai;
783                 }
784
785                 if ( ni == n->b_nmax ) {
786                         idl_free( n );
787                         return( idl_allids( be ) );
788                 } else {
789                         n->b_nids = ni;
790                         return( n );
791                 }
792         }
793
794         n = idl_dup( a );
795
796         ni = 0;
797         for ( ai = 0, bi = 0; ai < a->b_nids; ai++ ) {
798                 for ( ; bi < b->b_nids && b->b_ids[bi] < a->b_ids[ai];
799                     bi++ ) {
800                         ;       /* NULL */
801                 }
802
803                 if ( bi == b->b_nids ) {
804                         break;
805                 }
806
807                 if ( b->b_ids[bi] != a->b_ids[ai] ) {
808                         n->b_ids[ni++] = a->b_ids[ai];
809                 }
810         }
811
812         for ( ; ai < a->b_nids; ai++ ) {
813                 n->b_ids[ni++] = a->b_ids[ai];
814         }
815         n->b_nids = ni;
816
817         return( n );
818 }
819
820 ID
821 idl_firstid( IDList *idl )
822 {
823         if ( idl == NULL || idl->b_nids == 0 ) {
824                 return( NOID );
825         }
826
827         if ( ALLIDS( idl ) ) {
828                 return( idl->b_nids == 1 ? NOID : 1 );
829         }
830
831         return( idl->b_ids[0] );
832 }
833
834 ID
835 idl_nextid( IDList *idl, ID id )
836 {
837         int     i;
838
839         if ( ALLIDS( idl ) ) {
840                 return( ++id < idl->b_nids ? id : NOID );
841         }
842
843         for ( i = 0; i < idl->b_nids && idl->b_ids[i] < id; i++ ) {
844                 ;       /* NULL */
845         }
846         i++;
847
848         if ( i >= idl->b_nids ) {
849                 return( NOID );
850         } else {
851                 return( idl->b_ids[i] );
852         }
853 }