/*************************************************************************************************
 * Implementation of Depot
 *                                                      Copyright (C) 2000-2003 Mikio Hirabayashi
 * This file is part of QDBM, Quick Database Manager.
 * QDBM is free software; you can redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software Foundation; either version
 * 2.1 of the License or any later version.  QDBM is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
 * details.
 * You should have received a copy of the GNU Lesser General Public License along with QDBM; if
 * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 * 02111-1307 USA.
 *************************************************************************************************/


#include "depot.h"
#include "myconf.h"

#define DP_FILEMODE    00644             /* permission of a creating file */
#define DP_MAGICNUMB   "[DEPOT]\n\f"     /* magic number on environments of big endian */
#define DP_MAGICNUML   "[depot]\n\f"     /* magic number on environments of little endian */
#define DP_HEADSIZ     48                /* size of the reagion of the header */
#define DP_FSIZOFF     16                /* offset of the region of for the file size */
#define DP_BNUMOFF     24                /* offset of the region of for the bucket number */
#define DP_RNUMOFF     32                /* offset of the region of for the record number */
#define DP_DEFBNUM     8191              /* default bucket number */
#define DP_COALMAX     3                 /* number of trials for record coalescence */
#define DP_TMPFSUF     MYEXTSTR "dptmp"  /* suffix of a temporary file */
#define DP_OPTBLOAD    0.25              /* ratio of bucket loading at optimization */
#define DP_OPTRUNIT    256               /* number of records in a process of optimization */
#define DP_IOBUFSIZ    8192              /* size of an I/O buffer */

enum {                                   /* enumeration for a record header */
  DP_RHIFLAGS,                           /* offset of flags */
  DP_RHIHASH,                            /* offset of value of the second hash function */
  DP_RHIKSIZ,                            /* offset of the size of the key */
  DP_RHIVSIZ,                            /* offset of the size of the value */
  DP_RHIPSIZ,                            /* offset of the size of the padding bytes */
  DP_RHILEFT,                            /* offset of the offset of the left child */
  DP_RHIRIGHT,                           /* offset of the offset of the right child */
  DP_RHNUM                               /* number of elements of a header */
};

enum {                                   /* enumeration for the flag of a record */
  DP_RECFDEL = 1 << 0,                   /* deleted */
  DP_RECFREUSE = 1 << 1                  /* reusable */
};


/* private function prototypes */
static int dpbigendian(void);
static int dpbyteswap(int num);
static char *dpstrdup(const char *str);
static int dplock(int fd, int ex);
static int dpwrite(int fd, const void *buf, int size);
static int dpseekwrite(int fd, int off, const void *buf, int size);
static int dpseekwritenum(int fd, int off, int num);
static int dpread(int fd, void *buf, int size);
static int dpseekread(int fd, int off, void *buf, int size);
static int dpfcopy(int destfd, int destoff, int srcfd, int srcoff);
static int dpfirsthash(const char *kbuf, int ksiz);
static int dpsecondhash(const char *kbuf, int ksiz);
static int dpthirdhash(const char *kbuf, int ksiz);
static int dpgetprime(int num);
static int dppadsize(DEPOT *depot, int vsiz);
static int dprecsize(int *head);
static int dprechead(DEPOT *depot, int off, int *head);
static char *dpreckey(DEPOT *depot, int off, int *head);
static char *dprecval(DEPOT *depot, int off, int *head, int start, int max);
static int dpkeycmp(const char *abuf, int asiz, const char *bbuf, int bsiz);
static int dprecsearch(DEPOT *depot, const char *kbuf, int ksiz, int hash,
                       int *bip, int *offp, int *entp, int *head, int delhit);
static int dprecrewrite(DEPOT *depot, int off, int rsiz, const char *kbuf, int ksiz,
                        const char *vbuf, int vsiz, int hash, int left, int right);
static int dprecappend(DEPOT *depot, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
                       int hash, int left, int right);
static int dprecover(DEPOT *depot, int off, int *head, const char *vbuf, int vsiz, int cat);
static int dprecdelete(DEPOT *depot, int off, int *head, int reusable);



/*************************************************************************************************
 * public objects
 *************************************************************************************************/


/* String containing the version information. */
const char *dpversion = _QDBM_VERSION;


/* Last happened error code. */
int dpecode = DP_ENOERR;


/* Get a message string corresponding to an error code. */
const char *dperrmsg(int ecode){
  switch(ecode){
  case DP_ENOERR: return "no error";
  case DP_EFATAL: return "with fatal error";
  case DP_EMODE: return "invalid mode";
  case DP_EBROKEN: return "broken database file";
  case DP_EKEEP: return "existing record";
  case DP_ENOITEM: return "no item found";
  case DP_EALLOC: return "memory allocation error";
  case DP_EMAP: return "memory mapping error";
  case DP_EOPEN: return "open error";
  case DP_ECLOSE: return "close error";
  case DP_ETRUNC: return "trunc error";
  case DP_ESYNC: return "sync error";
  case DP_ESTAT: return "stat error";
  case DP_ESEEK: return "seek error";
  case DP_EREAD: return "read error";
  case DP_EWRITE: return "write error";
  case DP_ELOCK: return "lock error";
  case DP_EUNLINK: return "unlink error";
  case DP_EMKDIR: return "mkdir error";
  case DP_ERMDIR: return "rmdir error";
  case DP_EMISC: return "miscellaneous error";
  }
  return "(invalid ecode)";
}


