]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/idl.c
Rewrote entry_encode/entry_decode again, uses 50% less disk space.
[openldap] / servers / slapd / back-bdb / 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 #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;
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 static int 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         data.data = ids;
231         data.ulen = BDB_IDL_UM_SIZEOF;
232         data.flags = DB_DBT_USERMEM;
233
234         /* fetch it */
235         rc = db->get( db, tid, key, &data, bdb->bi_db_opflags );
236
237         if( rc == DB_NOTFOUND ) {
238                 return rc;
239
240         } else if( rc != 0 ) {
241                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
242                         "get failed: %s (%d)\n",
243                         db_strerror(rc), rc, 0 );
244                 return rc;
245
246         } else if ( data.size == 0 || data.size % sizeof( ID ) ) {
247                 /* size not multiple of ID size */
248                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
249                         "odd size: expected %ld multiple, got %ld\n",
250                         (long) sizeof( ID ), (long) data.size, 0 );
251                 return -1;
252
253         } else if ( data.size != BDB_IDL_SIZEOF(ids) ) {
254                 /* size mismatch */
255                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
256                         "get size mismatch: expected %ld, got %ld\n",
257                         (long) ((1 + ids[0]) * sizeof( ID )), (long) data.size, 0 );
258                 return -1;
259         }
260
261         return rc;
262 }
263
264 int
265 bdb_idl_insert_key(
266         BackendDB       *be,
267         DB                      *db,
268         DB_TXN          *tid,
269         DBT                     *key,
270         ID                      id )
271 {
272         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
273         int     rc;
274         ID ids[BDB_IDL_DB_SIZE];
275         DBT data;
276
277         /* for printable keys only */
278         Debug( LDAP_DEBUG_ARGS,
279                 "=> bdb_idl_insert_key: %s %ld\n",
280                 (char *)key->data, (long) id, 0 );
281
282         assert( id != NOID );
283
284         data.data = ids;
285         data.ulen = sizeof ids;
286         data.flags = DB_DBT_USERMEM;
287
288         /* fetch the key for read/modify/write */
289         rc = db->get( db, tid, key, &data, DB_RMW | bdb->bi_db_opflags );
290
291         if( rc == DB_NOTFOUND ) {
292                 ids[0] = 1;
293                 ids[1] = id;
294                 data.size = 2 * sizeof( ID );
295
296         } else if ( rc != 0 ) {
297                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
298                         "get failed: %s (%d)\n",
299                         db_strerror(rc), rc, 0 );
300                 return rc;
301
302         } else if ( data.size == 0 || data.size % sizeof( ID ) ) {
303                 /* size not multiple of ID size */
304                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
305                         "odd size: expected %ld multiple, got %ld\n",
306                         (long) sizeof( ID ), (long) data.size, 0 );
307                 return -1;
308         
309         } else if ( data.size != BDB_IDL_SIZEOF(ids) ) {
310                 /* size mismatch */
311                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
312                         "get size mismatch: expected %ld, got %ld\n",
313                         (long) ((1 + ids[0]) * sizeof( ID )), (long) data.size, 0 );
314                 return -1;
315
316         } else if ( BDB_IDL_IS_RANGE(ids) ) {
317                 if( id < ids[1] ) {
318                         ids[1] = id;
319                 } else if ( ids[2] > id ) {
320                         ids[2] = id;
321                 } else {
322                         return 0;
323                 }
324
325         } else {
326                 rc = idl_insert( ids, id );
327
328                 if( rc == -1 ) {
329                         Debug( LDAP_DEBUG_TRACE, "=> bdb_idl_insert_key: dup\n",
330                                 0, 0, 0 );
331                         return 0;
332                 }
333                 if( rc != 0 ) {
334                         Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
335                                 "idl_insert failed (%d)\n",
336                                 rc, 0, 0 );
337                         
338                         return rc;
339                 }
340
341                 data.size = BDB_IDL_SIZEOF( ids );
342         }
343
344         /* store the key */
345         rc = db->put( db, tid, key, &data, 0 );
346
347         if( rc != 0 ) {
348                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
349                         "put failed: %s (%d)\n",
350                         db_strerror(rc), rc, 0 );
351         }
352         return rc;
353 }
354
355 int
356 bdb_idl_delete_key(
357         BackendDB       *be,
358         DB                      *db,
359         DB_TXN          *tid,
360         DBT                     *key,
361         ID                      id )
362 {
363         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
364         int     rc;
365         ID ids[BDB_IDL_DB_SIZE];
366         DBT data;
367
368         /* for printable keys only */
369         Debug( LDAP_DEBUG_ARGS,
370                 "=> bdb_idl_delete_key: %s %ld\n",
371                 (char *)key->data, (long) id, 0 );
372
373         assert( id != NOID );
374
375         data.data = ids;
376         data.ulen = sizeof( ids );
377         data.flags = DB_DBT_USERMEM;
378
379         /* fetch the key for read/modify/write */
380         rc = db->get( db, tid, key, &data, DB_RMW | bdb->bi_db_opflags );
381
382         if ( rc != 0 ) {
383                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
384                         "get failed: %s (%d)\n",
385                         db_strerror(rc), rc, 0 );
386                 return rc;
387
388         } else if ( data.size == 0 || data.size % sizeof( ID ) ) {
389                 /* size not multiple of ID size */
390                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
391                         "odd size: expected %ld multiple, got %ld\n",
392                         (long) sizeof( ID ), (long) data.size, 0 );
393                 return -1;
394         
395         } else if ( BDB_IDL_IS_RANGE(ids) ) {
396                 return 0;
397
398         } else if ( data.size != (1 + ids[0]) * sizeof( ID ) ) {
399                 /* size mismatch */
400                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
401                         "get size mismatch: expected %ld, got %ld\n",
402                         (long) ((1 + ids[0]) * sizeof( ID )), (long) data.size, 0 );
403                 return -1;
404
405         } else {
406                 rc = idl_delete( ids, id );
407
408                 if( rc != 0 ) {
409                         Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
410                                 "idl_delete failed (%d)\n",
411                                 rc, 0, 0 );
412                         return rc;
413                 }
414
415                 if( ids[0] == 0 ) {
416                         /* delete the key */
417                         rc = db->del( db, tid, key, 0 );
418                         if( rc != 0 ) {
419                                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
420                                         "delete failed: %s (%d)\n",
421                                         db_strerror(rc), rc, 0 );
422                         }
423                         return rc;
424                 }
425
426                 data.size = (ids[0]+1) * sizeof( ID );
427         }
428
429         /* store the key */
430         rc = db->put( db, tid, key, &data, 0 );
431
432         if( rc != 0 ) {
433                 Debug( LDAP_DEBUG_ANY,
434                         "=> bdb_idl_delete_key: put failed: %s (%d)\n",
435                         db_strerror(rc), rc, 0 );
436         }
437
438         return rc;
439 }
440
441
442 /*
443  * idl_intersection - return a = a intersection b
444  */
445 int
446 bdb_idl_intersection(
447         ID *a,
448         ID *b )
449 {
450         ID ida, idb;
451         ID idmax, idmin;
452         ID cursora = 0, cursorb = 0, cursorc;
453         int swap = 0;
454
455         if ( BDB_IDL_IS_ZERO( a ) || BDB_IDL_IS_ZERO( b ) ) {
456                 a[0] = 0;
457                 return 0;
458         }
459
460         idmin = IDL_MAX( BDB_IDL_FIRST(a), BDB_IDL_FIRST(b) );
461         idmax = IDL_MIN( BDB_IDL_LAST(a), BDB_IDL_LAST(b) );
462         if ( idmin > idmax ) {
463                 a[0] = 0;
464                 return 0;
465         } else if ( idmin == idmax ) {
466                 a[0] = 1;
467                 a[1] = idmin;
468                 return 0;
469         }
470
471         if ( BDB_IDL_IS_RANGE( a ) && BDB_IDL_IS_RANGE(b) ) {
472                 a[1] = idmin;
473                 a[2] = idmax;
474                 return 0;
475         }
476
477         if ( BDB_IDL_IS_RANGE( a ) ) {
478                 ID *tmp = a;
479                 a = b;
480                 b = tmp;
481                 swap = 1;
482         }
483
484         if ( BDB_IDL_IS_RANGE( b ) && BDB_IDL_FIRST( b ) <= idmin &&
485                 BDB_IDL_LAST( b ) >= idmax) {
486                 if (idmax - idmin + 1 == a[0])
487                 {
488                         a[0] = NOID;
489                         a[1] = idmin;
490                         a[2] = idmax;
491                 }
492                 goto done;
493         }
494
495         ida = bdb_idl_first( a, &cursora );
496         idb = bdb_idl_first( b, &cursorb );
497         cursorc = 0;
498
499         while( ida < idmin )
500                 ida = bdb_idl_next( a, &cursora );
501         while( idb < idmin )
502                 idb = bdb_idl_next( b, &cursorb );
503
504         while( ida <= idmax || idb <= idmax ) {
505                 if( ida == idb ) {
506                         a[++cursorc] = ida;
507                         ida = bdb_idl_next( a, &cursora );
508                         idb = bdb_idl_next( b, &cursorb );
509                 } else if ( ida < idb ) {
510                         ida = bdb_idl_next( a, &cursora );
511                 } else {
512                         idb = bdb_idl_next( b, &cursorb );
513                 }
514         }
515         a[0] = cursorc;
516 done:
517         if (swap)
518                 BDB_IDL_CPY( b, a );
519
520         return 0;
521 }
522
523
524 /*
525  * idl_union - return a = a union b
526  */
527 int
528 bdb_idl_union(
529         ID      *a,
530         ID      *b )
531 {
532         ID ida, idb;
533         ID cursora = 0, cursorb = 0, cursorc;
534
535         if ( BDB_IDL_IS_ZERO( b ) ) {
536                 return 0;
537         }
538
539         if ( BDB_IDL_IS_ZERO( a ) ) {
540                 BDB_IDL_CPY( a, b );
541                 return 0;
542         }
543
544         if ( BDB_IDL_IS_RANGE( a ) || BDB_IDL_IS_RANGE(b) ) {
545 over:           a[1] = IDL_MIN( BDB_IDL_FIRST(a), BDB_IDL_FIRST(b) );
546                 a[2] = IDL_MAX( BDB_IDL_LAST(a), BDB_IDL_LAST(b) );
547                 return 0;
548         }
549
550         ida = bdb_idl_first( a, &cursora );
551         idb = bdb_idl_first( b, &cursorb );
552
553         cursorc = b[0];
554
555         /* The distinct elements of a are cat'd to b */
556         while( ida != NOID || idb != NOID ) {
557                 if ( ida < idb ) {
558                         if( ++cursorc > BDB_IDL_UM_MAX ) {
559                                 a[0] = NOID;
560                                 goto over;
561                         }
562                         b[cursorc] = ida;
563                         ida = bdb_idl_next( a, &cursora );
564
565                 } else {
566                         if ( ida == idb )
567                                 ida = bdb_idl_next( a, &cursora );
568                         idb = bdb_idl_next( b, &cursorb );
569                 }
570         }
571
572         /* b is copied back to a in sorted order */
573         a[0] = cursorc;
574         cursora = 1;
575         cursorb = 1;
576         cursorc = b[0]+1;
577         while (cursorb <= b[0] || cursorc <= a[0]) {
578                 if (cursorc > a[0])
579                         idb = NOID;
580                 else
581                         idb = b[cursorc];
582                 if (b[cursorb] < idb)
583                         a[cursora++] = b[cursorb++];
584                 else {
585                         a[cursora++] = idb;
586                         cursorc++;
587                 }
588         }
589
590         return 0;
591 }
592
593
594 #if 0
595 /*
596  * bdb_idl_notin - return a intersection ~b (or a minus b)
597  */
598 int
599 bdb_idl_notin(
600         ID      *a,
601         ID      *b,
602         ID *ids )
603 {
604         ID ida, idb;
605         ID cursora = 0, cursorb = 0;
606
607         if( BDB_IDL_IS_ZERO( a ) ||
608                 BDB_IDL_IS_ZERO( b ) ||
609                 BDB_IDL_IS_RANGE( b ) )
610         {
611                 BDB_IDL_CPY( ids, a );
612                 return 0;
613         }
614
615         if( BDB_IDL_IS_RANGE( a ) ) {
616                 BDB_IDL_CPY( ids, a );
617                 return 0;
618         }
619
620         ida = bdb_idl_first( a, &cursora ),
621         idb = bdb_idl_first( b, &cursorb );
622
623         ids[0] = 0;
624
625         while( ida != NOID ) {
626                 if ( idb == NOID ) {
627                         /* we could shortcut this */
628                         ids[++ids[0]] = ida;
629                         ida = bdb_idl_next( a, &cursora );
630
631                 } else if ( ida < idb ) {
632                         ids[++ids[0]] = ida;
633                         ida = bdb_idl_next( a, &cursora );
634
635                 } else if ( ida > idb ) {
636                         idb = bdb_idl_next( b, &cursorb );
637
638                 } else {
639                         ida = bdb_idl_next( a, &cursora );
640                         idb = bdb_idl_next( b, &cursorb );
641                 }
642         }
643
644         return 0;
645 }
646 #endif
647
648 ID bdb_idl_first( ID *ids, ID *cursor )
649 {
650         ID pos;
651
652         if ( ids[0] == 0 ) {
653                 *cursor = NOID;
654                 return NOID;
655         }
656
657         if ( BDB_IDL_IS_RANGE( ids ) ) {
658                 if( *cursor < ids[1] ) {
659                         *cursor = ids[1];
660                 }
661                 return *cursor;
662         }
663
664         if ( *cursor == 0 )
665                 pos = 1;
666         else
667                 pos = bdb_idl_search( ids, *cursor );
668
669         if( pos > ids[0] ) {
670                 return NOID;
671         }
672
673         *cursor = pos;
674         return ids[pos];
675 }
676
677 ID bdb_idl_next( ID *ids, ID *cursor )
678 {
679         if ( BDB_IDL_IS_RANGE( ids ) ) {
680                 if( ids[2] < ++(*cursor) ) {
681                         return NOID;
682                 }
683                 return *cursor;
684         }
685
686         if ( ++(*cursor) <= ids[0] ) {
687                 return ids[*cursor];
688         }
689
690         return NOID;
691 }