]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/idl.c
13ac4235b05752fa8a49c4bdfcf4dd0a33cd4305
[openldap] / servers / slapd / back-ldbm / idl.c
1 /* idl.c - ldap id list handling routines */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2000 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
12 #include <ac/string.h>
13 #include <ac/socket.h>
14
15 #include "slap.h"
16 #include "back-ldbm.h"
17
18 static ID_BLOCK* idl_dup( ID_BLOCK *idl );
19
20 static void cont_alloc( Datum *cont, Datum *key )
21 {
22         ldbm_datum_init( *cont );
23         cont->dsize = 1 + sizeof(ID) + key->dsize;
24         cont->dptr = ch_malloc( cont->dsize );
25
26         * (unsigned char *) cont->dptr = SLAP_INDEX_CONT_PREFIX;
27
28         AC_MEMCPY( &((unsigned char *)cont->dptr)[1 + sizeof(ID)],
29                 key->dptr, key->dsize );
30 }
31
32 static void cont_id( Datum *cont, ID id )
33 {
34         int i;
35
36         for( i=1; i <= sizeof(id); i++) {
37                 ((unsigned char *)cont->dptr)[i] = (unsigned char)(id & 0xFF);
38                 id >>= 8;
39         }
40
41 }
42
43 static void cont_free( Datum *cont )
44 {
45         ch_free( cont->dptr );
46 }
47
48 /* Allocate an ID_BLOCK with room for nids ids */
49 ID_BLOCK *
50 idl_alloc( unsigned int nids )
51 {
52         ID_BLOCK        *new;
53
54         /* nmax + nids + space for the ids */
55         new = (ID_BLOCK *) ch_calloc( (ID_BLOCK_IDS_OFFSET + nids), sizeof(ID) );
56         ID_BLOCK_NMAX(new) = nids;
57         ID_BLOCK_NIDS(new) = 0;
58
59         return( new );
60 }
61
62
63 /* Allocate an empty ALLIDS ID_BLOCK */
64 ID_BLOCK        *
65 idl_allids( Backend *be )
66 {
67         ID_BLOCK        *idl;
68
69         idl = idl_alloc( 0 );
70         ID_BLOCK_NMAX(idl) = ID_BLOCK_ALLIDS_VALUE;
71         ID_BLOCK_NIDS(idl) = next_id_get( be );
72
73         return( idl );
74 }
75
76 /* Free an ID_BLOCK */
77 void
78 idl_free( ID_BLOCK *idl )
79 {
80         if ( idl == NULL ) {
81                 Debug( LDAP_DEBUG_TRACE,
82                         "idl_free: called with NULL pointer\n",
83                         0, 0, 0 );
84                 return;
85         }
86
87         free( (char *) idl );
88 }
89
90
91 /* Fetch an single ID_BLOCK from the cache */
92 static ID_BLOCK *
93 idl_fetch_one(
94     Backend             *be,
95     DBCache     *db,
96     Datum               key
97 )
98 {
99         Datum   data;
100         ID_BLOCK        *idl;
101
102         /* Debug( LDAP_DEBUG_TRACE, "=> idl_fetch_one\n", 0, 0, 0 ); */
103
104         data = ldbm_cache_fetch( db, key );
105
106         if( data.dptr == NULL ) {
107                 return NULL;
108         }
109
110         idl = idl_dup((ID_BLOCK *) data.dptr);
111
112         ldbm_datum_free( db->dbc_db, data );
113
114         return idl;
115 }
116
117
118 /* Fetch a set of ID_BLOCKs from the cache
119  *      if not INDIRECT
120  *              if block return is an ALLIDS block,
121  *                      return an new ALLIDS block
122  *              otherwise
123  *                      return block
124  *      construct super block from all blocks referenced by INDIRECT block
125  *      return super block
126  */
127 ID_BLOCK *
128 idl_fetch(
129     Backend             *be,
130     DBCache     *db,
131     Datum               key
132 )
133 {
134         Datum   data;
135         ID_BLOCK        *idl;
136         ID_BLOCK        **tmp;
137         int     i, nids;
138
139         idl = idl_fetch_one( be, db, key );
140
141         if ( idl == NULL ) {
142                 return NULL;
143         }
144
145         if ( ID_BLOCK_ALLIDS(idl) ) {
146                 /* all ids block */
147                 /* make sure we have the current value of highest id */
148                 idl_free( idl );
149                 idl = idl_allids( be );
150
151                 return( idl );
152         }
153
154         if ( ! ID_BLOCK_INDIRECT( idl ) ) {
155                 /* regular block */
156                 return( idl );
157         }
158
159         /*
160          * this is an indirect block which points to other blocks.
161          * we need to read in all the blocks it points to and construct
162          * a big id list containing all the ids, which we will return.
163          */
164
165         /* count the number of blocks & allocate space for pointers to them */
166         for ( i = 0; !ID_BLOCK_NOID(idl, i); i++ )
167                 ;       /* NULL */
168         tmp = (ID_BLOCK **) ch_malloc( (i + 1) * sizeof(ID_BLOCK *) );
169
170         /* read in all the blocks */
171         cont_alloc( &data, &key );
172         nids = 0;
173         for ( i = 0; !ID_BLOCK_NOID(idl, i); i++ ) {
174                 cont_id( &data, ID_BLOCK_ID(idl, i) );
175
176                 if ( (tmp[i] = idl_fetch_one( be, db, data )) == NULL ) {
177                         Debug( LDAP_DEBUG_ANY,
178                             "idl_fetch: one returned NULL\n", 0, 0, 0 );
179                         continue;
180                 }
181
182                 nids += ID_BLOCK_NIDS(tmp[i]);
183         }
184         tmp[i] = NULL;
185         cont_free( &data );
186         idl_free( idl );
187
188         /* allocate space for the big block */
189         idl = idl_alloc( nids );
190         ID_BLOCK_NIDS(idl) = nids;
191         nids = 0;
192
193         /* copy in all the ids from the component blocks */
194         for ( i = 0; tmp[i] != NULL; i++ ) {
195                 if ( tmp[i] == NULL ) {
196                         continue;
197                 }
198
199                 AC_MEMCPY(
200                         (char *) &ID_BLOCK_ID(idl, nids),
201                         (char *) &ID_BLOCK_ID(tmp[i], 0),
202                         ID_BLOCK_NIDS(tmp[i]) * sizeof(ID) );
203                 nids += ID_BLOCK_NIDS(tmp[i]);
204
205                 idl_free( tmp[i] );
206         }
207         free( (char *) tmp );
208
209         Debug( LDAP_DEBUG_TRACE, "<= idl_fetch %ld ids (%ld max)\n",
210                ID_BLOCK_NIDS(idl), ID_BLOCK_NMAX(idl), 0 );
211         return( idl );
212 }
213
214
215 /* store a single block */
216 static int
217 idl_store(
218     Backend             *be,
219     DBCache     *db,
220     Datum               key, 
221     ID_BLOCK            *idl
222 )
223 {
224         int     rc, flags;
225         Datum   data;
226         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
227
228         ldbm_datum_init( data );
229
230         /* Debug( LDAP_DEBUG_TRACE, "=> idl_store\n", 0, 0, 0 ); */
231
232         data.dptr = (char *) idl;
233         data.dsize = (ID_BLOCK_IDS_OFFSET + ID_BLOCK_NMAX(idl)) * sizeof(ID);
234         
235 #ifdef LDBM_DEBUG
236         Statslog( LDAP_DEBUG_STATS, "<= idl_store(): rc=%d\n",
237                 rc, 0, 0, 0, 0 );
238 #endif
239
240         flags = LDBM_REPLACE;
241         rc = ldbm_cache_store( db, key, data, flags );
242
243         /* Debug( LDAP_DEBUG_TRACE, "<= idl_store %d\n", rc, 0, 0 ); */
244         return( rc );
245 }
246
247 /* split the block at id 
248  *      locate ID greater than or equal to id.
249  */
250 static void
251 idl_split_block(
252     ID_BLOCK    *b,
253     ID          id,
254     ID_BLOCK    **right,
255     ID_BLOCK    **left
256 )
257 {
258         unsigned int    nr, nl;
259
260         /* find where to split the block *//* XXX linear search XXX */
261         for ( nr = 0; nr < ID_BLOCK_NIDS(b) && id > ID_BLOCK_ID(b, nr); nr++ )
262                 ;       /* NULL */
263
264         nl = ID_BLOCK_NIDS(b) - nr;
265
266         *right = idl_alloc( nr == 0 ? 1 : nr );
267         *left = idl_alloc( nl + (nr == 0 ? 0 : 1));
268
269         /*
270          * everything before the id being inserted in the first block
271          * unless there is nothing, in which case the id being inserted
272          * goes there.
273          */
274         if ( nr == 0 ) {
275                 ID_BLOCK_NIDS(*right) = 1;
276                 ID_BLOCK_ID(*right, 0) = id;
277         } else {
278                 AC_MEMCPY(
279                         (char *) &ID_BLOCK_ID(*right, 0),
280                         (char *) &ID_BLOCK_ID(b, 0),
281                         nr * sizeof(ID) );
282                 ID_BLOCK_NIDS(*right) = nr;
283                 ID_BLOCK_ID(*left, 0) = id;
284         }
285
286         /* the id being inserted & everything after in the second block */
287         AC_MEMCPY(
288                 (char *) &ID_BLOCK_ID(*left, (nr == 0 ? 0 : 1)),
289             (char *) &ID_BLOCK_ID(b, nr),
290                 nl * sizeof(ID) );
291         ID_BLOCK_NIDS(*left) = nl + (nr == 0 ? 0 : 1);
292 }
293
294
295 /*
296  * idl_change_first - called when an indirect block's first key has
297  * changed, meaning it needs to be stored under a new key, and the
298  * header block pointing to it needs updating.
299  */
300 static int
301 idl_change_first(
302     Backend             *be,
303     DBCache     *db,
304     Datum               hkey,           /* header block key     */
305     ID_BLOCK            *h,             /* header block         */
306     int                 pos,            /* pos in h to update   */
307     Datum               bkey,           /* data block key       */
308     ID_BLOCK            *b              /* data block           */
309 )
310 {
311         int     rc;
312
313         /* Debug( LDAP_DEBUG_TRACE, "=> idl_change_first\n", 0, 0, 0 ); */
314
315         /* delete old key block */
316         if ( (rc = ldbm_cache_delete( db, bkey )) != 0 ) {
317                 Debug( LDAP_DEBUG_ANY,
318                     "idl_change_first: ldbm_cache_delete returned %d\n",
319                         rc, 0, 0 );
320                 return( rc );
321         }
322
323         /* write block with new key */
324         cont_id( &bkey, ID_BLOCK_ID(b, 0) );
325
326         if ( (rc = idl_store( be, db, bkey, b )) != 0 ) {
327                 Debug( LDAP_DEBUG_ANY,
328                     "idl_change_first: idl_store returned %d\n", rc, 0, 0 );
329                 return( rc );
330         }
331
332         /* update + write indirect header block */
333         ID_BLOCK_ID(h, pos) = ID_BLOCK_ID(b, 0);
334         if ( (rc = idl_store( be, db, hkey, h )) != 0 ) {
335                 Debug( LDAP_DEBUG_ANY,
336                     "idl_change_first: idl_store returned %d\n", rc, 0, 0 );
337                 return( rc );
338         }
339
340         return( 0 );
341 }
342
343
344 int
345 idl_insert_key(
346     Backend             *be,
347     DBCache     *db,
348     Datum               key,
349     ID                  id
350 )
351 {
352         int     i, j, first, rc;
353         ID_BLOCK        *idl, *tmp, *tmp2, *tmp3;
354         Datum   k2;
355
356         if ( (idl = idl_fetch_one( be, db, key )) == NULL ) {
357                 idl = idl_alloc( 1 );
358                 ID_BLOCK_ID(idl, ID_BLOCK_NIDS(idl)++) = id;
359                 rc = idl_store( be, db, key, idl );
360
361                 idl_free( idl );
362                 return( rc );
363         }
364
365         if ( ID_BLOCK_ALLIDS( idl ) ) {
366                 /* ALLIDS */
367                 idl_free( idl );
368                 return 0;
369         }
370
371         if ( ! ID_BLOCK_INDIRECT( idl ) ) {
372                 /* regular block */
373                 switch ( idl_insert( &idl, id, db->dbc_maxids ) ) {
374                 case 0:         /* id inserted - store the updated block */
375                 case 1:
376                         rc = idl_store( be, db, key, idl );
377                         break;
378
379                 case 2:         /* id already there - nothing to do */
380                         rc = 0;
381                         break;
382
383                 case 3:         /* id not inserted - block must be split */
384                         /* check threshold for marking this an all-id block */
385                         if ( db->dbc_maxindirect < 2 ) {
386                                 idl_free( idl );
387                                 idl = idl_allids( be );
388                                 rc = idl_store( be, db, key, idl );
389                                 break;
390                         }
391
392                         idl_split_block( idl, id, &tmp, &tmp2 );
393                         idl_free( idl );
394
395                         /* create the header indirect block */
396                         idl = idl_alloc( 3 );
397                         ID_BLOCK_NMAX(idl) = 3;
398                         ID_BLOCK_NIDS(idl) = ID_BLOCK_INDIRECT_VALUE;
399                         ID_BLOCK_ID(idl, 0) = ID_BLOCK_ID(tmp, 0);
400                         ID_BLOCK_ID(idl, 1) = ID_BLOCK_ID(tmp2, 0);
401                         ID_BLOCK_ID(idl, 2) = NOID;
402
403                         /* store it */
404                         rc = idl_store( be, db, key, idl );
405
406                         cont_alloc( &k2, &key );
407                         cont_id( &k2, ID_BLOCK_ID(tmp, 0) );
408
409                         rc = idl_store( be, db, k2, tmp );
410
411                         cont_id( &k2, ID_BLOCK_ID(tmp2, 0) );
412                         rc = idl_store( be, db, k2, tmp2 );
413
414                         cont_free( &k2 );
415
416                         idl_free( tmp );
417                         idl_free( tmp2 );
418                         break;
419                 }
420
421                 idl_free( idl );
422                 return( rc );
423         }
424
425         /*
426          * this is an indirect block which points to other blocks.
427          * we need to read in the block into which the id should be
428          * inserted, then insert the id and store the block.  we might
429          * have to split the block if it is full, which means we also
430          * need to write a new "header" block.
431          */
432
433         /* select the block to try inserting into *//* XXX linear search XXX */
434         for ( i = 0; !ID_BLOCK_NOID(idl, i) && id > ID_BLOCK_ID(idl, i); i++ )
435                 ;       /* NULL */
436
437         if ( i != 0 ) {
438                 i--;
439                 first = 0;
440         } else {
441                 first = 1;
442         }
443
444         /* get the block */
445         cont_alloc( &k2, &key );
446         cont_id( &k2, ID_BLOCK_ID(idl, i) );
447
448         if ( (tmp = idl_fetch_one( be, db, k2 )) == NULL ) {
449                 Debug( LDAP_DEBUG_ANY, "idl_insert_key: nonexistent continuation block\n",
450                     0, 0, 0 );
451                 cont_free( &k2 );
452                 idl_free( idl );
453                 return( -1 );
454         }
455
456         /* insert the id */
457         switch ( idl_insert( &tmp, id, db->dbc_maxids ) ) {
458         case 0:         /* id inserted ok */
459                 if ( (rc = idl_store( be, db, k2, tmp )) != 0 ) {
460                         Debug( LDAP_DEBUG_ANY,
461                             "idl_insert_key: idl_store returned %d\n", rc, 0, 0 );
462                 }
463                 break;
464
465         case 1:         /* id inserted - first id in block has changed */
466                 /*
467                  * key for this block has changed, so we have to
468                  * write the block under the new key, delete the
469                  * old key block + update and write the indirect
470                  * header block.
471                  */
472
473                 rc = idl_change_first( be, db, key, idl, i, k2, tmp );
474                 break;
475
476         case 2:         /* id not inserted - already there, do nothing */
477                 rc = 0;
478                 break;
479
480         case 3:         /* id not inserted - block is full */
481                 /*
482                  * first, see if it will fit in the next block,
483                  * without splitting, unless we're trying to insert
484                  * into the beginning of the first block.
485                  */
486
487                 /* is there a next block? */
488                 if ( !first && !ID_BLOCK_NOID(idl, i + 1) ) {
489                         /* read it in */
490                         cont_alloc( &k2, &key );
491                         cont_id( &k2, ID_BLOCK_ID(idl, i) );
492                         if ( (tmp2 = idl_fetch_one( be, db, k2 )) == NULL ) {
493                                 Debug( LDAP_DEBUG_ANY,
494                                     "idl_insert_key: idl_fetch_one returned NULL\n",
495                                     0, 0, 0 );
496                                 /* split the original block */
497                                 cont_free( &k2 );
498                                 goto split;
499                         }
500
501                         /* If the new id is less than the last id in the
502                          * current block, it must not be put into the next
503                          * block. Push the last id of the current block
504                          * into the next block instead.
505                          */
506                         if (id < ID_BLOCK_ID(tmp, ID_BLOCK_NIDS(tmp) - 1)) {
507                             ID id2 = ID_BLOCK_ID(tmp, ID_BLOCK_NIDS(tmp) - 1);
508                             Datum k3;
509
510                             ldbm_datum_init( k3 );
511
512                             --ID_BLOCK_NIDS(tmp);
513                             /* This must succeed since we just popped one
514                              * ID off the end of it.
515                              */
516                             rc = idl_insert( &tmp, id, db->dbc_maxids );
517
518                                 k3.dptr = ch_malloc(k2.dsize);
519                                 k3.dsize = k2.dsize;
520                                 AC_MEMCPY(k3.dptr, k2.dptr, k3.dsize);
521                             if ( (rc = idl_store( be, db, k3, tmp )) != 0 ) {
522                                 Debug( LDAP_DEBUG_ANY,
523                             "idl_insert_key: idl_store returned %d\n", rc, 0, 0 );
524                             }
525
526                                 free( k3.dptr );
527
528                             id = id2;
529                             /* This new id will necessarily be inserted
530                              * as the first id of the next block by the
531                              * following switch() statement.
532                              */
533                         }
534
535                         switch ( (rc = idl_insert( &tmp2, id,
536                             db->dbc_maxids )) ) {
537                         case 1:         /* id inserted first in block */
538                                 rc = idl_change_first( be, db, key, idl,
539                                     i + 1, k2, tmp2 );
540                                 /* FALL */
541
542                         case 2:         /* id already there - how? */
543                         case 0:         /* id inserted: this can never be
544                                          * the result of idl_insert, because
545                                          * we guaranteed that idl_change_first
546                                          * will always be called.
547                                          */
548                                 if ( rc == 2 ) {
549                                         Debug( LDAP_DEBUG_ANY,
550                                             "idl_insert_key: id %ld already in next block\n",
551                                             id, 0, 0 );
552                                 }
553
554                                 assert( 0 ); /* not yet implemented */
555                                 idl_free( tmp );
556                                 idl_free( tmp2 );
557                                 idl_free( idl );
558                                 return( 0 );
559
560                         case 3:         /* split the original block */
561                                 break;
562                         }
563
564                         idl_free( tmp2 );
565                 }
566
567 split:
568                 /*
569                  * must split the block, write both new blocks + update
570                  * and write the indirect header block.
571                  */
572
573                 rc = 0; /* optimistic */
574
575
576                 /* count how many indirect blocks *//* XXX linear count XXX */
577                 for ( j = 0; !ID_BLOCK_NOID(idl, j); j++ )
578                         ;       /* NULL */
579
580                 /* check it against all-id thresholed */
581                 if ( j + 1 > db->dbc_maxindirect ) {
582                         /*
583                          * we've passed the all-id threshold, meaning
584                          * that this set of blocks should be replaced
585                          * by a single "all-id" block.  our job: delete
586                          * all the indirect blocks, and replace the header
587                          * block by an all-id block.
588                          */
589
590                         /* delete all indirect blocks */
591                         for ( j = 0; !ID_BLOCK_NOID(idl, j); j++ ) {
592                                 cont_id( &k2, ID_BLOCK_ID(idl, j) );
593
594                                 rc = ldbm_cache_delete( db, k2 );
595                         }
596
597                         /* store allid block in place of header block */
598                         idl_free( idl );
599                         idl = idl_allids( be );
600                         rc = idl_store( be, db, key, idl );
601
602                         cont_free( &k2 );
603                         idl_free( idl );
604                         idl_free( tmp );
605                         return( rc );
606                 }
607
608                 idl_split_block( tmp, id, &tmp2, &tmp3 );
609                 idl_free( tmp );
610
611                 /* create a new updated indirect header block */
612                 tmp = idl_alloc( ID_BLOCK_NMAX(idl) + 1 );
613                 ID_BLOCK_NIDS(tmp) = ID_BLOCK_INDIRECT_VALUE;
614                 /* everything up to the split block */
615                 AC_MEMCPY(
616                         (char *) &ID_BLOCK_ID(tmp, 0),
617                         (char *) &ID_BLOCK_ID(idl, 0),
618                     i * sizeof(ID) );
619                 /* the two new blocks */
620                 ID_BLOCK_ID(tmp, i) = ID_BLOCK_ID(tmp2, 0);
621                 ID_BLOCK_ID(tmp, i + 1) = ID_BLOCK_ID(tmp3, 0);
622                 /* everything after the split block */
623                 AC_MEMCPY(
624                         (char *) &ID_BLOCK_ID(tmp, i + 2),
625                         (char *) &ID_BLOCK_ID(idl, i + 1),
626                         (ID_BLOCK_NMAX(idl) - i - 1) * sizeof(ID) );
627
628                 /* store the header block */
629                 rc = idl_store( be, db, key, tmp );
630
631                 /* store the first id block */
632                 cont_id( &k2, ID_BLOCK_ID(tmp2, 0) );
633                 rc = idl_store( be, db, k2, tmp2 );
634
635                 /* store the second id block */
636                 cont_id( &k2, ID_BLOCK_ID(tmp3, 0) );
637                 rc = idl_store( be, db, k2, tmp3 );
638
639                 idl_free( tmp2 );
640                 idl_free( tmp3 );
641                 break;
642         }
643
644         cont_free( &k2 );
645         idl_free( tmp );
646         idl_free( idl );
647         return( rc );
648 }
649
650
651 /*
652  * idl_insert - insert an id into an id list.
653  *
654  *      returns
655  *              0       id inserted
656  *              1       id inserted, first id in block has changed
657  *              2       id not inserted, already there
658  *              3       id not inserted, block must be split
659  */
660 int
661 idl_insert( ID_BLOCK **idl, ID id, unsigned int maxids )
662 {
663         unsigned int    i;
664
665         if ( ID_BLOCK_ALLIDS( *idl ) ) {
666                 return( 2 );    /* already there */
667         }
668
669         /* is it already there? *//* XXX linear search XXX */
670         for ( i = 0; i < ID_BLOCK_NIDS(*idl) && id > ID_BLOCK_ID(*idl, i); i++ ) {
671                 ;       /* NULL */
672         }
673         if ( i < ID_BLOCK_NIDS(*idl) && ID_BLOCK_ID(*idl, i) == id ) {
674                 return( 2 );    /* already there */
675         }
676
677         /* do we need to make room for it? */
678         if ( ID_BLOCK_NIDS(*idl) == ID_BLOCK_NMAX(*idl) ) {
679                 /* make room or indicate block needs splitting */
680                 if ( ID_BLOCK_NMAX(*idl) >= maxids ) {
681                         return( 3 );    /* block needs splitting */
682                 }
683
684                 ID_BLOCK_NMAX(*idl) *= 2;
685                 if ( ID_BLOCK_NMAX(*idl) > maxids ) {
686                         ID_BLOCK_NMAX(*idl) = maxids;
687                 }
688                 *idl = (ID_BLOCK *) ch_realloc( (char *) *idl,
689                     (ID_BLOCK_NMAX(*idl) + ID_BLOCK_IDS_OFFSET) * sizeof(ID) );
690         }
691
692         /* make a slot for the new id */
693         AC_MEMCPY( &ID_BLOCK_ID(*idl, i+1), &ID_BLOCK_ID(*idl, i),
694                     (ID_BLOCK_NIDS(*idl) - i) * sizeof(ID) );
695
696         ID_BLOCK_ID(*idl, i) = id;
697         ID_BLOCK_NIDS(*idl)++;
698         (void) memset(
699                 (char *) &ID_BLOCK_ID((*idl), ID_BLOCK_NIDS(*idl)),
700                 '\0',
701             (ID_BLOCK_NMAX(*idl) - ID_BLOCK_NIDS(*idl)) * sizeof(ID) );
702
703         return( i == 0 ? 1 : 0 );       /* inserted - first id changed or not */
704 }
705
706
707 int
708 idl_delete_key (
709         Backend         *be,
710         DBCache  *db,
711         Datum           key,
712         ID              id
713 )
714 {
715         Datum  data;
716         ID_BLOCK *idl;
717         unsigned i;
718         int j, nids;
719
720         if ( (idl = idl_fetch_one( be, db, key ) ) == NULL )
721         {
722                 /* It wasn't found.  Hmm... */
723                 return -1;
724         }
725
726         if ( ID_BLOCK_ALLIDS( idl ) ) {
727                 idl_free( idl );
728                 return 0;
729         }
730
731         if ( ! ID_BLOCK_INDIRECT( idl ) ) {
732                 for ( i=0; i < ID_BLOCK_NIDS(idl); i++ ) {
733                         if ( ID_BLOCK_ID(idl, i) == id ) {
734                                 if( --ID_BLOCK_NIDS(idl) == 0 ) {
735                                         ldbm_cache_delete( db, key );
736
737                                 } else {
738                                         AC_MEMCPY(
739                                                 &ID_BLOCK_ID(idl, i),
740                                                 &ID_BLOCK_ID(idl, i+1),
741                                                 (ID_BLOCK_NIDS(idl)-i) * sizeof(ID) );
742
743                                         ID_BLOCK_ID(idl, ID_BLOCK_NIDS(idl)) = NOID;
744
745                                         idl_store( be, db, key, idl );
746                                 }
747
748                                 idl_free( idl );
749                                 return 0;
750                         }
751                         /*  We didn't find the ID.  Hmmm... */
752                 }
753                 idl_free( idl );
754                 return -1;
755         }
756         
757         /* We have to go through an indirect block and find the ID
758            in the list of IDL's
759            */
760         for ( nids = 0; !ID_BLOCK_NOID(idl, nids); nids++ )
761                 ;       /* NULL */
762
763         cont_alloc( &data, &key );
764
765         for ( j = 0; !ID_BLOCK_NOID(idl, j); j++ ) 
766         {
767                 ID_BLOCK *tmp;
768                 cont_id( &data, ID_BLOCK_ID(idl, j) );
769
770                 if ( (tmp = idl_fetch_one( be, db, data )) == NULL ) {
771                         Debug( LDAP_DEBUG_ANY,
772                             "idl_delete_key: idl_fetch of returned NULL\n", 0, 0, 0 );
773                         continue;
774                 }
775                 /*
776                    Now try to find the ID in tmp
777                 */
778                 for ( i=0; i < ID_BLOCK_NIDS(tmp); i++ )
779                 {
780                         if ( ID_BLOCK_ID(tmp, i) == id )
781                         {
782                                 AC_MEMCPY(
783                                         &ID_BLOCK_ID(tmp, i),
784                                         &ID_BLOCK_ID(tmp, i+1),
785                                         (ID_BLOCK_NIDS(tmp)-(i+1)) * sizeof(ID));
786                                 ID_BLOCK_ID(tmp, ID_BLOCK_NIDS(tmp)-1 ) = NOID;
787                                 ID_BLOCK_NIDS(tmp)--;
788
789                                 if ( ID_BLOCK_NIDS(tmp) ) {
790                                         idl_store ( be, db, data, tmp );
791
792                                 } else {
793                                         ldbm_cache_delete( db, data );
794                                         AC_MEMCPY(
795                                                 &ID_BLOCK_ID(idl, j),
796                                                 &ID_BLOCK_ID(idl, j+1),
797                                                 (nids-(j+1)) * sizeof(ID));
798                                         ID_BLOCK_ID(idl, nids-1) = NOID;
799                                         nids--;
800                                         if ( ! nids )
801                                                 ldbm_cache_delete( db, key );
802                                         else
803                                                 idl_store( be, db, key, idl );
804                                 }
805                                 idl_free( tmp );
806                                 cont_free( &data );
807                                 idl_free( idl );
808                                 return 0;
809                         }
810                 }
811                 idl_free( tmp );
812         }
813
814         cont_free( &data );
815         idl_free( idl );
816         return -1;
817 }
818
819
820 /* return a duplicate of a single ID_BLOCK */
821 static ID_BLOCK *
822 idl_dup( ID_BLOCK *idl )
823 {
824         ID_BLOCK        *new;
825
826         if ( idl == NULL ) {
827                 return( NULL );
828         }
829
830         new = idl_alloc( ID_BLOCK_NMAX(idl) );
831
832         AC_MEMCPY(
833                 (char *) new,
834                 (char *) idl,
835                 (ID_BLOCK_NMAX(idl) + ID_BLOCK_IDS_OFFSET) * sizeof(ID) );
836
837         return( new );
838 }
839
840
841 /* return the smaller ID_BLOCK */
842 static ID_BLOCK *
843 idl_min( ID_BLOCK *a, ID_BLOCK *b )
844 {
845         return( ID_BLOCK_NIDS(a) > ID_BLOCK_NIDS(b) ? b : a );
846 }
847
848
849 /*
850  * idl_intersection - return a intersection b
851  */
852 ID_BLOCK *
853 idl_intersection(
854     Backend     *be,
855     ID_BLOCK    *a,
856     ID_BLOCK    *b
857 )
858 {
859         unsigned int    ai, bi, ni;
860         ID_BLOCK                *n;
861
862         if ( a == NULL || b == NULL ) {
863                 return( NULL );
864         }
865         if ( ID_BLOCK_ALLIDS( a ) ) {
866                 return( idl_dup( b ) );
867         }
868         if ( ID_BLOCK_ALLIDS( b ) ) {
869                 return( idl_dup( a ) );
870         }
871
872         n = idl_dup( idl_min( a, b ) );
873
874         for ( ni = 0, ai = 0, bi = 0; ai < ID_BLOCK_NIDS(a); ai++ ) {
875                 for ( ;
876                         bi < ID_BLOCK_NIDS(b) && ID_BLOCK_ID(b, bi) < ID_BLOCK_ID(a, ai);
877                         bi++ )
878                 {
879                         ;       /* NULL */
880                 }
881
882                 if ( bi == ID_BLOCK_NIDS(b) ) {
883                         break;
884                 }
885
886                 if ( ID_BLOCK_ID(b, bi) == ID_BLOCK_ID(a, ai) ) {
887                         ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai);
888                 }
889         }
890
891         if ( ni == 0 ) {
892                 idl_free( n );
893                 return( NULL );
894         }
895         ID_BLOCK_NIDS(n) = ni;
896
897         return( n );
898 }
899
900
901 /*
902  * idl_union - return a union b
903  */
904 ID_BLOCK *
905 idl_union(
906     Backend     *be,
907     ID_BLOCK    *a,
908     ID_BLOCK    *b
909 )
910 {
911         unsigned int    ai, bi, ni;
912         ID_BLOCK                *n;
913
914         if ( a == NULL ) {
915                 return( idl_dup( b ) );
916         }
917         if ( b == NULL ) {
918                 return( idl_dup( a ) );
919         }
920         if ( ID_BLOCK_ALLIDS( a ) || ID_BLOCK_ALLIDS( b ) ) {
921                 return( idl_allids( be ) );
922         }
923
924         if ( ID_BLOCK_NIDS(b) < ID_BLOCK_NIDS(a) ) {
925                 n = a;
926                 a = b;
927                 b = n;
928         }
929
930         n = idl_alloc( ID_BLOCK_NIDS(a) + ID_BLOCK_NIDS(b) );
931
932         for ( ni = 0, ai = 0, bi = 0;
933                 ai < ID_BLOCK_NIDS(a) && bi < ID_BLOCK_NIDS(b);
934                 )
935         {
936                 if ( ID_BLOCK_ID(a, ai) < ID_BLOCK_ID(b, bi) ) {
937                         ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai++);
938
939                 } else if ( ID_BLOCK_ID(b, bi) < ID_BLOCK_ID(a, ai) ) {
940                         ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(b, bi++);
941
942                 } else {
943                         ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai);
944                         ai++, bi++;
945                 }
946         }
947
948         for ( ; ai < ID_BLOCK_NIDS(a); ai++ ) {
949                 ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai);
950         }
951         for ( ; bi < ID_BLOCK_NIDS(b); bi++ ) {
952                 ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(b, bi);
953         }
954         ID_BLOCK_NIDS(n) = ni;
955
956         return( n );
957 }
958
959
960 /*
961  * idl_notin - return a intersection ~b (or a minus b)
962  */
963 ID_BLOCK *
964 idl_notin(
965     Backend     *be,
966     ID_BLOCK    *a,
967     ID_BLOCK    *b
968 )
969 {
970         unsigned int    ni, ai, bi;
971         ID_BLOCK                *n;
972
973         if ( a == NULL ) {
974                 return( NULL );
975         }
976         if ( b == NULL || ID_BLOCK_ALLIDS( b )) {
977                 return( idl_dup( a ) );
978         }
979
980         if ( ID_BLOCK_ALLIDS( a ) ) {
981                 n = idl_alloc( SLAPD_LDBM_MIN_MAXIDS );
982                 ni = 0;
983
984                 for ( ai = 1, bi = 0;
985                         ai < ID_BLOCK_NIDS(a) && ni < ID_BLOCK_NMAX(n) && bi < ID_BLOCK_NMAX(b);
986                         ai++ )
987                 {
988                         if ( ID_BLOCK_ID(b, bi) == ai ) {
989                                 bi++;
990                         } else {
991                                 ID_BLOCK_ID(n, ni++) = ai;
992                         }
993                 }
994
995                 for ( ; ai < ID_BLOCK_NIDS(a) && ni < ID_BLOCK_NMAX(n); ai++ ) {
996                         ID_BLOCK_ID(n, ni++) = ai;
997                 }
998
999                 if ( ni == ID_BLOCK_NMAX(n) ) {
1000                         idl_free( n );
1001                         return( idl_allids( be ) );
1002                 } else {
1003                         ID_BLOCK_NIDS(n) = ni;
1004                         return( n );
1005                 }
1006         }
1007
1008         n = idl_dup( a );
1009
1010         ni = 0;
1011         for ( ai = 0, bi = 0; ai < ID_BLOCK_NIDS(a); ai++ ) {
1012                 for ( ;
1013                         bi < ID_BLOCK_NIDS(b) && ID_BLOCK_ID(b, bi) < ID_BLOCK_ID(a, ai);
1014                     bi++ )
1015                 {
1016                         ;       /* NULL */
1017                 }
1018
1019                 if ( bi == ID_BLOCK_NIDS(b) ) {
1020                         break;
1021                 }
1022
1023                 if ( ID_BLOCK_ID(b, bi) != ID_BLOCK_ID(a, ai) ) {
1024                         ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai);
1025                 }
1026         }
1027
1028         for ( ; ai < ID_BLOCK_NIDS(a); ai++ ) {
1029                 ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai);
1030         }
1031         ID_BLOCK_NIDS(n) = ni;
1032
1033         return( n );
1034 }
1035
1036 /*      return the first ID in the block
1037  *      if ALLIDS block
1038  *              NIDS > 1 return 1
1039  *              otherwise return NOID 
1040  *      otherwise return first ID
1041  *
1042  *      cursor is set to 1
1043  */         
1044 ID
1045 idl_firstid( ID_BLOCK *idl, ID *cursor )
1046 {
1047         *cursor = 1;
1048
1049         if ( idl == NULL || ID_BLOCK_NIDS(idl) == 0 ) {
1050                 return( NOID );
1051         }
1052
1053         if ( ID_BLOCK_ALLIDS( idl ) ) {
1054                 return( ID_BLOCK_NIDS(idl) > 1 ? 1 : NOID );
1055         }
1056
1057         return( ID_BLOCK_ID(idl, 0) );
1058 }
1059
1060 /*      return next ID
1061  *      if ALLIDS block, cursor is id.
1062  *              increment id
1063  *              if id < NIDS return id
1064  *              otherwise NOID.
1065  *      otherwise cursor is index into block
1066  *              if index < nids
1067  *                      return id at index then increment
1068  */ 
1069 ID
1070 idl_nextid( ID_BLOCK *idl, ID *cursor )
1071 {
1072         if ( ID_BLOCK_ALLIDS( idl ) ) {
1073                 if( ++(*cursor) < ID_BLOCK_NIDS(idl) ) {
1074                         return *cursor;
1075                 } else {
1076                         return NOID;
1077                 }
1078         }
1079
1080         if ( *cursor < ID_BLOCK_NIDS(idl) ) {
1081                 return( ID_BLOCK_ID(idl, (*cursor)++) );
1082         }
1083
1084         return( NOID );
1085 }