Coverage Report

Created: 2026-08-31 06:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdbm/src/bucket.c
Line
Count
Source
1
/* bucket.c - The routines for playing with hash buckets. */
2
3
/* This file is part of GDBM, the GNU data base manager.
4
   Copyright (C) 1990-2025 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 3, 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. If not, see <http://www.gnu.org/licenses/>.   */
18
19
#include "autoconf.h"
20
#include "gdbmdefs.h"
21
#include <stdint.h>
22
#include <limits.h>
23
24
0
#define GDBM_MAX_DIR_SIZE INT32_MAX
25
0
#define GDBM_MAX_DIR_HALF (GDBM_MAX_DIR_SIZE / 2)
26
27
/* Initializing a new hash buckets sets all bucket entries to -1 hash value. */
28
void
29
_gdbm_new_bucket (GDBM_FILE dbf, hash_bucket *bucket, int bits)
30
12.0k
{
31
12.0k
  int index;
32
33
  /* Initialize the avail block. */
34
12.0k
  bucket->av_count = 0;
35
36
  /* Set the information fields first. */
37
12.0k
  bucket->bucket_bits = bits;
38
12.0k
  bucket->count = 0;
39
  
40
  /* Initialize all bucket elements. */
41
1.21M
  for (index = 0; index < dbf->header->bucket_elems; index++)
42
1.20M
    bucket->h_table[index].hash_value = -1;
43
12.0k
}
44

45
/* Bucket cache table functions */
46
47
/* Hash an off_t word into an index of width NBITS. */
48
static size_t
49
adrhash (off_t adr, size_t nbits)
50
3.88M
{
51
3.88M
  adr ^= adr >> (GDBM_HASH_BITS + 1 - nbits); 
52
3.88M
  return ((265443576910ul * adr) & 0xffffffff) >> (GDBM_HASH_BITS + 1 - nbits);
53
3.88M
}
54
55
/*
56
 * Return a pointer to the cache table slot for bucket address ADR.
57
 * Never returns NULL.
58
 */
59
static cache_elem **
60
cache_tab_lookup_slot (GDBM_FILE dbf, off_t adr)
61
3.70M
{
62
3.70M
  cache_elem **cache = dbf->cache;
63
3.70M
  size_t h = adrhash (adr, dbf->cache_bits);
64
65
3.70M
  if (cache[h])
66
3.54M
    {
67
3.54M
      if (cache[h]->ca_adr != adr)
68
13.7k
  {
69
13.7k
    cache_elem *prev = cache[h], *p = prev->ca_coll;
70
15.0k
    while (p)
71
6.91k
      {
72
6.91k
        if (p->ca_adr == adr)
73
5.61k
    break;
74
1.29k
        prev = p;
75
1.29k
        p = prev->ca_coll;
76
1.29k
      }
77
13.7k
    return &prev->ca_coll;
78
13.7k
  }
79
3.54M
    }
80
3.69M
  return &cache[h];
81
3.70M
}
82

83
/* LRU list management */
84
85
/*
86
 * Link ELEM after REF in DBF cache.  If REF is NULL, link at head and
87
 * set DBF->bucket to point to the ca_bucket of ELEM.
88
 */
89
static void
90
lru_link_elem (GDBM_FILE dbf, cache_elem *elem, cache_elem *ref)
91
3.71M
{
92
3.71M
  if (!ref)
93
3.70M
    {
94
3.70M
      elem->ca_prev = NULL;
95
3.70M
      elem->ca_next = dbf->cache_mru;
96
3.70M
      if (dbf->cache_mru)
97
3.11M
  dbf->cache_mru->ca_prev = elem;
98
594k
      else
99
594k
  dbf->cache_lru = elem;
100
3.70M
      dbf->cache_mru = elem;
101
3.70M
      dbf->bucket = dbf->cache_mru->ca_bucket;
102
3.70M
    }
103
6.67k
  else
104
6.67k
    {
105
6.67k
      cache_elem *x;
106
107
6.67k
      elem->ca_prev = ref;
108
6.67k
      elem->ca_next = ref->ca_next;
109
6.67k
      if ((x = ref->ca_next))
110
5.11k
  x->ca_prev = elem;
111
1.56k
      else
112
1.56k
  dbf->cache_lru = elem;
113
6.67k
      ref->ca_next = elem;
114
6.67k
    }
115
3.71M
}
116
117
/*
118
 * Unlink ELEM from the list of cache elements in DBF.
119
 * If cache_mru gets updated, update DBF->bucket accordingly.
120
 */
