]> mj.ucw.cz Git - libucw.git/blobdiff - lib/db.c
Word type 0 is reserved.
[libucw.git] / lib / db.c
index 6276e40ba34773f2073ab57395e20b7b8c6a92d8..de48171f58ba6d6bca523c5c3319789657880885 100644 (file)
--- a/lib/db.c
+++ b/lib/db.c
@@ -1,7 +1,7 @@
 /*
  *     Sherlock Library -- Fast Database Management Routines
  *
- *     (c) 1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *     (c) 1999--2000 Martin Mares <mj@ucw.cz>
  */
 
 /*
  *     and we assume it's sorted.
  */
 
+#include "lib/lib.h"
+#include "lib/lfs.h"
+#include "lib/pagecache.h"
+#include "lib/db.h"
+#include "lib/db_internal.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
 
-#include "lib.h"
-#include "pagecache.h"
-#include "db.h"
-#include "db_internal.h"
-
 struct sdbm *
 sdbm_open(struct sdbm_options *o)
 {
@@ -37,23 +38,22 @@ sdbm_open(struct sdbm_options *o)
   struct sdbm_root root, *r;
   uns cache_size = o->cache_size ? o->cache_size : 16;
 
-  d = xmalloc(sizeof(struct sdbm));
-  bzero(d, sizeof(*d));
+  d = xmalloc_zero(sizeof(struct sdbm));
   d->flags = o->flags;
-  d->fd = open(o->name, ((d->flags & SDBM_WRITE) ? O_RDWR : O_RDONLY), 0666);
+  d->fd = sh_open(o->name, ((d->flags & SDBM_WRITE) ? O_RDWR : O_RDONLY), 0666);
   if (d->fd >= 0)                      /* Already exists, let's check it */
     {
       if (read(d->fd, &root, sizeof(root)) != sizeof(root))
        goto bad;
       if (root.magic != SDBM_MAGIC || root.version != SDBM_VERSION)
        goto bad;
-      d->file_size = lseek(d->fd, 0, SEEK_END);
+      d->file_size = sh_seek(d->fd, 0, SEEK_END);
       d->page_size = 1 << root.page_order;
       d->cache = pgc_open(d->page_size, cache_size);
       d->root_page = pgc_read(d->cache, d->fd, 0);
       d->root = (void *) d->root_page->data;
     }
-  else if ((d->flags & SDBM_CREAT) && (d->fd = open(o->name, O_RDWR | O_CREAT, 0666)) >= 0)
+  else if ((d->flags & SDBM_CREAT) && (d->fd = sh_open(o->name, O_RDWR | O_CREAT, 0666)) >= 0)
     {
       struct page *q;
       uns page_order = o->page_order;
@@ -101,14 +101,17 @@ sdbm_close(struct sdbm *d)
     pgc_close(d->cache);
   if (d->fd >= 0)
     close(d->fd);
-  free(d);
+  xfree(d);
 }
 
 static uns
 sdbm_alloc_pages(struct sdbm *d, uns number)
 {
   uns where = d->file_size;
-  d->file_size += number << d->page_order;
+  uns size = number << d->page_order;
+  if (d->file_size + size < d->file_size)      /* Wrap around? */
+    die("SDB: Database file too large, giving up");
+  d->file_size += size;
   return where;
 }
 
@@ -146,16 +149,13 @@ static u32
 sdbm_hash(byte *key, uns keylen)
 {
   /*
-   *  This is the same hash function as GDBM uses.
-   *  It seems to work well.
+   *  This used to be the same hash function as GDBM uses,
+   *  but it turned out that it tends to give the same results
+   *  on similar keys. Damn it.
    */
-  u32 value;
-  uns index;
-
-  /* Set the initial value from key. */
-  value = 0x238F13AF * keylen;
-  for (index = 0; index < keylen; index++)
-    value = value + (key[index] << (index*5 % 24));
+  u32 value = 0x238F13AF * keylen;
+  while (keylen--)
+    value = 37*value + *key++;
   return (1103515243 * value + 12345);
 }
 
@@ -367,12 +367,22 @@ sdbm_split_dir(struct sdbm *d, uns dirpos, uns count, uns pos)
     }
 }
 
+static inline uns
+sdbm_dirpos(struct sdbm *d, uns hash)
+{
+  if (d->dir_shift != 32)              /* avoid shifting by 32 bits */
+    return (hash >> d->dir_shift) << 2;        /* offset in the directory */
+  else
+    return 0;
+}
+
 static struct page *
-sdbm_split_page(struct sdbm *d, struct page *b, u32 hash, uns dirpos)
+sdbm_split_page(struct sdbm *d, struct page *b, u32 hash)
 {
   struct page *p[2];
-  uns i, rank, sigbit, rank_log;
+  uns i, rank, sigbit, rank_log, dirpos;
 
+  dirpos = sdbm_dirpos(d, hash);
   rank = sdbm_page_rank(d, dirpos);    /* rank = # of pointers to this page */
   if (rank == 1)
     {
@@ -424,10 +434,7 @@ sdbm_access(struct sdbm *d, byte *key, uns keylen, byte *val, uns *vallen, uns m
   if (!mode && !(d->flags & SDBM_WRITE))
     return SDBM_ERROR_READ_ONLY;
   hash = sdbm_hash(key, keylen);
-  if (d->dir_shift != 32)              /* avoid shifting by 32 bits */
-    h = (hash >> d->dir_shift) << 2;   /* offset in the directory */
-  else
-    h = 0;
+  h = sdbm_dirpos(d, hash);
   p = pgc_read(d->cache, d->fd, d->root->dir_start + (h & ~d->page_mask));
   pos = GET32(p->data, h & d->page_mask);
   pgc_put(d->cache, p);
@@ -478,7 +485,7 @@ insert:
              pgc_put(d->cache, q);
              return SDBM_ERROR_GIANT;
            }
-         q = sdbm_split_page(d, q, hash, h);
+         q = sdbm_split_page(d, q, hash);
          b = (void *) q->data;
        }
       sdbm_store_entry(d, b->data + b->used, key, keylen, val, *vallen);