/* Get a database handle. */
DEPOT *dpopen(const char *name, int omode, int bnum){
  int mode, fd, inode, fsiz, rnum, c, msiz;
  char hbuf[DP_HEADSIZ], *map;
  struct stat sbuf;
  DEPOT *depot;
  assert(name);
  mode = O_RDONLY;
  if(omode & DP_OWRITER){
    mode = O_RDWR;
    if(omode & DP_OCREAT) mode |= O_CREAT;
  }
  if((fd = open(name, mode, DP_FILEMODE)) == -1){
    dpecode = DP_EOPEN;
    return NULL;
  }
  if(!(omode & DP_ONOLCK)){
    if(!dplock(fd, omode & DP_OWRITER)){
      close(fd);
      return NULL;
    }
  }
  if((omode & DP_OWRITER) && (omode & DP_OTRUNC)){
    if(ftruncate(fd, 0) == -1){
      close(fd);
      dpecode = DP_ETRUNC;
      return NULL;
    }
  }
  if(fstat(fd, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)){
    close(fd);
    dpecode = DP_ESTAT;
    return NULL;
  }
  inode = sbuf.st_ino;
  fsiz = sbuf.st_size;
  if((omode & DP_OWRITER) && fsiz == 0){
    memset(hbuf, 0, DP_HEADSIZ);
    if(dpbigendian()){
      memcpy(hbuf, DP_MAGICNUMB, strlen(DP_MAGICNUMB));
    } else {
      memcpy(hbuf, DP_MAGICNUML, strlen(DP_MAGICNUML));
    }
    bnum = bnum < 1 ? DP_DEFBNUM : bnum;
    bnum = dpgetprime(bnum);
    memcpy(hbuf + DP_BNUMOFF, &bnum, sizeof(int));
    rnum = 0;
    memcpy(hbuf + DP_RNUMOFF, &rnum, sizeof(int));
    c = 0;
    fsiz = DP_HEADSIZ + bnum * sizeof(int);
    memcpy(hbuf + DP_FSIZOFF, &fsiz, sizeof(int));
    if(!dpseekwrite(fd, 0, hbuf, DP_HEADSIZ) ||
       !dpseekwrite(fd, fsiz - 1, &c, 1)){
      close(fd);
      return NULL;
    }
  }
  if(!dpseekread(fd, 0, hbuf, DP_HEADSIZ)){
    close(fd);
    dpecode = DP_EBROKEN;
    return NULL;
  }
  if((dpbigendian() ? memcmp(hbuf, DP_MAGICNUMB, strlen(DP_MAGICNUMB)) != 0 :
      memcmp(hbuf, DP_MAGICNUML, strlen(DP_MAGICNUML)) != 0) ||
     memcmp(hbuf + DP_FSIZOFF, &fsiz, sizeof(int)) != 0){
    close(fd);
    dpecode = DP_EBROKEN;
    return NULL;
  }
  bnum = *((int *)(hbuf + DP_BNUMOFF));
  rnum = *((int *)(hbuf + DP_RNUMOFF));
  if(bnum < 1 || rnum < 0 || fsiz < DP_HEADSIZ + bnum * sizeof(int)){
    close(fd);
    dpecode = DP_EBROKEN;
    return NULL;
  }
  msiz = DP_HEADSIZ + bnum * sizeof(int);
  map = mmap(0, msiz, PROT_READ | ((mode & DP_OWRITER) ? PROT_WRITE : 0), MAP_SHARED, fd, 0);
  if(map == MAP_FAILED){
    close(fd);
    dpecode = DP_EMAP;
    return NULL;
  }
  if(!(depot = malloc(sizeof(DEPOT))) || !(depot->name = dpstrdup(name))){
    munmap(map, msiz);
    free(depot);
    close(fd);
    dpecode = DP_EALLOC;
    return NULL;
  }
  depot->wmode = (mode & DP_OWRITER);
  depot->inode = inode;
  depot->fd = fd;
  depot->fsiz = fsiz;
  depot->map = map;
  depot->msiz = msiz;
  depot->buckets = (int *)(map + DP_HEADSIZ);
  depot->bnum = bnum;
  depot->rnum = rnum;
  depot->fatal = FALSE;
  depot->ioff = 0;
  depot->mroff = -1;
  depot->mrsiz = -1;
  depot->align = 0;
  return depot;
}


/* Close a database handle. */
int dpclose(DEPOT *depot){
  int err;
  assert(depot);
  err = FALSE;
  if(depot->wmode){
    *((int *)(depot->map + DP_FSIZOFF)) = depot->fsiz;
    *((int *)(depot->map + DP_RNUMOFF)) = depot->rnum;
  }
  if(depot->map != MAP_FAILED){
    if(munmap(depot->map, depot->msiz) == -1){
      err = TRUE;
      dpecode = DP_EMAP;
    }
  }
  if(close(depot->fd) == -1){
    err = TRUE;
    dpecode = DP_ECLOSE;
  }
  free(depot->name);
  free(depot);
  return err ? FALSE : TRUE;
}


/* Store a record. */
int dpput(DEPOT *depot, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode){
  int head[DP_RHNUM], next[DP_RHNUM];
  int i, hash, bi, off, entoff, newoff, rsiz, nsiz, fdel, mroff, mrsiz;
  char *tval;
  assert(depot && kbuf && vbuf);
  if(depot->fatal){
    dpecode = DP_EFATAL;
    return FALSE;
  }
  if(!depot->wmode){
    dpecode = DP_EMODE;
    return FALSE;
  }
  if(ksiz < 0) ksiz = strlen(kbuf);
  if(vsiz < 0) vsiz = strlen(vbuf);
  newoff = -1;
  hash = dpsecondhash(kbuf, ksiz);
  switch(dprecsearch(depot, kbuf, ksiz, hash, &bi, &off, &entoff, head, TRUE)){
  case -1:
    depot->fatal = TRUE;
    return FALSE;
  case 0:
    fdel = head[DP_RHIFLAGS] & DP_RECFDEL;
    if(dmode == DP_DKEEP && !fdel){
      dpecode = DP_EKEEP;
      return FALSE;
    }
    if(fdel){
      head[DP_RHIPSIZ] += head[DP_RHIVSIZ];
      head[DP_RHIVSIZ] = 0;
    }
    rsiz = dprecsize(head);
    nsiz = DP_RHNUM * sizeof(int) + ksiz + vsiz;
    if(dmode == DP_DCAT) nsiz += head[DP_RHIVSIZ];
    if(off + rsiz >= depot->fsiz){
      if(rsiz < nsiz){
        head[DP_RHIPSIZ] += nsiz - rsiz;
        rsiz = nsiz;
        depot->fsiz = off + rsiz;
      }
    } else {
      for(i = 0; i < DP_COALMAX && nsiz > rsiz && off + rsiz < depot->fsiz; i++){
        if(off + rsiz == depot->mroff){
          depot->mroff = -1;
          depot->mrsiz = -1;
        }
        if(!dprechead(depot, off + rsiz, next)) return FALSE;
        if(!(next[DP_RHIFLAGS] & DP_RECFREUSE)) break;
        head[DP_RHIPSIZ] += dprecsize(next);
        rsiz += dprecsize(next);
      }
    }
    if(nsiz <= rsiz){
      if(!dprecover(depot, off, head, vbuf, vsiz, dmode == DP_DCAT)){
        depot->fatal = TRUE;
        return FALSE;
      }
    } else {
      tval = NULL;
      if(dmode == DP_DCAT){
        if(!(tval = dprecval(depot, off, head, 0, -1))){
          depot->fatal = TRUE;
          return FALSE;
        }
        if(!(tval = realloc(tval, head[DP_RHIVSIZ] + vsiz))){
          free(tval);
          dpecode = DP_EALLOC;
          depot->fatal = TRUE;
          return FALSE;
        }
        memcpy(tval + head[DP_RHIVSIZ], vbuf, vsiz);
        vsiz += head[DP_RHIVSIZ];
        vbuf = tval;
      }
      mroff = depot->mroff;
      mrsiz = depot->mrsiz;
      if(!dprecdelete(depot, off, head, TRUE)){
        free(tval);
        depot->fatal = TRUE;
        return FALSE;
      }
      if(mroff > 0 && nsiz <= mrsiz){
        if(!dprecrewrite(depot, mroff, mrsiz, kbuf, ksiz, vbuf, vsiz,
                         hash, head[DP_RHILEFT], head[DP_RHIRIGHT])){
          free(tval);
          depot->fatal = TRUE;
          return FALSE;
        }
        newoff = mroff;
      } else {
        if((newoff = dprecappend(depot, kbuf, ksiz, vbuf, vsiz,
                                 hash, head[DP_RHILEFT], head[DP_RHIRIGHT])) == -1){
          free(tval);
          depot->fatal = TRUE;
          return FALSE;
        }
      }
      free(tval);
    }
    if(fdel) depot->rnum++;
    break;
  default:
    if((newoff = dprecappend(depot, kbuf, ksiz, vbuf, vsiz, hash, 0, 0)) == -1){
      depot->fatal = TRUE;
      return FALSE;
    }
    depot->rnum++;
    break;
  }
  if(newoff > 0){
    if(entoff > 0){
      if(!dpseekwritenum(depot->fd, entoff, newoff)){
        depot->fatal = TRUE;
        return FALSE;
      }
    } else {
      depot->buckets[bi] = newoff;
    }
  }
  return TRUE;
}