121
static void
122
lru_unlink_elem (GDBM_FILE dbf, cache_elem *elem)
123
3.71M
{
124
3.71M
  cache_elem *x;
125
126
3.71M
  if ((x = elem->ca_prev))
127
438k
    x->ca_next = elem->ca_next;
128
3.27M
  else
129
3.27M
    {
130
3.27M
      dbf->cache_mru = elem->ca_next;
131
3.27M
      dbf->bucket = dbf->cache_mru ? dbf->cache_mru->ca_bucket : NULL;
132
3.27M
    }
133
3.71M
  if ((x = elem->ca_next))
134
3.01M
    x->ca_prev = elem->ca_prev;
135
700k
  else
136
700k
    dbf->cache_lru = elem->ca_prev;
137
3.71M
  elem->ca_prev = elem->ca_next = NULL;
138
3.71M
}
139

140
/* Creates and returns new cache element for DBF.  The element is initialized,
141
   but not linked to the LRU list.
142
   Return NULL on error.
143
*/
144
static cache_elem *
145
cache_elem_new (GDBM_FILE dbf, off_t adr)
146
177k
{
147
177k
  cache_elem *elem;
148
149
177k
  elem = dbf->cache_avail;
150
177k
  if (elem)
151
156k
    {
152
156k
      dbf->cache_avail = elem->ca_next;
153
156k
    }
154
20.5k
  else
155
20.5k
    {
156
20.5k
      elem = calloc (1,
157
20.5k
         sizeof (*elem) -
158
20.5k
         sizeof (elem->ca_bucket[0]) +
159
20.5k
         dbf->header->bucket_size);
160
161
20.5k
      if (!elem)
162
0
  return NULL;
163
20.5k
    }
164
165
177k
  elem->ca_adr = adr;
166
177k
  elem->ca_changed = FALSE;
167
177k
  elem->ca_data.hash_val = -1;
168
177k
  elem->ca_data.elem_loc = -1;
169
170
177k
  elem->ca_prev = elem->ca_next = elem->ca_coll = NULL;
171
177k
  elem->ca_hits = 0;
172
  
173
177k
  return elem;
174
177k
}
175
176
/* Frees element ELEM.  Unlinks it from the cache tree and LRU list. */
177
static void
178
cache_elem_free (GDBM_FILE dbf, cache_elem *elem)
179
177k
{
180
177k
  size_t h = adrhash (elem->ca_adr, dbf->cache_bits);
181
177k
  cache_elem **pp;
182
  
183
177k
  lru_unlink_elem (dbf, elem);
184
185
177k
  elem->ca_next = dbf->cache_avail;
186
177k
  dbf->cache_avail = elem;
187
177k
  dbf->cache_num--;
188
189
177k
  pp = &dbf->cache[h];
190
186k
  while (*pp)
191
186k
    {
192
186k
      if (*pp == elem)
193
177k
  {
194
177k
    *pp = (*pp)->ca_coll;
195
177k
    break;
196
177k
  }
197
9.15k
      pp = &(*pp)->ca_coll;
198
9.15k
    }      
199
177k
}
200
201
/* Free the least recently used cache entry. */
202
static inline int
203
cache_lru_free (GDBM_FILE dbf)
204
0
{
205
0
  cache_elem *last = dbf->cache_lru;
206
0
  if (last->ca_changed)
207
0
    {
208
0
      if (_gdbm_write_bucket (dbf, last))
209
0
  return -1;
210
0
    }
211
0
  cache_elem_free (dbf, last);
212
0
  return 0;
213
0
}
214

215
/*
216
 * Round up V to the next highest power of 2 and compute log2 of
217
 * it using De Brujin sequences.
218
 * See http://supertech.csail.mit.edu/papers/debruijn.pdf
219
 */
