source: main/tags/2.26/gsdl/packages/wingdbm/falloc.c@ 21066

Last change on this file since 21066 was 18, checked in by sjboddie, 26 years ago

Added windows gdbm and mg versions

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 12.1 KB
Line 
1/* falloc.c - The file space management routines for dbm. */
2
3/* This file is part of GDBM, the GNU data base manager, by Philip A. Nelson.
4 Copyright (C) 1990, 1991, 1993, 1994 Free Software Foundation, Inc.
5
6 GDBM is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GDBM is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GDBM; see the file COPYING. If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 You may contact the author by:
21 e-mail: [email protected]
22 us-mail: Philip A. Nelson
23 Computer Science Department
24 Western Washington University
25 Bellingham, WA 98226
26
27*************************************************************************/
28
29
30/* AIX demands this be the very first thing in the file. */
31#if !defined(__GNUC__) && defined(_AIX)
32 #pragma alloca
33#endif
34
35/* include system configuration before all else. */
36#include "autoconf.h"
37
38#include "gdbmdefs.h"
39
40
41/* The forward definitions for this file. See the functions for
42 the definition of the function. */
43
44static avail_elem get_elem _ARGS((int, avail_elem [], int *));
45static avail_elem get_block _ARGS((int, gdbm_file_info *));
46static void push_avail_block _ARGS((gdbm_file_info *));
47static void pop_avail_block _ARGS((gdbm_file_info *));
48static void adjust_bucket_avail _ARGS((gdbm_file_info *));
49
50/* Allocate space in the file DBF for a block NUM_BYTES in length. Return
51 the file address of the start of the block.
52
53 Each hash bucket has a fixed size avail table. We first check this
54 avail table to satisfy the request for space. In most cases we can
55 and this causes changes to be only in the current hash bucket.
56 Allocation is done on a first fit basis from the entries. If a
57 request can not be satisfied from the current hash bucket, then it is
58 satisfied from the file header avail block. If nothing is there that
59 has enough space, another block at the end of the file is allocated
60 and the unused portion is returned to the avail block. This routine
61 "guarantees" that an allocation does not cross a block boundary unless
62 the size is larger than a single block. The avail structure is
63 changed by this routine if a change is needed. If an error occurs,
64 the value of 0 will be returned. */
65
66off_t
67_gdbm_alloc (dbf, num_bytes)
68 gdbm_file_info *dbf;
69 int num_bytes;
70{
71 off_t file_adr; /* The address of the block. */
72 avail_elem av_el; /* For temporary use. */
73
74 /* The current bucket is the first place to look for space. */
75 av_el = get_elem (num_bytes, dbf->bucket->bucket_avail,
76 &dbf->bucket->av_count);
77
78 /* If we did not find some space, we have more work to do. */
79 if (av_el.av_size == 0)
80 {
81 /* Is the header avail block empty and there is something on the stack. */
82 if ((dbf->header->avail.count == 0)
83 && (dbf->header->avail.next_block != 0))
84 pop_avail_block (dbf);
85
86 /* check the header avail table next */
87 av_el = get_elem (num_bytes, dbf->header->avail.av_table,
88 &dbf->header->avail.count);
89 if (av_el.av_size == 0)
90 /* Get another full block from end of file. */
91 av_el = get_block (num_bytes, dbf);
92
93 dbf->header_changed = TRUE;
94 }
95
96 /* We now have the place from which we will allocate the new space. */
97 file_adr = av_el.av_adr;
98
99 /* Put the unused space back in the avail block. */
100 av_el.av_adr += num_bytes;
101 av_el.av_size -= num_bytes;
102 _gdbm_free (dbf, av_el.av_adr, av_el.av_size);
103
104 /* Return the address. */
105 return file_adr;
106
107}
108
109
110
111/* Free space of size NUM_BYTES in the file DBF at file address FILE_ADR. Make
112 it avaliable for reuse through _gdbm_alloc. This routine changes the
113 avail structure. The value TRUE is returned if there were errors. If no
114 errors occured, the value FALSE is returned. */
115
116void
117_gdbm_free (dbf, file_adr, num_bytes)
118 gdbm_file_info *dbf;
119 off_t file_adr;
120 int num_bytes;
121{
122 avail_elem temp;
123
124 /* Is it too small to worry about? */
125 if (num_bytes <= IGNORE_SIZE)
126 return;
127
128 /* Initialize the avail element. */
129 temp.av_size = num_bytes;
130 temp.av_adr = file_adr;
131
132 /* Is the freed space large or small? */
133 if (num_bytes >= dbf->header->block_size)
134 {
135 if (dbf->header->avail.count == dbf->header->avail.size)
136 {
137 push_avail_block (dbf);
138 }
139 _gdbm_put_av_elem (temp, dbf->header->avail.av_table,
140 &dbf->header->avail.count);
141 dbf->header_changed = TRUE;
142 }
143 else
144 {
145 /* Try to put into the current bucket. */
146 if (dbf->bucket->av_count < BUCKET_AVAIL)
147 _gdbm_put_av_elem (temp, dbf->bucket->bucket_avail,
148 &dbf->bucket->av_count);
149 else
150 {
151 if (dbf->header->avail.count == dbf->header->avail.size)
152 {
153 push_avail_block (dbf);
154 }
155 _gdbm_put_av_elem (temp, dbf->header->avail.av_table,
156 &dbf->header->avail.count);
157 dbf->header_changed = TRUE;
158 }
159 }
160
161 if (dbf->header_changed)
162 adjust_bucket_avail (dbf);
163
164 /* All work is done. */
165 return;
166}
167
168
169
170/* The following are all utility routines needed by the previous two. */
171
172
173/* Gets the avail block at the top of the stack and loads it into the
174 active avail block. It does a "free" for itself! */
175
176static void
177pop_avail_block (dbf)
178 gdbm_file_info *dbf;
179{
180 int num_bytes; /* For use with the read system call. */
181 off_t file_pos; /* For use with the lseek system call. */
182 avail_elem temp;
183
184 /* Set up variables. */
185 temp.av_adr = dbf->header->avail.next_block;
186 temp.av_size = ( ( (dbf->header->avail.size * sizeof (avail_elem)) >> 1)
187 + sizeof (avail_block));
188
189 /* Read the block. */
190 file_pos = lseek (dbf->desc, temp.av_adr, L_SET);
191 if (file_pos != temp.av_adr) _gdbm_fatal (dbf, "lseek error");
192 num_bytes = read (dbf->desc, &dbf->header->avail, temp.av_size);
193 if (num_bytes != temp.av_size) _gdbm_fatal (dbf, "read error");
194
195 /* We changed the header. */
196 dbf->header_changed = TRUE;
197
198 /* Free the previous avail block. */
199 _gdbm_put_av_elem (temp, dbf->header->avail.av_table,
200 &dbf->header->avail.count);
201}
202
203
204/* Splits the header avail block and pushes half onto the avail stack. */
205
206static void
207push_avail_block (dbf)
208 gdbm_file_info *dbf;
209{
210 int num_bytes;
211 int av_size;
212 off_t av_adr;
213 int index;
214 off_t file_pos;
215 avail_block *temp;
216 avail_elem new_loc;
217
218
219 /* Caclulate the size of the split block. */
220 av_size = ( (dbf->header->avail.size * sizeof (avail_elem)) >> 1)
221 + sizeof (avail_block);
222
223 /* Get address in file for new av_size bytes. */
224 new_loc = get_elem (av_size, dbf->header->avail.av_table,
225 &dbf->header->avail.count);
226 if (new_loc.av_size == 0)
227 new_loc = get_block (av_size, dbf);
228 av_adr = new_loc.av_adr;
229
230
231 /* Split the header block. */
232 temp = (avail_block *) alloca (av_size);
233#ifdef MSDOS
234 if (temp == (avail_block *) 0)
235 {
236 fprintf (stderr, "Error: alloca() failed in gdbm (%s).\n", __FILE__);
237 exit (-2);
238 }
239#endif /* MSDOS */ /* Set the size to be correct AFTER the pop_avail_block. */
240 temp->size = dbf->header->avail.size;
241 temp->count = 0;
242 temp->next_block = dbf->header->avail.next_block;
243 dbf->header->avail.next_block = av_adr;
244 for (index = 1; index < dbf->header->avail.count; index++)
245 if ( (index & 0x1) == 1) /* Index is odd. */
246 temp->av_table[temp->count++] = dbf->header->avail.av_table[index];
247 else
248 dbf->header->avail.av_table[index>>1]
249 = dbf->header->avail.av_table[index];
250
251 /* Update the header avail count to previous size divided by 2. */
252 dbf->header->avail.count >>= 1;
253
254 /* Free the unneeded space. */
255 new_loc.av_adr += av_size;
256 new_loc.av_size -= av_size;
257 _gdbm_free (dbf, new_loc.av_adr, new_loc.av_size);
258
259 /* Update the disk. */
260 file_pos = lseek (dbf->desc, av_adr, L_SET);
261 if (file_pos != av_adr) _gdbm_fatal (dbf, "lseek error");
262 num_bytes = write (dbf->desc, temp, av_size);
263 if (num_bytes != av_size) _gdbm_fatal (dbf, "write error");
264
265}
266
267
268
269/* Get_elem returns an element in the AV_TABLE block which is
270 larger than SIZE. AV_COUNT is the number of elements in the
271 AV_TABLE. If an item is found, it extracts it from the AV_TABLE
272 and moves the other elements up to fill the space. If no block is
273 found larger than SIZE, get_elem returns a size of zero. This
274 routine does no I/O. */
275
276static avail_elem
277get_elem (size, av_table, av_count)
278 int size;
279 avail_elem av_table[];
280 int *av_count;
281{
282 int index; /* For searching through the avail block. */
283 avail_elem val; /* The default return value. */
284
285 /* Initialize default return value. */
286 val.av_adr = 0;
287 val.av_size = 0;
288
289 /* Search for element. List is sorted by size. */
290 index = 0;
291 while (index < *av_count && av_table[index].av_size < size)
292 {
293 index++;
294 }
295
296 /* Did we find one of the right size? */
297 if (index >= *av_count)
298 return val;
299
300 /* Ok, save that element and move all others up one. */
301 val = av_table[index];
302 *av_count -= 1;
303 while (index < *av_count)
304 {
305 av_table[index] = av_table[index+1];
306 index++;
307 }
308
309 return val;
310}
311
312
313/* This routine inserts a single NEW_EL into the AV_TABLE block in
314 sorted order. This routine does no I/O. */
315
316int
317_gdbm_put_av_elem (new_el, av_table, av_count)
318 avail_elem new_el;
319 avail_elem av_table[];
320 int *av_count;
321{
322 int index; /* For searching through the avail block. */
323 int index1;
324
325 /* Is it too small to deal with? */
326 if (new_el.av_size <= IGNORE_SIZE)
327 return FALSE;
328
329 /* Search for place to put element. List is sorted by size. */
330 index = 0;
331 while (index < *av_count && av_table[index].av_size < new_el.av_size)
332 {
333 index++;
334 }
335
336 /* Move all others up one. */
337 index1 = *av_count-1;
338 while (index1 >= index)
339 {
340 av_table[index1+1] = av_table[index1];
341 index1--;
342 }
343
344 /* Add the new element. */
345 av_table[index] = new_el;
346
347 /* Increment the number of elements. */
348 *av_count += 1;
349
350 return TRUE;
351}
352
353
354
355
356
357/* Get_block "allocates" new file space and the end of the file. This is
358 done in integral block sizes. (This helps insure that data smaller than
359 one block size is in a single block.) Enough blocks are allocated to
360 make sure the number of bytes allocated in the blocks is larger than SIZE.
361 DBF contains the file header that needs updating. This routine does
362 no I/O. */
363
364static avail_elem
365get_block (size, dbf)
366 int size;
367 gdbm_file_info *dbf;
368{
369 avail_elem val;
370
371 /* Need at least one block. */
372 val.av_adr = dbf->header->next_block;
373 val.av_size = dbf->header->block_size;
374
375 /* Get enough blocks to fit the need. */
376 while (val.av_size < size)
377 val.av_size += dbf->header->block_size;
378
379 /* Update the header and return. */
380 dbf->header->next_block += val.av_size;
381
382 /* We changed the header. */
383 dbf->header_changed = TRUE;
384
385 return val;
386
387}
388
389
390/* When the header already needs writing, we can make sure the current
391 bucket has its avail block as close to 1/2 full as possible. */
392static void
393adjust_bucket_avail (dbf)
394 gdbm_file_info *dbf;
395{
396 int third = BUCKET_AVAIL / 3;
397 avail_elem av_el;
398
399 /* Can we add more entries to the bucket? */
400 if (dbf->bucket->av_count < third)
401 {
402 if (dbf->header->avail.count > 0)
403 {
404 dbf->header->avail.count -= 1;
405 av_el = dbf->header->avail.av_table[dbf->header->avail.count];
406 _gdbm_put_av_elem (av_el, dbf->bucket->bucket_avail,
407 &dbf->bucket->av_count);
408 dbf->bucket_changed = TRUE;
409 }
410 return;
411 }
412
413 /* Is there too much in the bucket? */
414 while (dbf->bucket->av_count > BUCKET_AVAIL-third
415 && dbf->header->avail.count < dbf->header->avail.size)
416 {
417 av_el = get_elem (0, dbf->bucket->bucket_avail, &dbf->bucket->av_count);
418 _gdbm_put_av_elem (av_el, dbf->header->avail.av_table,
419 &dbf->header->avail.count);
420 dbf->bucket_changed = TRUE;
421 }
422}
Note: See TracBrowser for help on using the repository browser.