/* Delete a record. */
int dpout(DEPOT *depot, const char *kbuf, int ksiz){
  int head[DP_RHNUM], hash, bi, off, entoff;
  assert(depot && kbuf);
  if(depot->fatal){
    dpecode = DP_EFATAL;
    return FALSE;
  }
  if(!depot->wmode){
    dpecode = DP_EMODE;
    return FALSE;
  }
  if(ksiz < 0) ksiz = strlen(kbuf);
  hash = dpsecondhash(kbuf, ksiz);
  switch(dprecsearch(depot, kbuf, ksiz, hash, &bi, &off, &entoff, head, FALSE)){
  case -1:
    depot->fatal = TRUE;
    return FALSE;
  case 0:
    break;
  default:
    dpecode = DP_ENOITEM;
    return FALSE;
  }
  if(!dprecdelete(depot, off, head, FALSE)){
    depot->fatal = TRUE;
    return FALSE;
  }
  depot->rnum--;
  return TRUE;
}


/* Retrieve a record. */
char *dpget(DEPOT *depot, const char *kbuf, int ksiz, int start, int max, int *sp){
  int head[DP_RHNUM], hash, bi, off, entoff;
  char *vbuf;
  assert(depot && kbuf && start >= 0);
  if(depot->fatal){
    dpecode = DP_EFATAL;
    return NULL;
  }
  if(ksiz < 0) ksiz = strlen(kbuf);
  hash = dpsecondhash(kbuf, ksiz);
  switch(dprecsearch(depot, kbuf, ksiz, hash, &bi, &off, &entoff, head, FALSE)){
  case -1:
    depot->fatal = TRUE;
    return NULL;
  case 0:
    break;
  default:
    dpecode = DP_ENOITEM;
    return NULL;
  }
  if(start > head[DP_RHIVSIZ]){
    dpecode = DP_ENOITEM;
    return NULL;
  }
  if(!(vbuf = dprecval(depot, off, head, start, max))){
    depot->fatal = TRUE;
    return NULL;
  }
  if(sp){
    if(max < 0){
      *sp = head[DP_RHIVSIZ];
    } else {
      *sp = max < head[DP_RHIVSIZ] ? max : head[DP_RHIVSIZ];
    }
  }
  return vbuf;
}


/* Get the size of the value of a record. */
int dpvsiz(DEPOT *depot, const char *kbuf, int ksiz){
  int head[DP_RHNUM], hash, bi, off, entoff;
  assert(depot && kbuf);
  if(depot->fatal){
    dpecode = DP_EFATAL;
    return -1;
  }
  if(ksiz < 0) ksiz = strlen(kbuf);
  hash = dpsecondhash(kbuf, ksiz);
  switch(dprecsearch(depot, kbuf, ksiz, hash, &bi, &off, &entoff, head, FALSE)){
  case -1:
    depot->fatal = TRUE;
    return -1;
  case 0:
    break;
  default:
    dpecode = DP_ENOITEM;
    return -1;
  }
  return head[DP_RHIVSIZ];
}


/* Initialize the iterator of a database handle. */
int dpiterinit(DEPOT *depot){
  assert(depot);
  if(depot->fatal){
    dpecode = DP_EFATAL;
    return FALSE;
  }
  depot->ioff = 0;
  return TRUE;
}


/* Get the next key of the iterator. */
char *dpiternext(DEPOT *depot, int *sp){
  int off, head[DP_RHNUM];
  char *kbuf;
  assert(depot);
  if(depot->fatal){
    dpecode = DP_EFATAL;
    return NULL;
  }
  off = DP_HEADSIZ + depot->bnum * sizeof(int);
  off = off > depot->ioff ? off : depot->ioff;
  while(off < depot->fsiz){
    if(!dprechead(depot, off, head)){
      depot->fatal = TRUE;
      return NULL;
    }
    if(head[DP_RHIFLAGS] & DP_RECFDEL){
      off += dprecsize(head);
    } else {
      if(!(kbuf = dpreckey(depot, off, head))){
        depot->fatal = TRUE;
        return NULL;
      }
      depot->ioff = off + dprecsize(head);
      if(sp) *sp = head[DP_RHIKSIZ];
      return kbuf;
    }
  }
  dpecode = DP_ENOITEM;
  return NULL;
}


/* Set alignment of a database handle. */
int dpsetalign(DEPOT *depot, int align){
  assert(depot);
  if(depot->fatal){
    dpecode = DP_EFATAL;
    return FALSE;
  }
  if(!depot->wmode){
    dpecode = DP_EMODE;
    return FALSE;
  }
  depot->align = align;
  return TRUE;
}


/* Synchronize contents of updating a database with the file and the device. */
int dpsync(DEPOT *depot){
  assert(depot);
  if(depot->fatal){
    dpecode = DP_EFATAL;
    return FALSE;
  }
  if(!depot->wmode){
    dpecode = DP_EMODE;
    return FALSE;
  }
  *((int *)(depot->map + DP_FSIZOFF)) = depot->fsiz;
  *((int *)(depot->map + DP_RNUMOFF)) = depot->rnum;
  if(msync(depot->map, depot->msiz, MS_SYNC) == -1){
    dpecode = DP_EMAP;
    depot->fatal = TRUE;
    return FALSE;
  }
  if(fsync(depot->fd) == -1){
    dpecode = DP_ESYNC;
    depot->fatal = TRUE;
    return FALSE;
  }
  return TRUE;
}