220
static unsigned
221
log2i (unsigned v)
222
0
{
223
0
  static const int dbp[32] =
224
0
    {
225
0
      0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 
226
0
      31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
227
0
    };
228
229
0
  v--;
230
0
  v |= v >> 1;
231
0
  v |= v >> 2;
232
0
  v |= v >> 4;
233
0
  v |= v >> 8;
234
0
  v |= v >> 16;
235
0
  v++;
236
0
  return dbp[(uint32_t)(v * 0x077CB531U) >> 27];
237
0
}
238
239
static int
240
cache_tab_resize (GDBM_FILE dbf, int bits)
241
8.13k
{
242
8.13k
  size_t size = 1 << bits;
243
244
8.13k
  if (!dbf->cache || size != dbf->cache_size)
245
8.13k
    {
246
8.13k
      size_t n = size * sizeof (dbf->cache[0]);
247
8.13k
      cache_elem **p, *elem;
248
      
249
      /* Flush existing cache */
250
8.13k
      if (_gdbm_cache_flush (dbf))
251
0
  return -1;
252
253
      /* Reallocate it */
254
8.13k
      p = realloc (dbf->cache, n);
255
8.13k
      if (!p)
256
0
  {
257
0
    GDBM_SET_ERRNO (dbf, GDBM_MALLOC_ERROR, FALSE);
258
0
    return -1;
259
0
  }
260
8.13k
      dbf->cache = p;
261
8.13k
      dbf->cache_size = size;
262
8.13k
      dbf->cache_bits = bits;
263
264
8.13k
      memset (dbf->cache, 0, n);
265
266
      /* Rehash and free surplus elements */
267
8.13k
      for (elem = dbf->cache_lru; elem; )
268
0
  {
269
0
    cache_elem *prev = elem->ca_prev;
270
0
    elem->ca_coll = NULL;
271
0
    if (size < dbf->cache_num)
272
0
      {
273
0
        cache_elem_free (dbf, elem);
274
0
      }
275
0
    else
276
0
      {
277
0
        p = cache_tab_lookup_slot (dbf, elem->ca_adr);
278
0
        if (*p)
279
0
    abort ();// shouldn't happen
280
0
        *p = elem;
281
0
      }
282
0
    elem = prev;
283
0
  }
284
8.13k
    }
285
8.13k
  return 0;
286
8.13k
}
287

288
enum
289
  {
290
    cache_found,
291
    cache_new,
292
    cache_failure
293
  };
294
295
static int
296
cache_lookup (GDBM_FILE dbf, off_t adr, cache_elem *ref, cache_elem **ret_elem)
297
3.70M
{
298
3.70M
  int rc;
299
3.70M
  cache_elem **elp, *elem;
300
  
301
3.70M
  dbf->cache_access_count++;
302
303
3.70M
  elp = cache_tab_lookup_slot (dbf, adr);
304
  
305
3.70M
  if (*elp != NULL)
306
3.53M
    {
307
3.53M
      elem = *elp;
308
3.53M
      elem->ca_hits++;
309
3.53M
      dbf->cache_hits++;
310
3.53M
      lru_unlink_elem (dbf, elem);
311
3.53M
      rc = cache_found;
312
3.53M
    }
313
177k
  else if ((elem = cache_elem_new (dbf, adr)) == NULL)
314
0
    return cache_failure;
315
177k
  else
316
177k
    {
317
177k
      rc = cache_new;
318
319
177k
      if (dbf->cache_num == dbf->cache_size)
320
0
  {
321
0
    if (dbf->cache_auto && dbf->cache_bits < dbf->header->dir_bits &&
322
0
        cache_tab_resize (dbf, dbf->cache_bits + 1) == 0)
323
0
      {
324
        /* Table has been reallocated, recompute the slot. */
325
0
        elp = cache_tab_lookup_slot (dbf, adr);
326
0
      }
327
0
    else if (cache_lru_free (dbf))
328
0
      {
329
0
        rc = cache_failure;
330
0
      }
331
0
  }
332
      
333
177k
      if (rc == cache_new)
334
177k
  {
335
177k
    *elp = elem;
336
177k
    dbf->cache_num++;
337
177k
  }
338
177k
    }
339
340
  /*
341
   * If the obtained bucket is not changed and is going to become current,
342
   * flush all changed cache elements.  This ensures that changed cache
343
   * elements form a contiguous sequence at the head of the cache list (see
344
   * _gdbm_cache_flush).
345
   */
346
3.70M
  if (ref == NULL && !elem->ca_changed)
347
3.70M
    _gdbm_cache_flush (dbf);
348
  
349
3.70M
  lru_link_elem (dbf, elem, ref);
350
3.70M
  if (rc != cache_failure)
351
3.70M
    *ret_elem = elem;
352
3.70M
  return rc;
353
3.70M
}
354

