1 /** @file midl.c
2 * @brief ldap bdb back-end ID List functions */
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 2000-2021 The OpenLDAP Foundation.
7 * Portions Copyright 2001-2021 Howard Chu, Symas Corp.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
12 * Public License.
13 *
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
17 */
18
19 #include <limits.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <errno.h>
23 #include <sys/types.h>
24 #include "midl.h"
25
26 /** @defgroup internal LMDB Internals
27 * @{
28 */
29 /** @defgroup idls ID List Management
30 * @{
31 */
32 #define CMP(x,y) ( (x) < (y) ? -1 : (x) > (y) )
33
mdb_midl_search(MDB_IDL ids,MDB_ID id)34 unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id )
35 {
36 /*
37 * binary search of id in ids
38 * if found, returns position of id
39 * if not found, returns first position greater than id
40 */
41 unsigned base = 0;
42 unsigned cursor = 1;
43 int val = 0;
44 unsigned n = ids[0];
45
46 while( 0 < n ) {
47 unsigned pivot = n >> 1;
48 cursor = base + pivot + 1;
49 val = CMP( ids[cursor], id );
50
51 if( val < 0 ) {
52 n = pivot;
53
54 } else if ( val > 0 ) {
55 base = cursor;
56 n -= pivot + 1;
57
58 } else {
59 return cursor;
60 }
61 }
62
63 if( val > 0 ) {
64 ++cursor;
65 }
66 return cursor;
67 }
68
69 #if 0 /* superseded by append/sort */
70 int mdb_midl_insert( MDB_IDL ids, MDB_ID id )
71 {
72 unsigned x, i;
73
74 x = mdb_midl_search( ids, id );
75 assert( x > 0 );
76
77 if( x < 1 ) {
78 /* internal error */
79 return -2;
80 }
81
82 if ( x <= ids[0] && ids[x] == id ) {
83 /* duplicate */
84 assert(0);
85 return -1;
86 }
87
88 if ( ++ids[0] >= MDB_IDL_DB_MAX ) {
89 /* no room */
90 --ids[0];
91 return -2;
92
93 } else {
94 /* insert id */
95 for (i=ids[0]; i>x; i--)
96 ids[i] = ids[i-1];
97 ids[x] = id;
98 }
99
100 return 0;
101 }
102 #endif
103
mdb_midl_alloc(int num)104 MDB_IDL mdb_midl_alloc(int num)
105 {
106 MDB_IDL ids = malloc((num+2) * sizeof(MDB_ID));
107 if (ids) {
108 *ids++ = num;
109 *ids = 0;
110 }
111 return ids;
112 }
113
mdb_midl_free(MDB_IDL ids)114 void mdb_midl_free(MDB_IDL ids)
115 {
116 if (ids)
117 free(ids-1);
118 }
119
mdb_midl_shrink(MDB_IDL * idp)120 void mdb_midl_shrink( MDB_IDL *idp )
121 {
122 MDB_IDL ids = *idp;
123 if (*(--ids) > MDB_IDL_UM_MAX &&
124 (ids = realloc(ids, (MDB_IDL_UM_MAX+2) * sizeof(MDB_ID))))
125 {
126 *ids++ = MDB_IDL_UM_MAX;
127 *idp = ids;
128 }
129 }
130
mdb_midl_grow(MDB_IDL * idp,int num)131 static int mdb_midl_grow( MDB_IDL *idp, int num )
132 {
133 MDB_IDL idn = *idp-1;
134 /* grow it */
135 idn = realloc(idn, (*idn + num + 2) * sizeof(MDB_ID));
136 if (!idn)
137 return ENOMEM;
138 *idn++ += num;
139 *idp = idn;
140 return 0;
141 }
142
mdb_midl_need(MDB_IDL * idp,unsigned num)143 int mdb_midl_need( MDB_IDL *idp, unsigned num )
144 {
145 MDB_IDL ids = *idp;
146 num += ids[0];
147 if (num > ids[-1]) {
148 num = (num + num/4 + (256 + 2)) & -256;
149 if (!(ids = realloc(ids-1, num * sizeof(MDB_ID))))
150 return ENOMEM;
151 *ids++ = num - 2;
152 *idp = ids;
153 }
154 return 0;
155 }
156
mdb_midl_append(MDB_IDL * idp,MDB_ID id)157 int mdb_midl_append( MDB_IDL *idp, MDB_ID id )
158 {
159 MDB_IDL ids = *idp;
160 /* Too big? */
161 if (ids[0] >= ids[-1]) {
162 if (mdb_midl_grow(idp, MDB_IDL_UM_MAX))
163 return ENOMEM;
164 ids = *idp;
165 }
166 ids[0]++;
167 ids[ids[0]] = id;
168 return 0;
169 }
170
mdb_midl_append_list(MDB_IDL * idp,MDB_IDL app)171 int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app )
172 {
173 MDB_IDL ids = *idp;
174 /* Too big? */
175 if (ids[0] + app[0] >= ids[-1]) {
176 if (mdb_midl_grow(idp, app[0]))
177 return ENOMEM;
178 ids = *idp;
179 }
180 memcpy(&ids[ids[0]+1], &app[1], app[0] * sizeof(MDB_ID));
181 ids[0] += app[0];
182 return 0;
183 }
184
mdb_midl_append_range(MDB_IDL * idp,MDB_ID id,unsigned n)185 int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n )
186 {
187 MDB_ID *ids = *idp, len = ids[0];
188 /* Too big? */
189 if (len + n > ids[-1]) {
190 if (mdb_midl_grow(idp, n | MDB_IDL_UM_MAX))
191 return ENOMEM;
192 ids = *idp;
193 }
194 ids[0] = len + n;
195 ids += len;
196 while (n)
197 ids[n--] = id++;
198 return 0;
199 }
200
mdb_midl_xmerge(MDB_IDL idl,MDB_IDL merge)201 void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge )
202 {
203 MDB_ID old_id, merge_id, i = merge[0], j = idl[0], k = i+j, total = k;
204 idl[0] = (MDB_ID)-1; /* delimiter for idl scan below */
205 old_id = idl[j];
206 while (i) {
207 merge_id = merge[i--];
208 for (; old_id < merge_id; old_id = idl[--j])
209 idl[k--] = old_id;
210 idl[k--] = merge_id;
211 }
212 idl[0] = total;
213 }
214
215 /* Quicksort + Insertion sort for small arrays */
216
217 #define SMALL 8
218 #define MIDL_SWAP(a,b) { itmp=(a); (a)=(b); (b)=itmp; }
219
220 void
mdb_midl_sort(MDB_IDL ids)221 mdb_midl_sort( MDB_IDL ids )
222 {
223 /* Max possible depth of int-indexed tree * 2 items/level */
224 int istack[sizeof(int)*CHAR_BIT * 2];
225 int i,j,k,l,ir,jstack;
226 MDB_ID a, itmp;
227
228 ir = (int)ids[0];
229 l = 1;
230 jstack = 0;
231 for(;;) {
232 if (ir - l < SMALL) { /* Insertion sort */
233 for (j=l+1;j<=ir;j++) {
234 a = ids[j];
235 for (i=j-1;i>=1;i--) {
236 if (ids[i] >= a) break;
237 ids[i+1] = ids[i];
238 }
239 ids[i+1] = a;
240 }
241 if (jstack == 0) break;
242 ir = istack[jstack--];
243 l = istack[jstack--];
244 } else {
245 k = (l + ir) >> 1; /* Choose median of left, center, right */
246 MIDL_SWAP(ids[k], ids[l+1]);
247 if (ids[l] < ids[ir]) {
248 MIDL_SWAP(ids[l], ids[ir]);
249 }
250 if (ids[l+1] < ids[ir]) {
251 MIDL_SWAP(ids[l+1], ids[ir]);
252 }
253 if (ids[l] < ids[l+1]) {
254 MIDL_SWAP(ids[l], ids[l+1]);
255 }
256 i = l+1;
257 j = ir;
258 a = ids[l+1];
259 for(;;) {
260 do i++; while(ids[i] > a);
261 do j--; while(ids[j] < a);
262 if (j < i) break;
263 MIDL_SWAP(ids[i],ids[j]);
264 }
265 ids[l+1] = ids[j];
266 ids[j] = a;
267 jstack += 2;
268 if (ir-i+1 >= j-l) {
269 istack[jstack] = ir;
270 istack[jstack-1] = i;
271 ir = j-1;
272 } else {
273 istack[jstack] = j-1;
274 istack[jstack-1] = l;
275 l = i;
276 }
277 }
278 }
279 }
280
mdb_mid2l_search(MDB_ID2L ids,MDB_ID id)281 unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id )
282 {
283 /*
284 * binary search of id in ids
285 * if found, returns position of id
286 * if not found, returns first position greater than id
287 */
288 unsigned base = 0;
289 unsigned cursor = 1;
290 int val = 0;
291 unsigned n = (unsigned)ids[0].mid;
292
293 while( 0 < n ) {
294 unsigned pivot = n >> 1;
295 cursor = base + pivot + 1;
296 val = CMP( id, ids[cursor].mid );
297
298 if( val < 0 ) {
299 n = pivot;
300
301 } else if ( val > 0 ) {
302 base = cursor;
303 n -= pivot + 1;
304
305 } else {
306 return cursor;
307 }
308 }
309
310 if( val > 0 ) {
311 ++cursor;
312 }
313 return cursor;
314 }
315
mdb_mid2l_insert(MDB_ID2L ids,MDB_ID2 * id)316 int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id )
317 {
318 unsigned x, i;
319
320 x = mdb_mid2l_search( ids, id->mid );
321
322 if( x < 1 ) {
323 /* internal error */
324 return -2;
325 }
326
327 if ( x <= ids[0].mid && ids[x].mid == id->mid ) {
328 /* duplicate */
329 return -1;
330 }
331
332 if ( ids[0].mid >= MDB_IDL_UM_MAX ) {
333 /* too big */
334 return -2;
335
336 } else {
337 /* insert id */
338 ids[0].mid++;
339 for (i=(unsigned)ids[0].mid; i>x; i--)
340 ids[i] = ids[i-1];
341 ids[x] = *id;
342 }
343
344 return 0;
345 }
346
mdb_mid2l_append(MDB_ID2L ids,MDB_ID2 * id)347 int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id )
348 {
349 /* Too big? */
350 if (ids[0].mid >= MDB_IDL_UM_MAX) {
351 return -2;
352 }
353 ids[0].mid++;
354 ids[ids[0].mid] = *id;
355 return 0;
356 }
357
358 /** @} */
359 /** @} */
360