/* Optimize a database. */
int dpoptimize(DEPOT *depot, int bnum){
  DEPOT *tdepot;
  char *name;
  int i, err, off, head[DP_RHNUM], ksizs[DP_OPTRUNIT], vsizs[DP_OPTRUNIT], unum;
  char *kbufs[DP_OPTRUNIT], *vbufs[DP_OPTRUNIT];
  assert(depot);
  if(depot->fatal){
    dpecode = DP_EFATAL;
    return FALSE;
  }
  if(!depot->wmode){
    dpecode = DP_EMODE;
    return FALSE;
  }
  if(!(name = malloc(strlen(depot->name) + strlen(DP_TMPFSUF) + 1))){
    dpecode = DP_EALLOC;
    depot->fatal = FALSE;
    return FALSE;
  }
  sprintf(name, "%s%s", depot->name, DP_TMPFSUF);
  bnum = dpgetprime(bnum > 0 ? bnum : (int)(depot->rnum * (1.0 / DP_OPTBLOAD)) + 1);
  if(!(tdepot = dpopen(name, DP_OWRITER | DP_OCREAT | DP_OTRUNC, bnum))){
    free(name);
    depot->fatal = TRUE;
    return FALSE;
  }
  free(name);
  tdepot->align = depot->align;
  err = FALSE;
  off = DP_HEADSIZ + depot->bnum * sizeof(int);
  unum = 0;
  while(off < depot->fsiz){
    if(!dprechead(depot, off, head)){
      err = TRUE;
      break;
    }
    if(!(head[DP_RHIFLAGS] & DP_RECFDEL)){
      kbufs[unum] = dpreckey(depot, off, head);
      ksizs[unum] = head[DP_RHIKSIZ];
      vbufs[unum] = dprecval(depot, off, head, 0, -1);
      vsizs[unum] = head[DP_RHIVSIZ];
      unum++;
      if(unum >= DP_OPTRUNIT){
        for(i = 0; i < unum; i++){
          if(kbufs[i] && vbufs[i]){
            if(!dpput(tdepot, kbufs[i], ksizs[i], vbufs[i], vsizs[i], DP_DKEEP)) err = TRUE;
          } else {
            err = TRUE;
          }
          free(kbufs[i]);
          free(vbufs[i]);
          if(err) break;
        }
        unum = 0;
      }
    }
    off += dprecsize(head);
    if(err) break;
  }
  for(i = 0; i < unum; i++){
    if(kbufs[i] && vbufs[i]){
      if(!dpput(tdepot, kbufs[i], ksizs[i], vbufs[i], vsizs[i], DP_DKEEP)) err = TRUE;
    } else {
      err = TRUE;
    }
    free(kbufs[i]);
    free(vbufs[i]);
    if(err) break;
  }
  if(!dpsync(tdepot)) err = TRUE;
  if(err){
    unlink(tdepot->name);
    dpclose(tdepot);
    depot->fatal = TRUE;
    return FALSE;
  }
  if(munmap(depot->map, depot->msiz) == -1){
    dpclose(tdepot);
    dpecode = DP_EMAP;
    depot->fatal = TRUE;
    return FALSE;
  }
  depot->map = MAP_FAILED;
  if(ftruncate(depot->fd, 0) == -1){
    dpclose(tdepot);
    unlink(tdepot->name);
    dpecode = DP_ETRUNC;
    depot->fatal = TRUE;
    return FALSE;
  }
  if(dpfcopy(depot->fd, 0, tdepot->fd, 0) == -1){
    dpclose(tdepot);
    unlink(tdepot->name);
    depot->fatal = TRUE;
    return FALSE;
  }
  depot->fsiz = tdepot->fsiz;
  depot->bnum = tdepot->bnum;
  depot->ioff = 0;
  depot->mroff = -1;
  depot->mrsiz = -1;
  depot->msiz = tdepot->msiz;
  depot->map = mmap(0, depot->msiz, PROT_READ | PROT_WRITE, MAP_SHARED, depot->fd, 0);
  if(depot->map == MAP_FAILED){
    dpecode = DP_EMAP;
    depot->fatal = TRUE;
    return FALSE;
  }
  depot->buckets = (int *)(depot->map + DP_HEADSIZ);
  if(!(name = dpname(tdepot))){
    dpclose(tdepot);
    unlink(tdepot->name);
    depot->fatal = TRUE;
    return FALSE;
  }
  if(!dpclose(tdepot)){
    free(name);
    unlink(tdepot->name);
    depot->fatal = TRUE;
    return FALSE;
  }
  if(unlink(name) == -1){
    free(name);
    dpecode = DP_EUNLINK;
    depot->fatal = TRUE;
    return FALSE;
  }
  free(name);
  return TRUE;
}


/* Get the name of a database. */
char *dpname(DEPOT *depot){
  char *name;
  assert(depot);
  if(depot->fatal){
    dpecode = DP_EFATAL;
    return NULL;
  }
  if(!(name = dpstrdup(depot->name))){
    dpecode = DP_EALLOC;
    depot->fatal = TRUE;
    return NULL;
  }
  return name;
}


/* Get the size of a database file. */
int dpfsiz(DEPOT *depot){
  assert(depot);
  if(depot->fatal){
    dpecode = DP_EFATAL;
    return -1;
  }
  return depot->fsiz;
}


/* Get the number of the elements of the bucket array. */
int dpbnum(DEPOT *depot){
  assert(depot);
  if(depot->fatal){
    dpecode = DP_EFATAL;
    return -1;
  }
  return depot->bnum;
}


/* Get the number of the used elements of the bucket array. */
int dpbusenum(DEPOT *depot){
  int i, hits;
  assert(depot);
  if(depot->fatal){
    dpecode = DP_EFATAL;
    return -1;
  }
  hits = 0;
  for(i = 0; i < depot->bnum; i++){
    if(depot->buckets[i]) hits++;
  }
  return hits;
}


/* Get the number of the records stored in a database. */
int dprnum(DEPOT *depot){
  assert(depot);
  if(depot->fatal){
    dpecode = DP_EFATAL;
    return -1;
  }
  return depot->rnum;
}


/* Check whether a database handle is a writer or not. */
int dpwritable(DEPOT *depot){
  assert(depot);
  return depot->wmode;
}


/* Check whether a database has a fatal error or not. */
int dpfatalerror(DEPOT *depot){
  assert(depot);
  return depot->fatal;
}


/* Get the inode number of a database file. */
int dpinode(DEPOT *depot){
  assert(depot);
  return depot->inode;
}


/* Get the file descriptor of a database file. */
int dpfdesc(DEPOT *depot){
  assert(depot);
  return depot->fd;
}


/* Remove a database file. */
int dpremove(const char *name){
  struct stat sbuf;
  DEPOT *depot;
  assert(name);
  if(stat(name, &sbuf) == -1){
    dpecode = DP_ESTAT;
    return FALSE;
  }
  if((depot = dpopen(name, DP_OWRITER | DP_OTRUNC, -1)) != NULL) dpclose(depot);
  if(unlink(name) == -1){
    dpecode = DP_EUNLINK;
    return FALSE;
  }
  return TRUE;
}