355
/*
356
 * Find a bucket for DBF that is pointed to by the bucket directory from
357
 * location DIR_INDEX.   The bucket cache is first checked to see if it
358
 * is already in memory.  If not, the last recently used bucket may be
359
 * tossed (if the cache is full) to read the new bucket.
360
 *
361
 * On success, the cached entry with the requested bucket is placed at
362
 * the head of the cache list (cache_mru) and the requested bucket becomes
363
 * "current".
364
 *
365
 * On error, the current bucket remains unchanged.
366
 */
367
int
368
_gdbm_get_bucket (GDBM_FILE dbf, int dir_index)
369
3.76M
{
370
3.76M
  int rc;
371
3.76M
  off_t bucket_adr; /* The address of the correct hash bucket.  */
372
3.76M
  off_t file_pos; /* The return address for lseek. */
373
3.76M
  hash_bucket *bucket;
374
3.76M
  cache_elem *elem;
375
  
376
3.76M
  if (!gdbm_dir_entry_valid_p (dbf, dir_index))
377
60.9k
    {
378
      /* FIXME: negative caching? */
379
60.9k
      GDBM_SET_ERRNO (dbf, GDBM_BAD_DIR_ENTRY, TRUE);
380
60.9k
      return -1;
381
60.9k
    }
382
  
383
  /* Initial set up. */
384
3.70M
  dbf->bucket_dir = dir_index;
385
3.70M
  bucket_adr = dbf->dir[dir_index];
386
387
3.70M
  switch (cache_lookup (dbf, bucket_adr, NULL, &elem))
388
3.70M
    {
389
3.53M
    case cache_found:
390
3.53M
      break;
391
      
392
170k
    case cache_new:
393
      /* Position the file pointer */
394
170k
      file_pos = gdbm_file_seek (dbf, bucket_adr, SEEK_SET);
395
170k
      if (file_pos != bucket_adr)
396
0
  {
397
0
    GDBM_SET_ERRNO (dbf, GDBM_FILE_SEEK_ERROR, TRUE);
398
0
    cache_elem_free (dbf, elem);
399
0
    _gdbm_fatal (dbf, _("lseek error"));
400
0
    return -1;
401
0
  }
402
403
      /* Read the bucket. */
404
170k
      rc = _gdbm_full_read (dbf, elem->ca_bucket, dbf->header->bucket_size);
405
170k
      if (rc)
406
143k
  {
407
143k
    GDBM_DEBUG (GDBM_DEBUG_ERR,
408
143k
          "%s: error reading bucket: %s",
409
143k
          dbf->name, gdbm_db_strerror (dbf));
410
143k
    dbf->need_recovery = TRUE;
411
143k
    cache_elem_free (dbf, elem);
412
143k
    _gdbm_fatal (dbf, gdbm_db_strerror (dbf));
413
143k
    return -1;
414
143k
  }
415
416
      /* Validate the bucket */
417
26.6k
      bucket = elem->ca_bucket;
418
26.6k
      if (!(bucket->count >= 0
419
25.7k
      && bucket->count <= dbf->header->bucket_elems
420
22.1k
      && bucket->bucket_bits >= 0
421
21.6k
      && bucket->bucket_bits <= dbf->header->dir_bits))
422
8.14k
  {
423
8.14k
    GDBM_SET_ERRNO (dbf, GDBM_BAD_BUCKET, TRUE);
424
8.14k
    cache_elem_free (dbf, elem);
425
8.14k
    return -1;
426
8.14k
  }
427
      /* Validate bucket_avail table */
428
18.5k
      if (gdbm_bucket_avail_table_validate (dbf, bucket))
429
4.49k
  {
430
4.49k
    cache_elem_free (dbf, elem);
431
4.49k
    return -1;
432
4.49k
  }
433
      
434
      /* Update the cache */
435
14.0k
      elem->ca_adr = bucket_adr;
436
14.0k
      elem->ca_data.elem_loc = -1;
437
14.0k
      elem->ca_changed = FALSE;
438
      
439
14.0k
      break;
440
      
441
0
    case cache_failure:
442
0
      return -1;
443
3.70M
    }
444
  
445
3.54M
  return 0;
446
3.70M
}
447
448
/* Split the current bucket.  This includes moving all items in the bucket to
449
   a new bucket.  This doesn't require any disk reads because all hash values
450
   are stored in the buckets.  Splitting the current bucket may require
451
   doubling the size of the hash directory.  */
