This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
5bbd873c033fbda54ab86e7a0e6f28d1e072e63d
[perl5.git] / ext / SDBM_File / pair.c
1 /*
2  * sdbm - ndbm work-alike hashed database library
3  * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978).
4  * author: oz@nexus.yorku.ca
5  * status: public domain.
6  *
7  * page-level routines
8  */
9
10 #include "config.h"
11 #ifdef __CYGWIN__
12 # define EXTCONST extern const
13 #else
14 # include "EXTERN.h"
15 #endif
16 #include "sdbm.h"
17 #include "tune.h"
18 #include "pair.h"
19
20 #define exhash(item)    sdbm_hash((item).dptr, (item).dsize)
21
22 /* 
23  * forward 
24  */
25 static int seepair proto((char *, int, const char *, int));
26
27 /*
28  * page format:
29  *      +------------------------------+
30  * ino  | n | keyoff | datoff | keyoff |
31  *      +------------+--------+--------+
32  *      | datoff | - - - ---->         |
33  *      +--------+---------------------+
34  *      |        F R E E A R E A       |
35  *      +--------------+---------------+
36  *      |  <---- - - - | data          |
37  *      +--------+-----+----+----------+
38  *      |  key   | data     | key      |
39  *      +--------+----------+----------+
40  *
41  * calculating the offsets for free area:  if the number
42  * of entries (ino[0]) is zero, the offset to the END of
43  * the free area is the block size. Otherwise, it is the
44  * nth (ino[ino[0]]) entry's offset.
45  */
46
47 int
48 fitpair(char *pag, int need)
49 {
50         int n;
51         int off;
52         int free;
53         short *ino = (short *) pag;
54
55         off = ((n = ino[0]) > 0) ? ino[n] : PBLKSIZ;
56         free = off - (n + 1) * sizeof(short);
57         need += 2 * sizeof(short);
58
59         debug(("free %d need %d\n", free, need));
60
61         return need <= free;
62 }
63
64 void
65 putpair(char *pag, datum key, datum val)
66 {
67         int n;
68         int off;
69         short *ino = (short *) pag;
70
71         off = ((n = ino[0]) > 0) ? ino[n] : PBLKSIZ;
72 /*
73  * enter the key first
74  */
75         off -= key.dsize;
76         (void) memcpy(pag + off, key.dptr, key.dsize);
77         ino[n + 1] = off;
78 /*
79  * now the data
80  */
81         off -= val.dsize;
82         (void) memcpy(pag + off, val.dptr, val.dsize);
83         ino[n + 2] = off;
84 /*
85  * adjust item count
86  */
87         ino[0] += 2;
88 }
89
90 datum
91 getpair(char *pag, datum key)
92 {
93         int i;
94         int n;
95         datum val;
96         short *ino = (short *) pag;
97
98         if ((n = ino[0]) == 0)
99                 return nullitem;
100
101         if ((i = seepair(pag, n, key.dptr, key.dsize)) == 0)
102                 return nullitem;
103
104         val.dptr = pag + ino[i + 1];
105         val.dsize = ino[i] - ino[i + 1];
106         return val;
107 }
108
109 int
110 exipair(char *pag, datum key)
111 {
112         short *ino = (short *) pag;
113
114         if (ino[0] == 0)
115                 return 0;
116
117         return (seepair(pag, ino[0], key.dptr, key.dsize) != 0);
118 }
119
120 #ifdef SEEDUPS
121 int
122 duppair(char *pag, datum key)
123 {
124         short *ino = (short *) pag;
125         return ino[0] > 0 && seepair(pag, ino[0], key.dptr, key.dsize) > 0;
126 }
127 #endif
128
129 datum
130 getnkey(char *pag, int num)
131 {
132         datum key;
133         int off;
134         short *ino = (short *) pag;
135
136         num = num * 2 - 1;
137         if (ino[0] == 0 || num > ino[0])
138                 return nullitem;
139
140         off = (num > 1) ? ino[num - 1] : PBLKSIZ;
141
142         key.dptr = pag + ino[num];
143         key.dsize = off - ino[num];
144
145         return key;
146 }
147
148 int
149 delpair(char *pag, datum key)
150 {
151         int n;
152         int i;
153         short *ino = (short *) pag;
154
155         if ((n = ino[0]) == 0)
156                 return 0;
157
158         if ((i = seepair(pag, n, key.dptr, key.dsize)) == 0)
159                 return 0;
160 /*
161  * found the key. if it is the last entry
162  * [i.e. i == n - 1] we just adjust the entry count.
163  * hard case: move all data down onto the deleted pair,
164  * shift offsets onto deleted offsets, and adjust them.
165  * [note: 0 < i < n]
166  */
167         if (i < n - 1) {
168                 int m;
169                 char *dst = pag + (i == 1 ? PBLKSIZ : ino[i - 1]);
170                 char *src = pag + ino[i + 1];
171                 int   zoo = dst - src;
172
173                 debug(("free-up %d ", zoo));
174 /*
175  * shift data/keys down
176  */
177                 m = ino[i + 1] - ino[n];
178 #ifdef DUFF
179 #define MOVB    *--dst = *--src
180
181                 if (m > 0) {
182                         int loop = (m + 8 - 1) >> 3;
183
184                         switch (m & (8 - 1)) {
185                         case 0: do {
186                                 MOVB; /* FALLTHROUGH */ case 7: MOVB; /* FALLTHROUGH */
187                         case 6: MOVB; /* FALLTHROUGH */ case 5: MOVB; /* FALLTHROUGH */
188                         case 4: MOVB; /* FALLTHROUGH */ case 3: MOVB; /* FALLTHROUGH */
189                         case 2: MOVB; /* FALLTHROUGH */ case 1: MOVB;
190                                 } while (--loop);
191                         }
192                 }
193 #else
194                 dst -= m;
195                 src -= m;
196                 memmove(dst, src, m);
197 #endif
198 /*
199  * adjust offset index up
200  */
201                 while (i < n - 1) {
202                         ino[i] = ino[i + 2] + zoo;
203                         i++;
204                 }
205         }
206         ino[0] -= 2;
207         return 1;
208 }
209
210 /*
211  * search for the key in the page.
212  * return offset index in the range 0 < i < n.
213  * return 0 if not found.
214  */
215 static int
216 seepair(char *pag, int n, const char *key, int siz)
217 {
218         int i;
219         int off = PBLKSIZ;
220         short *ino = (short *) pag;
221
222         for (i = 1; i < n; i += 2) {
223                 if (siz == off - ino[i] &&
224                     memEQ(key, pag + ino[i], siz))
225                         return i;
226                 off = ino[i + 1];
227         }
228         return 0;
229 }
230
231 void
232 splpage(char *pag, char *New, long int sbit)
233 {
234         datum key;
235         datum val;
236
237         int n;
238         int off = PBLKSIZ;
239         char cur[PBLKSIZ];
240         short *ino = (short *) cur;
241
242         (void) memcpy(cur, pag, PBLKSIZ);
243         (void) memset(pag, 0, PBLKSIZ);
244         (void) memset(New, 0, PBLKSIZ);
245
246         n = ino[0];
247         for (ino++; n > 0; ino += 2) {
248                 key.dptr = cur + ino[0]; 
249                 key.dsize = off - ino[0];
250                 val.dptr = cur + ino[1];
251                 val.dsize = ino[0] - ino[1];
252 /*
253  * select the page pointer (by looking at sbit) and insert
254  */
255                 (void) putpair((exhash(key) & sbit) ? New : pag, key, val);
256
257                 off = ino[1];
258                 n -= 2;
259         }
260
261         debug(("%d split %d/%d\n", ((short *) cur)[0] / 2, 
262                ((short *) New)[0] / 2,
263                ((short *) pag)[0] / 2));
264 }
265
266 /*
267  * check page sanity: 
268  * number of entries should be something
269  * reasonable, and all offsets in the index should be in order.
270  * this could be made more rigorous.
271  */
272 int
273 chkpage(char *pag)
274 {
275         int n;
276         int off;
277         short *ino = (short *) pag;
278
279         if ((n = ino[0]) < 0 || n > (int)(PBLKSIZ / sizeof(short)))
280                 return 0;
281
282         if (n > 0) {
283                 off = PBLKSIZ;
284                 for (ino++; n > 0; ino += 2) {
285                         if (ino[0] > off || ino[1] > off ||
286                             ino[1] > ino[0])
287                                 return 0;
288                         off = ino[1];
289                         n -= 2;
290                 }
291         }
292         return 1;
293 }