/* Convert a database file for another platform with different byte order. */
int dpeconv(const char *name, int big){
  int i, ifd, ofd, mybig, fbig, fsiz, bnum, rnum, err, rhead[DP_RHNUM], off, ksiz, vsiz, psiz;
  char *tname, hbuf[DP_HEADSIZ], *bbuf, *rbuf;
  assert(name);
  if((ifd = open(name, O_RDONLY, DP_FILEMODE)) == -1){
    dpecode = DP_EOPEN;
    return FALSE;
  }
  if(!dplock(ifd, FALSE) || !dpseekread(ifd, 0, hbuf, DP_HEADSIZ)){
    close(ifd);
    return FALSE;
  }
  mybig = dpbigendian();
  fbig = FALSE;
  if(!memcmp(hbuf, DP_MAGICNUMB, strlen(DP_MAGICNUMB))){
    fbig = TRUE;
  } else if(!memcmp(hbuf, DP_MAGICNUML, strlen(DP_MAGICNUML))){
    fbig = FALSE;
  } else {
    close(ifd);
    dpecode = DP_EBROKEN;
    return FALSE;
  }
  if(!(tname = malloc(strlen(name) + strlen(DP_TMPFSUF) + 1))){
    close(ifd);
    dpecode = DP_EALLOC;
    return FALSE;
  }
  sprintf(tname, "%s%s", name, DP_TMPFSUF);
  if((ofd = open(tname, O_WRONLY | O_CREAT | O_TRUNC, DP_FILEMODE)) == -1){
    free(tname);
    close(ifd);
    dpecode = DP_EOPEN;
    return FALSE;
  }
  fsiz = *((int *)(hbuf + DP_FSIZOFF));
  bnum = *((int *)(hbuf + DP_BNUMOFF));
  rnum = *((int *)(hbuf + DP_RNUMOFF));
  if(mybig ? !fbig : fbig){
    fsiz = dpbyteswap(fsiz);
    bnum = dpbyteswap(bnum);
    rnum = dpbyteswap(rnum);
  }
  if(fsiz < 1 || bnum < 1 || rnum < 0){
    free(tname);
    close(ifd);
    close(ofd);
    dpecode = DP_EBROKEN;
    return FALSE;
  }
  strcpy(hbuf, big ? DP_MAGICNUMB : DP_MAGICNUML);
  if(big ? !fbig : fbig){
    *((int *)(hbuf + DP_FSIZOFF)) = dpbyteswap(*((int *)(hbuf + DP_FSIZOFF)));
    *((int *)(hbuf + DP_BNUMOFF)) = dpbyteswap(*((int *)(hbuf + DP_BNUMOFF)));
    *((int *)(hbuf + DP_RNUMOFF)) = dpbyteswap(*((int *)(hbuf + DP_RNUMOFF)));
  }
  if(!dpseekwrite(ofd, 0, hbuf, DP_HEADSIZ)){
    free(tname);
    close(ifd);
    close(ofd);
    return FALSE;
  }
  if(!(bbuf = malloc(bnum * sizeof(int)))){
    free(tname);
    close(ifd);
    close(ofd);
    dpecode = DP_EALLOC;
    return FALSE;
  }
  if(!dpseekread(ifd, DP_HEADSIZ, bbuf, bnum * sizeof(int))){
    free(tname);
    free(bbuf);
    close(ifd);
    close(ofd);
    return FALSE;
  }
  if(big ? !fbig : fbig){
    for(i = 0; i < bnum * sizeof(int); i += sizeof(int)){
      *((int *)(bbuf + i)) = dpbyteswap(*((int *)(bbuf + i)));
    }
  }
  if(!dpseekwrite(ofd, DP_HEADSIZ, bbuf, bnum * sizeof(int))){
    free(tname);
    free(bbuf);
    close(ifd);
    close(ofd);
    return FALSE;
  }
  free(bbuf);
  err = FALSE;
  off = DP_HEADSIZ + bnum * sizeof(int);
  while(off < fsiz){
    if(!dpseekread(ifd, off, rhead, sizeof(rhead))){
      err = TRUE;
      break;
    }
    ksiz = rhead[DP_RHIKSIZ];
    vsiz = rhead[DP_RHIVSIZ];
    psiz = rhead[DP_RHIPSIZ];
    if(mybig ? !fbig : fbig){
      ksiz = dpbyteswap(ksiz);
      vsiz = dpbyteswap(vsiz);
      psiz = dpbyteswap(psiz);
    }
    if(ksiz < 0 || vsiz < 0 || psiz < 0){
      dpecode = DP_EBROKEN;
      err = TRUE;
      break;
    }
    if(!(rbuf = malloc(sizeof(rhead) + ksiz + vsiz + psiz))){
      dpecode = DP_EALLOC;
      err = TRUE;
      break;
    }
    if(big ? !fbig : fbig){
      for(i = 0; i < DP_RHNUM; i++){
        rhead[i] = dpbyteswap(rhead[i]);
      }
    }
    memcpy(rbuf, rhead, sizeof(rhead));
    if(!dpseekread(ifd, off + sizeof(rhead), rbuf + sizeof(rhead), ksiz + vsiz + psiz) ||
       !dpseekwrite(ofd, -1, rbuf, sizeof(rhead) + ksiz + vsiz + psiz)){
      free(rbuf);
      err = TRUE;
      break;
    }
    free(rbuf);
    off += sizeof(rhead) + ksiz + vsiz + psiz;
  }
  close(ifd);
  close(ofd);
  if(rename(tname, name) == -1){
    dpecode = DP_EMISC;
    err = TRUE;
  }
  free(tname);
  return err ? FALSE : TRUE;
}


/* Hash function used inside Depot. */
int dpinnerhash(const char *kbuf, int ksiz){
  assert(kbuf);
  if(ksiz < 0) ksiz = strlen(kbuf);
  return dpfirsthash(kbuf, ksiz);
}


/* Hash function which is independent from the hash functions used inside Depot. */
int dpouterhash(const char *kbuf, int ksiz){
  assert(kbuf);
  if(ksiz < 0) ksiz = strlen(kbuf);
  return dpthirdhash(kbuf, ksiz);
}


/* Get a natural prime number not less than a number. */
int dpprimenum(int num){
  assert(num > 0);
  return dpgetprime(num);
}



/*************************************************************************************************
 * Functions for Experts
 *************************************************************************************************/


/* Synchronize updating contents on memory. */
int dpmemsync(DEPOT *depot){
  assert(depot);
  if(depot->fatal){
    dpecode = DP_EFATAL;
    return FALSE;
  }
  if(!depot->wmode){
    dpecode = DP_EMODE;
    return FALSE;
  }
  *((int *)(depot->map + DP_FSIZOFF)) = depot->fsiz;
  *((int *)(depot->map + DP_RNUMOFF)) = depot->rnum;
  if(msync(depot->map, depot->msiz, MS_ASYNC) == -1){
    dpecode = DP_EMAP;
    depot->fatal = TRUE;
    return FALSE;
  }
  return TRUE;
}



/*************************************************************************************************
 * private objects
 *************************************************************************************************/


/* Check whether the byte order of the platform is big endian or not.
   The return value is true if bigendian, else, it is false. */
static int dpbigendian(void){
  char buf[sizeof(int)];
  *(int *)buf = 1;
  return !buf[0];
}


/* Get a byte-reversed integer.
   `num' specifies an original integer.
   The return value is the swapped integer of the original integer. */
static int dpbyteswap(int num){
  char buf[sizeof(int)];
  int i;
  for(i = 0; i < sizeof(int); i++){
    buf[i] = ((char *)&num)[sizeof(int)-i-1];
  }
  return *(int *)buf;
}


/* Get a copied string.
   `str' specifies an original string.
   The return value is a copied string whose region is allocated by `malloc'. */
static char *dpstrdup(const char *str){
  int len;
  char *buf;
  assert(str);
  len = strlen(str);
  if(!(buf = malloc(len + 1))) return NULL;
  memcpy(buf, str, len + 1);
  return buf;
}


/* Lock a file descriptor.
   `fd' specifies a file descriptor.
   `ex' specifies whether an exclusive lock or a shared lock is performed.
   The return value is true if successful, else, it is false. */