452
int
453
_gdbm_split_bucket (GDBM_FILE dbf, int next_insert)
454
2.69k
{
455
2.69k
  off_t        old_adr[GDBM_HASH_BITS];  /* Address of the old directories. */
456
2.69k
  int          old_size[GDBM_HASH_BITS]; /* Size of the old directories. */
457
2.69k
  int        old_count; /* Number of old directories. */
458
459
2.69k
  int          index;   /* Used in array indexing. */
460
2.69k
  int          index1;    /* Used in array indexing. */
461
  
462
  /* No directories are yet old. */
463
2.69k
  old_count = 0;
464
6.03k
  while (dbf->bucket->count == dbf->header->bucket_elems)
465
3.33k
    {
466
3.33k
      int          new_bits;  /* The number of bits for the new buckets. */
467
3.33k
      cache_elem  *newcache[2]; /* Location in the cache for the buckets. */
468
3.33k
      off_t        adr_0; /* File address of the new bucket 0. */
469
3.33k
      off_t        adr_1; /* File address of the new bucket 1. */
470
3.33k
      avail_elem   old_bucket;  /* Avail Struct for the old bucket. */
471
      
472
3.33k
      off_t        dir_start0;  /* Used in updating the directory. */
473
3.33k
      off_t        dir_start1;
474
3.33k
      off_t        dir_end;
475
476
3.33k
      new_bits = dbf->bucket->bucket_bits + 1;
477
478
      /*
479
       * Allocate two new buckets.  They will be populated with the entries
480
       * from the current bucket (cache_mru->bucket), so make sure that
481
       * cache_mru remains unchanged until both buckets are fully formed.
482
       * Newly allocated buckets must be linked right after cache_mru, so
483
       * that all changed buckets form a contiguous sequence at the beginning
484
       * of the cache list (this is needed by _gdbm_cache_flush).
485
       */
486
3.33k
      adr_0 = _gdbm_alloc (dbf, dbf->header->bucket_size);
487
3.33k
      switch (cache_lookup (dbf, adr_0, dbf->cache_mru, &newcache[0]))
488
3.33k
  {
489
3.33k
  case cache_new:
490
3.33k
    break;
491
492
0
  case cache_found:
493
    /* should not happen */
494
0
    GDBM_DEBUG (GDBM_DEBUG_ERR,
495
0
          "%s: bucket found where it should not",
496
0
          dbf->name);
497
0
    GDBM_SET_ERRNO (dbf, GDBM_BUCKET_CACHE_CORRUPTED, TRUE);
498
0
    return -1;
499
500
0
  case cache_failure:
501
0
    return -1;
502
3.33k
  }
503
3.33k
      _gdbm_new_bucket (dbf, newcache[0]->ca_bucket, new_bits);
504
505
3.33k
      adr_1 = _gdbm_alloc (dbf, dbf->header->bucket_size);
506
3.33k
      switch (cache_lookup (dbf, adr_1, newcache[0], &newcache[1]))
507
3.33k
  {
508
3.33k
  case cache_new:
509
3.33k
    break;
510
511
0
  case cache_found:
512
    /* should not happen */
513
0
    GDBM_DEBUG (GDBM_DEBUG_ERR,
514
0
          "%s: bucket found where it should not",
515
0
          dbf->name);
516
0
    GDBM_SET_ERRNO (dbf, GDBM_BUCKET_CACHE_CORRUPTED, TRUE);
517
0
    return -1;
518
519
0
  case cache_failure:
520
0
    return -1;
521
3.33k
  }
522
3.33k
      _gdbm_new_bucket (dbf, newcache[1]->ca_bucket, new_bits);
523
524
      /* Double the directory size if necessary. */
525
3.33k
      if (dbf->header->dir_bits == dbf->bucket->bucket_bits)
526
0
  {
527
0
    off_t       *new_dir;   /* Pointer to the new directory. */
528
0
    int          dir_size;  /* Size of the new directory. */
529
0
    off_t        dir_adr;   /* Address of the new directory. */
530
    
531
0
    if (dbf->header->dir_size >= GDBM_MAX_DIR_HALF)
532
0
      {
533
0
        GDBM_SET_ERRNO (dbf, GDBM_DIR_OVERFLOW, TRUE);
534
0
        _gdbm_fatal (dbf, _("directory overflow"));
535
0
        return -1;
536
0
      }
537
0
    dir_size = dbf->header->dir_size * 2;
538
0
    dir_adr  = _gdbm_alloc (dbf, dir_size);
539
0
    if (dir_adr == 0)
540
0
      return -1;
541
0
    new_dir = malloc (dir_size);
542
0
    if (new_dir == NULL)
543
0
      {
544
0
        GDBM_SET_ERRNO (dbf, GDBM_MALLOC_ERROR, TRUE);
545
0
        _gdbm_fatal (dbf, _("malloc error"));
546
0
        return -1;
547
0
      }
548
549
0
    for (index = 0; index < GDBM_DIR_COUNT (dbf); index++)
550
0
      {
551
0
        new_dir[2*index]   = dbf->dir[index];
552
0
        new_dir[2*index+1] = dbf->dir[index];
553
0
      }
554
    
555
    /* Update header. */
556
0
    old_adr[old_count] = dbf->header->dir;
557
0
    dbf->header->dir = dir_adr;
558
0
    old_size[old_count] = dbf->header->dir_size;
559
0
    dbf->header->dir_size = dir_size;
560
0
    dbf->header->dir_bits = new_bits;
561
0
    old_count++;
562
    
563
    /* Now update dbf.  */
564
0
    dbf->header_changed = TRUE;
565
0
    dbf->bucket_dir *= 2;
566
0
    free (dbf->dir);
567
0
    dbf->dir = new_dir;
568
0
  }
569
570
      /* Copy all elements in dbf->bucket into the new buckets. */
571
248k
      for (index = 0; index < dbf->header->bucket_elems; index++)
572
244k
  {
573
244k
    bucket_element *old_el = &dbf->bucket->h_table[index];
574
244k
    hash_bucket *bucket;
575
244k
    int elem_loc;
576
    
577
244k
    if (old_el->hash_value < 0)
578
0
      {
579
0
        GDBM_SET_ERRNO (dbf, GDBM_BAD_BUCKET, TRUE);
580
0
        return -1;
581
0
      }
582
583
244k
    bucket =
584
244k
      newcache[(old_el->hash_value >> (GDBM_HASH_BITS - new_bits)) & 1]->ca_bucket;
585
244k
    elem_loc = old_el->hash_value % dbf->header->bucket_elems;
586
963k
    while (bucket->h_table[elem_loc].hash_value != -1)
587
718k
      elem_loc = (elem_loc + 1) % dbf->header->bucket_elems;
588
244k
    bucket->h_table[elem_loc] = *old_el;
589
244k
    bucket->count++;
590
244k
  }
591
      
592
      /* Allocate avail space for the newcache[1]->ca_bucket. */
593
3.33k
      newcache[1]->ca_bucket->bucket_avail[0].av_adr
594
3.33k
  = _gdbm_alloc (dbf, dbf->header->block_size);
595
3.33k
      if (newcache[1]->ca_bucket->bucket_avail[0].av_adr == 0)
596
0
  return -1;
597
3.33k
      newcache[1]->ca_bucket->bucket_avail[0].av_size
598
3.33k
  = dbf->header->block_size;
599
3.33k
      newcache[1]->ca_bucket->av_count = 1;
600
      
601
      /* Copy the avail elements in dbf->bucket to newcache[0]->ca_bucket. */
602
3.33k
      newcache[0]->ca_bucket->av_count = dbf->bucket->av_count;
603
3.33k
      index = 0;
604
3.33k
      if (newcache[0]->ca_bucket->av_count == BUCKET_AVAIL)
605
28
  {
606
    /* The avail is full, move the first one to newcache[1]->ca_bucket.*/
607
28
    _gdbm_put_av_elem (dbf->bucket->bucket_avail[0],
608
28
           newcache[1]->ca_bucket->bucket_avail,
609
28
           &newcache[1]->ca_bucket->av_count, 
610
28
                             dbf->coalesce_blocks);
611
28
    index = 1;
612
28
    newcache[0]->ca_bucket->av_count--;
613
28
  }
614
615
3.33k
      index1 = 0;
616
14.6k
      for (; index < dbf->bucket->av_count; index++)
617
11.3k
  {
618
11.3k
    newcache[0]->ca_bucket->bucket_avail[index1++]
619
11.3k
      = dbf->bucket->bucket_avail[index];
620
11.3k
  }
621
      
622
      /* Update the directory.  We have new file addresses for both buckets. */
623
3.33k
      dir_start1 = (dbf->bucket_dir >> (dbf->header->dir_bits - new_bits)) | 1;
624
3.33k
      dir_end = (dir_start1 + 1) << (dbf->header->dir_bits - new_bits);
625
3.33k
      dir_start1 = dir_start1 << (dbf->header->dir_bits - new_bits);
626
3.33k
      dir_start0 = dir_start1 - (dir_end - dir_start1);
627
193k
      for (index = dir_start0; index < dir_start1; index++)
628
190k
  dbf->dir[index] = adr_0;
629
193k
      for (index = dir_start1; index < dir_end; index++)
630
190k
  dbf->dir[index] = adr_1;
631
      
632
      /* Set changed flags. */
633
3.33k
      newcache[0]->ca_changed = TRUE;
634
3.33k
      newcache[1]->ca_changed = TRUE;
635
3.33k
      dbf->directory_changed = TRUE;
636
      
637
      /* Update the cache! */
638
3.33k
      dbf->bucket_dir = _gdbm_bucket_dir (dbf, next_insert);
639
      
640
      /* Invalidate old cache entry. */
641
3.33k
      avail_elem_init (&old_bucket,
642
3.33k
           dbf->header->bucket_size,
643
3.33k
           dbf->cache_mru->ca_adr);
644
3.33k
      cache_elem_free (dbf, dbf->cache_mru);
645
      
646
      /* Set dbf->bucket to the proper bucket. */
647
3.33k
      if (dbf->dir[dbf->bucket_dir] != adr_0)
648
1.86k
  {
649
1.86k
    cache_elem *t = newcache[0];
650
1.86k
    newcache[0] = newcache[1];
651
1.86k
    newcache[1] = t;
652
1.86k
  }
653
654
3.33k
      _gdbm_put_av_elem (old_bucket,
655
3.33k
       newcache[1]->ca_bucket->bucket_avail,
656
3.33k
       &newcache[1]->ca_bucket->av_count, 
657
3.33k
       dbf->coalesce_blocks);
658
659
3.33k
      lru_unlink_elem (dbf, newcache[0]);
660
3.33k
      lru_link_elem (dbf, newcache[0], NULL);
661
3.33k
    }
662
663
  /* Get rid of old directories. */
664
2.69k
  for (index = 0; index < old_count; index++)
665
0
    if (_gdbm_free (dbf, old_adr[index], old_size[index]))
666
0
      return -1;
667
668
2.69k
  return 0;
669
2.69k
}
670
671
672
/* The only place where a bucket is written.  CA_ENTRY is the
673
   cache entry containing the bucket to be written. */