static int dplock(int fd, int ex){
  struct flock lock;
  assert(fd >= 0);
  memset(&lock, 0, sizeof(struct flock));
  lock.l_type = ex ? F_WRLCK : F_RDLCK;
  lock.l_whence = SEEK_SET;
  lock.l_start = 0;
  lock.l_len = 0;
  lock.l_pid = 0;
  while(fcntl(fd, F_SETLKW, &lock) == -1){
    if(errno != EINTR){
      dpecode = DP_ELOCK;
      return FALSE;
    }
  }
  return TRUE;
}


/* Write into a file.
   `fd' specifies a file descriptor.
   `buf' specifies a buffer to write.
   `size' specifies the size of the buffer.
   The return value is the size of the written buffer, or, -1 on failure. */
static int dpwrite(int fd, const void *buf, int size){
  const char *lbuf;
  int rv, wb;
  assert(fd >= 0 && buf && size >= 0);
  lbuf = buf;
  rv = 0;
  do {
    wb = write(fd, lbuf, size);
    switch(wb){
    case -1: if(errno != EINTR) return -1;
    case 0: break;
    default:
      lbuf += wb;
      size -= wb;
      rv += wb;
      break;
    }
  } while(size > 0);
  return rv;
}


/* Write into a file at an offset.
   `fd' specifies a file descriptor.
   `off' specifies an offset of the file.
   `buf' specifies a buffer to write.
   `size' specifies the size of the buffer.
   The return value is true if successful, else, it is false. */
static int dpseekwrite(int fd, int off, const void *buf, int size){
  char *lbuf;
  assert(fd >= 0 && buf && size >= 0);
  if(size < 1) return TRUE;
  lbuf = (char *)buf;
  if(off < 0){
    if(lseek(fd, 0, SEEK_END) == -1){
      dpecode = DP_ESEEK;
      return FALSE;
    }
  } else {
    if(lseek(fd, off, SEEK_SET) != off){
      dpecode = DP_ESEEK;
      return FALSE;
    }
  }
  if(dpwrite(fd, lbuf, size) != size){
    dpecode = DP_EWRITE;
    return FALSE;
  }
  return TRUE;
}


/* Write an integer into a file at an offset.
   `fd' specifies a file descriptor.
   `off' specifies an offset of the file.
   `num' specifies an integer.
   The return value is true if successful, else, it is false. */
static int dpseekwritenum(int fd, int off, int num){
  assert(fd >= 0);
  return dpseekwrite(fd, off, &num, sizeof(int));
}


/* Read from a file and store the data into a buffer.
   `fd' specifies a file descriptor.
   `buffer' specifies a buffer to store into.
   `size' specifies the size to read with.
   The return value is the size read with, or, -1 on failure. */
static int dpread(int fd, void *buf, int size){
  char *lbuf;
  int i, bs;
  assert(fd >= 0 && buf && size >= 0);
  lbuf = buf;
  for(i = 0; i < size && (bs = read(fd, lbuf + i, size - i)) != 0; i += bs){
    if(bs == -1 && errno != EINTR) return -1;
  }
  return i;
}


/* Read from a file at an offset and store the data into a buffer.
   `fd' specifies a file descriptor.
   `off' specifies an offset of the file.
   `buffer' specifies a buffer to store into.
   `size' specifies the size to read with.
   The return value is true if successful, else, it is false. */
static int dpseekread(int fd, int off, void *buf, int size){
  char *lbuf;
  assert(fd >= 0 && off >= 0 && buf && size >= 0);
  lbuf = (char *)buf;
  if(lseek(fd, off, SEEK_SET) != off){
    dpecode = DP_ESEEK;
    return FALSE;
  }
  if(dpread(fd, lbuf, size) != size){
    dpecode = DP_EREAD;
    return FALSE;
  }
  return TRUE;
}


/* Copy data between files.
   `destfd' specifies a file descriptor of a destination file.
   `destoff' specifies an offset of the destination file.
   `srcfd' specifies a file descriptor of a source file.
   `srcoff' specifies an offset of the source file.
   The return value is the size copied with, or, -1 on failure. */
static int dpfcopy(int destfd, int destoff, int srcfd, int srcoff){
  char iobuf[DP_IOBUFSIZ];
  int sum, iosiz;
  if(lseek(srcfd, srcoff, SEEK_SET) == -1 || lseek(destfd, destoff, SEEK_SET) == -1){
    dpecode = DP_ESEEK;
    return -1;
  }
  sum = 0;
  while((iosiz = dpread(srcfd, iobuf, DP_IOBUFSIZ)) > 0){
    if(dpwrite(destfd, iobuf, iosiz) == -1){
      dpecode = DP_EWRITE;
      return -1;
    }
    sum += iosiz;
  }
  if(iosiz < 0){
    dpecode = DP_EREAD;
    return -1;
  }
  return sum;
}


/* Get the first hash value.
   `kbuf' specifies the pointer to the region of a key.
   `ksiz' specifies the size of the key.
   The return value is 31 bit hash value of the key. */
static int dpfirsthash(const char *kbuf, int ksiz){
  unsigned int sum;
  int i;
  assert(kbuf && ksiz >= 0);
  if(ksiz == sizeof(int)){
    memcpy(&sum, kbuf, sizeof(int));
  } else {
    sum = 751;
  }
  for(i = 0; i < ksiz; i++){
    sum = sum * 31 + ((const unsigned char *)kbuf)[i];
  }
  return (sum * 87767623) & 0x7FFFFFFF;
}


/* Get the second hash value.
   `kbuf' specifies the pointer to the region of a key.
   `ksiz' specifies the size of the key.
   The return value is 31 bit hash value of the key. */
static int dpsecondhash(const char *kbuf, int ksiz){
  unsigned int sum;
  int i;
  assert(kbuf && ksiz >= 0);
  sum = 19780211;
  for(i = ksiz - 1; i >= 0; i--){
    sum = sum * 37 + ((const unsigned char *)kbuf)[i];
  }
  return (sum * 43321879) & 0x7FFFFFFF;
}


/* Get the third hash value.
   `kbuf' specifies the pointer to the region of a key.
   `ksiz' specifies the size of the key.
   The return value is 31 bit hash value of the key. */
static int dpthirdhash(const char *kbuf, int ksiz){
  unsigned int sum;
  int i;
  assert(kbuf && ksiz >= 0);
  sum = 774831917;
  for(i = ksiz - 1; i >= 0; i--){
    sum = sum * 29 + ((const unsigned char *)kbuf)[i];
  }
  return (sum * 5157883) & 0x7FFFFFFF;
}


/* Get a natural prime number not less than a number.
   `num' specified a natural number.
   The return value is a prime number not less than the specified number. */
static int dpgetprime(int num){
  int primes[] = {
    1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 43, 47, 53, 59, 61, 71, 79, 83,
    89, 103, 109, 113, 127, 139, 157, 173, 191, 199, 223, 239, 251, 283, 317, 349,
    383, 409, 443, 479, 509, 571, 631, 701, 761, 829, 887, 953, 1021, 1151, 1279,
    1399, 1531, 1663, 1789, 1913, 2039, 2297, 2557, 2803, 3067, 3323, 3583, 3833,
    4093, 4603, 5119, 5623, 6143, 6653, 7159, 7673, 8191, 9209, 10223, 11261,
    12281, 13309, 14327, 15359, 16381, 18427, 20479, 22511, 24571, 26597, 28669,
    30713, 32749, 36857, 40949, 45053, 49139, 53239, 57331, 61417, 65521, 73727,
    81919, 90107, 98299, 106487, 114679, 122869, 131071, 147451, 163819, 180221,
    196597, 212987, 229373, 245759, 262139, 294911, 327673, 360439, 393209, 425977,
    458747, 491503, 524287, 589811, 655357, 720887, 786431, 851957, 917503, 982981,
    1048573, 1179641, 1310719, 1441771, 1572853, 1703903, 1835003, 1966079,
    2097143, 2359267, 2621431, 2883577, 3145721, 3407857, 3670013, 3932153,
    4194301, 4718579, 5242877, 5767129, 6291449, 6815741, 7340009, 7864301,
    8388593, 9437179, 10485751, 11534329, 12582893, 13631477, 14680063, 15728611,
    16777213, 18874367, 20971507, 23068667, 25165813, 27262931, 29360087, 31457269,
    33554393, 37748717, 41943023, 46137319, 50331599, 54525917, 58720253, 62914549,
    67108859, 75497467, 83886053, 92274671, 100663291, 109051903, 117440509,
    125829103, 134217689, 150994939, 167772107, 184549373, 201326557, 218103799,
    234881011, 251658227, 268435399, 301989881, 335544301, 369098707, 402653171,
    436207613, 469762043, 503316469, 536870909, 603979769, 671088637, 738197503,
    805306357, 872415211, 939524087, 1006632947, 1073741789, 1207959503,
    1342177237, 1476394991, 1610612711, 1744830457, 1879048183, 2013265907, -1
  };
  int i;
  assert(num > 0);
  for(i = 0; primes[i] > 0; i++){
    if(num <= primes[i]) return primes[i];
  }
  return primes[i-1];
}


/* Get the padding size of a record.
   `vsiz' specifies the size of the value of a record.
   The return value is the padding size of a record. */
static int dppadsize(DEPOT *depot, int vsiz){
  int mod;
  assert(depot && vsiz >= 0);
  if(depot->align > 0){
    mod = vsiz % depot->align;
    return mod == 0 ? 0 : depot->align - mod;
  } else if(depot->align < 0){
    return (int)(vsiz * (2.0 / (1 << -(depot->align))));
  }
  return 0;
}


/* Get the size of a record in a database file.
   `head' specifies the header of  a record.
   The return value is the size of a record in a database file. */
static int dprecsize(int *head){
  assert(head);
  return DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + head[DP_RHIVSIZ] + head[DP_RHIPSIZ];
}


/* Read the header of a record.
   `depot' specifies a database handle.
   `off' specifies an offset of the database file.
   `head' specifies a buffer for the header.
   The return value is true if successful, else, it is false. */
static int dprechead(DEPOT *depot, int off, int *head){
  assert(depot && off >= 0 && head);
  if(off > depot->fsiz){
    dpecode = DP_EBROKEN;
    return FALSE;
  }
  if(!dpseekread(depot->fd, off, head, DP_RHNUM * sizeof(int))) return FALSE;
  if(head[DP_RHIKSIZ] < 0 || head[DP_RHIVSIZ] < 0 || DP_RHIPSIZ < 0 ||
     head[DP_RHILEFT] < 0 || head[DP_RHIRIGHT] < 0){
    dpecode = DP_EBROKEN;
    return FALSE;
  }
  return TRUE;
}


/* Read the entitiy of the key of a record.
   `depot' specifies a database handle.
   `off' specifies an offset of the database file.
   `head' specifies the header of a record.
   The return value is a key data whose region is allocated by `malloc', or NULL on failure. */
static char *dpreckey(DEPOT *depot, int off, int *head){
  char *kbuf;
  int ksiz;
  assert(depot && off >= 0);
  ksiz = head[DP_RHIKSIZ];
  if(!(kbuf = malloc(ksiz + 1))){
    dpecode = DP_EALLOC;
    return NULL;
  }
  if(!dpseekread(depot->fd, off + DP_RHNUM * sizeof(int), kbuf, ksiz)){
    free(kbuf);
    return NULL;
  }
  kbuf[ksiz] = '\0';
  return kbuf;
}


/* Read the entitiy of the value of a record.
   `depot' specifies a database handle.
   `off' specifies an offset of the database file.
   `head' specifies the header of a record.
   `start' specifies the offset address of the beginning of the region of the value to be read.
   `max' specifies the max size to be read.  If it is negative, the size to read is unlimited.
   The return value is a value data whose region is allocated by `malloc', or NULL on failure. */
static char *dprecval(DEPOT *depot, int off, int *head, int start, int max){
  char *vbuf;
  int vsiz;
  assert(depot && off >= 0 && start >= 0);
  head[DP_RHIVSIZ] -= start;
  if(max < 0){
    vsiz = head[DP_RHIVSIZ];
  } else {
    vsiz = max < head[DP_RHIVSIZ] ? max : head[DP_RHIVSIZ];
  }
  if(!(vbuf = malloc(vsiz + 1))){
    dpecode = DP_EALLOC;
    return NULL;
  }
  if(!dpseekread(depot->fd, off + DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + start, vbuf, vsiz)){
    free(vbuf);
    return NULL;
  }
  vbuf[vsiz] = '\0';
  return vbuf;
}


/* Compare two keys.
   `abuf' specifies the pointer to the region of the former.
   `asiz' specifies the size of the region.
   `bbuf' specifies the pointer to the region of the latter.
   `bsiz' specifies the size of the region.
   The return value is 0 if two equals, positive if the formar is big, else, negative. */
static int dpkeycmp(const char *abuf, int asiz, const char *bbuf, int bsiz){
  assert(abuf && asiz >= 0 && bbuf && bsiz >= 0);
  if(asiz > bsiz) return 1;
  if(asiz < bsiz) return -1;
  return memcmp(abuf, bbuf, asiz);
}


/* Search a record.
   `depot' specifies a database handle.
   `kbuf' specifies the pointer to the region of a key.
   `ksiz' specifies the size of the region.
   `hash' specifies the second hash value of the key.
   `bip' specifies the pointer to the region to assign the index of the corresponding record.
   `offp' specifies the pointer to the region to assign the last visited node in the hash chain,
   or, -1 if the hash chain is empty.
   `entp' specifies the offset of the last used joint, or, -1 if the hash chain is empty.
   `head' specifies the pointer to the region to store the header of the last visited record in.
   `delhit' specifies whether a deleted record corresponds or not.
   The return value is 0 if successful, 1 if there is no corresponding record, -1 on error. */