674
675
int
676
_gdbm_write_bucket (GDBM_FILE dbf, cache_elem *ca_entry)
677
228k
{
678
228k
  int rc;
679
228k
  off_t file_pos; /* The return value for lseek. */
680
681
228k
  file_pos = gdbm_file_seek (dbf, ca_entry->ca_adr, SEEK_SET);
682
228k
  if (file_pos != ca_entry->ca_adr)
683
0
    {
684
0
      GDBM_SET_ERRNO (dbf, GDBM_FILE_SEEK_ERROR, TRUE);
685
0
      _gdbm_fatal (dbf, _("lseek error"));
686
0
      return -1;
687
0
    }
688
228k
  rc = _gdbm_full_write (dbf, ca_entry->ca_bucket, dbf->header->bucket_size);
689
228k
  if (rc)
690
0
    {
691
0
      GDBM_DEBUG (GDBM_DEBUG_STORE|GDBM_DEBUG_ERR,
692
0
      "%s: error writing bucket: %s",
693
0
      dbf->name, gdbm_db_strerror (dbf));   
694
0
      _gdbm_fatal (dbf, gdbm_strerror (rc));
695
0
      return -1;
696
0
    }
697
698
228k
  ca_entry->ca_changed = FALSE;
699
228k
  ca_entry->ca_data.hash_val = -1;
700
228k
  ca_entry->ca_data.elem_loc = -1;
701
228k
  return 0;
702
228k
}
703