static int dprecsearch(DEPOT *depot, const char *kbuf, int ksiz, int hash,
                       int *bip, int *offp, int *entp, int *head, int delhit){
  int off, entoff, thash, kcmp;
  char *tkey;
  assert(depot && kbuf && ksiz >= 0 && hash >= 0 && bip && offp && entp && head);
  *bip = dpfirsthash(kbuf, ksiz) % depot->bnum;
  off = depot->buckets[*bip];
  *offp = -1;
  *entp = -1;
  entoff = -1;
  while(off != 0){
    if(!dprechead(depot, off, head)) return -1;
    thash = head[DP_RHIHASH];
    if(hash > thash){
      entoff = off + DP_RHILEFT * sizeof(int);
      off = head[DP_RHILEFT];
    } else if(hash < thash){
      entoff = off + DP_RHIRIGHT * sizeof(int);
      off = head[DP_RHIRIGHT];
    } else {
      if(!(tkey = dpreckey(depot, off, head))) return -1;
      kcmp = dpkeycmp(kbuf, ksiz, tkey, head[DP_RHIKSIZ]);
      free(tkey);
      if(kcmp > 0){
        entoff = off + DP_RHILEFT * sizeof(int);
        off = head[DP_RHILEFT];
      } else if(kcmp < 0){
        entoff = off + DP_RHIRIGHT * sizeof(int);
        off = head[DP_RHIRIGHT];
      } else {
        if(!delhit && (head[DP_RHIFLAGS] & DP_RECFDEL)){
          entoff = off + DP_RHILEFT * sizeof(int);
          off = head[DP_RHILEFT];
        } else {
          *offp = off;
          *entp = entoff;
          return 0;
        }
      }
    }
  }
  *offp = off;
  *entp = entoff;
  return 1;
}


/* Overwrite a record.
   `depot' specifies a database handle.
   `off' specifies the offset of the database file.
   `rsiz' specifies the size of the existing record.
   `kbuf' specifies the pointer to the region of a key.
   `ksiz' specifies the size of the region.
   `vbuf' specifies the pointer to the region of a value.
   `vsiz' specifies the size of the region.
   `hash' specifies the second hash value of the key.
   `left' specifies the offset of the left child.
   `right' specifies the offset of the right child.
   The return value is true if successful, or, false on failure. */
static int dprecrewrite(DEPOT *depot, int off, int rsiz, const char *kbuf, int ksiz,
                        const char *vbuf, int vsiz, int hash, int left, int right){
  int head[DP_RHNUM], psiz, hoff, koff, voff, c;
  assert(depot && off >= 1 && rsiz > 0 && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
  psiz = rsiz - sizeof(head) - ksiz - vsiz;
  head[DP_RHIFLAGS] = 0;
  head[DP_RHIHASH] = hash;
  head[DP_RHIKSIZ] = ksiz;
  head[DP_RHIVSIZ] = vsiz;
  head[DP_RHIPSIZ] = psiz;
  head[DP_RHILEFT] = left;
  head[DP_RHIRIGHT] = right;
  hoff = off;
  koff = hoff + sizeof(head);
  voff = koff + ksiz;
  c = 0;
  if(!dpseekwrite(depot->fd, hoff, head, sizeof(head)) ||
     !dpseekwrite(depot->fd, koff, kbuf, ksiz) ||
     !dpseekwrite(depot->fd, voff, vbuf, vsiz) ||
     (psiz > 0 && !dpseekwrite(depot->fd, voff + vsiz + psiz - 1, &c, 1))) return FALSE;
  return TRUE;
}


/* Write a record at the end of a database file.
   `depot' specifies a database handle.
   `kbuf' specifies the pointer to the region of a key.
   `ksiz' specifies the size of the region.
   `vbuf' specifies the pointer to the region of a value.
   `vsiz' specifies the size of the region.
   `hash' specifies the second hash value of the key.
   `left' specifies the offset of the left child.
   `right' specifies the offset of the right child.
   The return value is the offset of the record, or, -1 on failure. */
static int dprecappend(DEPOT *depot, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
                       int hash, int left, int right){
  int head[DP_RHNUM], psiz, off, hoff, koff, voff, c;
  assert(depot && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
  psiz = dppadsize(depot, vsiz);
  head[DP_RHIFLAGS] = 0;
  head[DP_RHIHASH] = hash;
  head[DP_RHIKSIZ] = ksiz;
  head[DP_RHIVSIZ] = vsiz;
  head[DP_RHIPSIZ] = psiz;
  head[DP_RHILEFT] = left;
  head[DP_RHIRIGHT] = right;
  off = depot->fsiz;
  hoff = off;
  koff = hoff + sizeof(head);
  voff = koff + ksiz;
  c = 0;
  if(!dpseekwrite(depot->fd, hoff, head, sizeof(head)) ||
     !dpseekwrite(depot->fd, koff, kbuf, ksiz) ||
     !dpseekwrite(depot->fd, voff, vbuf, vsiz) ||
     (psiz > 0 && !dpseekwrite(depot->fd, voff + vsiz + psiz - 1, &c, 1))) return -1;
  depot->fsiz += sizeof(head) + vsiz + ksiz + psiz;
  return off;
}


/* Overwrite the value of a record.
   `depot' specifies a database handle.
   `off' specifies the offset of the database file.
   `head' specifies the header of the record.
   `vbuf' specifies the pointer to the region of a value.
   `vsiz' specifies the size of the region.
   `cat' specifies whether it is concatenate mode or not.
   The return value is true if successful, or, false on failure. */
static int dprecover(DEPOT *depot, int off, int *head, const char *vbuf, int vsiz, int cat){
  int hsiz, hoff, voff;
  assert(depot && off >= 0 && head && vbuf && vsiz >= 0);
  if(depot->mroff == off) depot->mroff = -1;
  if(cat){
    head[DP_RHIFLAGS] = 0;
    head[DP_RHIPSIZ] -= vsiz;
    head[DP_RHIVSIZ] += vsiz;
    hsiz = DP_RHNUM * sizeof(int);
    hoff = off;
    voff = hoff + DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + head[DP_RHIVSIZ] - vsiz;
  } else {
    head[DP_RHIFLAGS] = 0;
    head[DP_RHIPSIZ] += head[DP_RHIVSIZ] - vsiz;
    head[DP_RHIVSIZ] = vsiz;
    hsiz = DP_RHNUM * sizeof(int);
    hoff = off;
    voff = hoff + DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ];
  }
  if(!dpseekwrite(depot->fd, hoff, head, hsiz) ||
     !dpseekwrite(depot->fd, voff, vbuf, vsiz)) return FALSE;
  return TRUE;
}


/* Delete a record.
   `depot' specifies a database handle.
   `off' specifies the offset of the database file.
   `head' specifies the header of the record.
   `reusable' specifies whether the region is reusable or not.
   The return value is true if successful, or, false on failure. */
static int dprecdelete(DEPOT *depot, int off, int *head, int reusable){
  assert(depot && off >= 0 && head);
  if(reusable){
    depot->mroff = off;
    depot->mrsiz = dprecsize(head);
  }
  return dpseekwritenum(depot->fd, off + DP_RHIFLAGS * sizeof(int),
                        DP_RECFDEL | (reusable ? DP_RECFREUSE : 0));
}



/* END OF FILE */