704
/* Cache manipulation interface functions. */
705
706
16.2k
#define INIT_CACHE_BITS 9
707
708
/* Initialize the bucket cache. */
709
int
710
_gdbm_cache_init (GDBM_FILE dbf, size_t size)
711
8.13k
{
712
8.13k
  int bits;
713
8.13k
  int cache_auto;
714
  
715
8.13k
  if (size == GDBM_CACHE_AUTO)
716
8.13k
    {
717
8.13k
      cache_auto = TRUE;
718
8.13k
      bits = dbf->cache ? dbf->cache_bits : INIT_CACHE_BITS;
719
8.13k
    }
720
0
  else if (size > SIZE_T_MAX / sizeof (dbf->cache[0]))
721
0
    {
722
0
      GDBM_SET_ERRNO (dbf, GDBM_OPT_BADVAL, FALSE);
723
0
      return -1;
724
0
    }
725
0
  else
726
0
    {
727
0
      cache_auto = FALSE;
728
0
      bits = log2i (size < 4 ? 4 : size);
729
0
    }
730
731
8.13k
  dbf->cache_auto = cache_auto;
732
733
8.13k
  return cache_tab_resize (dbf, bits);
734
8.13k
}
735
736
/* Free the bucket cache */
737
void
738
_gdbm_cache_free (GDBM_FILE dbf)
739
9.09k
{
740
9.09k
  cache_elem *elem;
741
742
26.4k
  while (dbf->cache_lru)
743
17.3k
    cache_elem_free (dbf, dbf->cache_lru);
744
9.09k
  free (dbf->cache);
745
9.09k
  dbf->cache = NULL;
746
29.6k
  while ((elem = dbf->cache_avail) != NULL)
747
20.5k
    {
748
20.5k
      dbf->cache_avail = elem->ca_next;
749
20.5k
      free (elem->ca_data.dptr);
750
20.5k
      free (elem);
751
20.5k
    }
752
9.09k
}
753
754
/*
755
 * Flush cache content to disk.
756
 * All cache elements with the changed buckets form a contiguous sequence
757
 * at the head of the cache list (starting with cache_mru).
758
 */
759
int
760
_gdbm_cache_flush (GDBM_FILE dbf)
761
3.95M
{
762
3.95M
  cache_elem *elem;
763
4.18M
  for (elem = dbf->cache_mru; elem && elem->ca_changed; elem = elem->ca_next)
764
228k
    {
765
228k
      if (_gdbm_write_bucket (dbf, elem))
766
0
  return -1;
767
228k
    }
768
3.95M
  return 0;
769
3.95M
}
770
771

772
void
773
gdbm_get_cache_stats (GDBM_FILE dbf,
774
          size_t *access_count,
775
          size_t *cache_hits,
776
          size_t *cache_count,
777
          struct gdbm_cache_stat *bstat,
778
          size_t nstat)
779
0
{
780
0
  if (access_count)
781
0
    *access_count = dbf->cache_access_count;
782
0
  if (cache_hits)
783
0
    *cache_hits = dbf->cache_hits;
784
0
  if (cache_count)
785
0
    *cache_count = dbf->cache_num;
786
0
  if (bstat)
787
0
    {
788
0
      size_t i;
789
0
      cache_elem *elem;
790
791
0
      if (nstat > dbf->cache_num)
792
0
  nstat = dbf->cache_num;
793
      
794
0
      for (i = 0, elem = dbf->cache_mru; i < nstat; i++, elem = elem->ca_next)
795
0
  {
796
0
    bstat[i].adr = elem->ca_adr;
797
0
    bstat[i].hits = elem->ca_hits;
798
0
  }
799
0
    }
800
0
}