/*******************************************
 * Copyright(c) 2000-2003 Mikio Hirabayashi
 *******************************************/


#include "depot.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <assert.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>

#undef TRUE
#define TRUE           1
#undef FALSE
#define FALSE          0

#define DP_FILEMODE    00644
#define DP_MAGICNUM    "[depot]\n\f"
#define DP_HEADSIZ     48
#define DP_FSIZOFF     16
#define DP_BNUMOFF     24
#define DP_RNUMOFF     32
#define DP_NUMBUFSIZ   32
#define DP_DEFBNUM     8191
#define DP_COALMAX     3
#define DP_ALIGNMOD    8
#define DP_OPTFSUF     ".opttmp"
#define DP_OPTBLOAD    0.25
#define DP_OPTRUNIT    256
#define DP_IOBUFSIZ    8192


enum {
  DP_RHIFLAGS,
  DP_RHIHASH,
  DP_RHIKSIZ,
  DP_RHIVSIZ,
  DP_RHIPSIZ,
  DP_RHILEFT,
  DP_RHIRIGHT,
  DP_RHNUM
};

enum {
  DP_RECFDEL = 1 << 0,
  DP_RECFREUSE = 1 << 1
};


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 dpprintf(int fd, const char *format, ...);
static void dpdbgoutdepot(const char *fname, DEPOT *depot);
static void dpdbgouthead(const char *fname, int *head);
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
 *************************************************************************************************/


const char *dpversion = DP_VERSION;


int dpdbgfd = -1;


int dpecode = DP_ENOERR;


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)";
}


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_OPENFLAGS, DP_FILEMODE)) == -1){
    dpecode = DP_EOPEN;
    return NULL;
  }
  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);
    memcpy(hbuf, DP_MAGICNUM, strlen(DP_MAGICNUM));
    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(memcmp(hbuf, DP_MAGICNUM, strlen(DP_MAGICNUM)) != 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->bucket = (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;
  dpdbgoutdepot("dpopen", depot);
  return depot;
}


int dpclose(DEPOT *depot){
  int err;
  assert(depot);
  err = FALSE;
  dpdbgoutdepot("dpclose", depot);
  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;
}


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->bucket[bi] = newoff;
    }
  }
  return TRUE;
}


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;
}


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;
}


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];
}


int dpiterinit(DEPOT *depot){
  assert(depot);
  if(depot->fatal){
    dpecode = DP_EFATAL;
    return FALSE;
  }
  depot->ioff = 0;
  return TRUE;
}


char *dpiternext(DEPOT *depot, int *sp){
  int off, head[DP_RHNUM];
  char *val;
  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;
    }
    dpdbgouthead("dpiternext", head);
    if(head[DP_RHIFLAGS] & DP_RECFDEL){
      off += dprecsize(head);
    } else {
      if(!(val = dpreckey(depot, off, head))){
        depot->fatal = TRUE;
        return NULL;
      }
      depot->ioff = off + dprecsize(head);
      if(sp) *sp = head[DP_RHIKSIZ];
      return val;
    }
  }
  dpecode = DP_ENOITEM;
  return NULL;
}


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;
}


int dpsetalign(DEPOT *depot, int align){
  assert(depot && align >= 0);
  if(depot->fatal){
    dpecode = DP_EFATAL;
    return FALSE;
  }
  if(!depot->wmode){
    dpecode = DP_EMODE;
    return FALSE;
  }
  depot->align = align;
  return TRUE;
}


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_OPTFSUF) + 1))){
    dpecode = DP_EALLOC;
    depot->fatal = FALSE;
    return FALSE;
  }
  sprintf(name, "%s%s", depot->name, DP_OPTFSUF);
  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;
    }
    dpdbgouthead("dpoptimize", head);
    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_DOVER)) 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_DOVER)) 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->bucket = (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;
}


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;
}


int dpfsiz(DEPOT *depot){
  assert(depot);
  if(depot->fatal){
    dpecode = DP_EFATAL;
    return -1;
  }
  return depot->fsiz;
}


int dpbnum(DEPOT *depot){
  assert(depot);
  if(depot->fatal){
    dpecode = DP_EFATAL;
    return -1;
  }
  return depot->bnum;
}


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->bucket[i]) hits++;
  }
  return hits;
}


int dprnum(DEPOT *depot){
  assert(depot);
  if(depot->fatal){
    dpecode = DP_EFATAL;
    return -1;
  }
  return depot->rnum;
}


int dpwritable(DEPOT *depot){
  assert(depot);
  return depot->wmode;
}


int dpfatalerror(DEPOT *depot){
  assert(depot);
  return depot->fatal;
}


int dpinode(DEPOT *depot){
  assert(depot);
  return depot->inode;
}


int dpfdesc(DEPOT *depot){
  assert(depot);
  return depot->fd;
}


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;
}


int dpinnerhash(const char *kbuf, int ksiz){
  assert(kbuf);
  if(ksiz < 0) ksiz = strlen(kbuf);
  return dpfirsthash(kbuf, ksiz);
}


int dpouterhash(const char *kbuf, int ksiz){
  assert(kbuf);
  if(ksiz < 0) ksiz = strlen(kbuf);
  return dpthirdhash(kbuf, ksiz);
}


int dpprimenum(int num){
  assert(num > 0);
  return dpgetprime(num);
}



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


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;
}


static int dplock(int fd, int ex){
  struct flock l;
  assert(fd >= 0);
  memset(&l, 0, sizeof(struct flock));
  l.l_type = ex ? F_WRLCK : F_RDLCK;
  l.l_whence = SEEK_SET;
  l.l_start = 0;
  l.l_len = 0;
  l.l_pid = 0;
  while(fcntl(fd, F_SETLKW, &l) == -1){
    if(errno != EINTR){
      dpecode = DP_ELOCK;
      return FALSE;
    }
  }
  return TRUE;
}


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;
}


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;
}


static int dpseekwritenum(int fd, int off, int num){
  assert(fd >= 0);
  return dpseekwrite(fd, off, &num, sizeof(int));
}


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;
}


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;
}


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;
}


static int dpprintf(int fd, const char *format, ...){
  va_list ap;
  char numstr[DP_NUMBUFSIZ], *s;
  int len, wb;
  assert(fd >= 0 && format);
  va_start(ap, format);
  len = 0;
  while(*format != '\0'){
    if(*format == '%'){
      format++;
      switch(*format){
      case 's':
        s = va_arg(ap, char *);
        wb = dpwrite(fd, s, strlen(s));
        break;
      case 'd':
        sprintf(numstr, "%d", va_arg(ap, int));
        wb = dpwrite(fd, numstr, strlen(numstr));
        break;
      case 'x':
        sprintf(numstr, "%x", va_arg(ap, int));
        wb = dpwrite(fd, numstr, strlen(numstr));
        break;
      case '%':
        wb = dpwrite(fd, "%", 1);
        break;
      case '\0':
        va_end(ap);
        return -1;
      default:
        wb = 0;
        break;
      }
      if(wb == -1){
        va_end(ap);
        return -1;
      }
      len += wb;
    } else {
      if(dpwrite(fd, format, 1) == -1){
        va_end(ap);
        return -1;
      }
      len++;
    }
    format++;
  }
  va_end(ap);
  return len;
}


static void dpdbgoutdepot(const char *fname, DEPOT *depot){
  assert(fname && depot);
  if(dpdbgfd < 0) return;
  dpprintf(dpdbgfd, "%s: depot: name=%s wmode=%d fd=%d inode=%d fsiz=%d map=0x%x msiz=%d "
           "bucket=0x%x bnum=%d rnum=%d fatal=%d ioff=%d mroff=%d mrsiz=%d align=%d\n",
           fname, depot->name, depot->wmode, depot->fd, depot->inode, depot->fsiz, depot->map,
           depot->msiz, depot->bucket, depot->bnum, depot->rnum, depot->fatal,
           depot->ioff, depot->mroff, depot->mrsiz, depot->align);
}


static void dpdbgouthead(const char *fname, int *head){
  assert(fname && head);
  if(dpdbgfd < 0) return;
  dpprintf(dpdbgfd, "%s: head: flags=%d hash=%d ksiz=%d vsiz=%d psiz=%d "
           "left=%d right=%d [siz=%d]\n",
           fname, head[DP_RHIFLAGS], head[DP_RHIHASH], head[DP_RHIKSIZ], head[DP_RHIVSIZ],
           head[DP_RHIPSIZ], head[DP_RHILEFT], head[DP_RHIRIGHT], dprecsize(head));
}


static int dpfirsthash(const char *kbuf, int ksiz){
  const unsigned char *p;
  unsigned int sum;
  int i;
  assert(kbuf && ksiz >= 0);
  p = (const unsigned char *)kbuf;
  sum = 751;
  for(i = 0; i < ksiz; i++){
    sum = sum * 31 + p[i];
  }
  return (sum * 87767623) & 0x7FFFFFFF;
}


static int dpsecondhash(const char *kbuf, int ksiz){
  const unsigned char *p;
  unsigned int sum;
  int i;
  assert(kbuf && ksiz >= 0);
  p = (const unsigned char *)kbuf;
  sum = 19780211;
  for(i = ksiz - 1; i >= 0; i--){
    sum = sum * 37 + p[i];
  }
  return (sum * 43321879) & 0x7FFFFFFF;
}


static int dpthirdhash(const char *kbuf, int ksiz){
  const unsigned char *p;
  unsigned int sum;
  int i;
  assert(kbuf && ksiz >= 0);
  p = (const unsigned char *)kbuf;
  sum = 774831917;
  for(i = ksiz - 1; i >= 0; i--){
    sum = sum * 29 + p[i];
  }
  return (sum * 5157883) & 0x7FFFFFFF;
}


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];
}


static int dppadsize(DEPOT *depot, int vsiz){
  int aunit, abase;
  assert(depot && vsiz >= 0);
  if(depot->align < 1) return 0;
  aunit = depot->align * DP_ALIGNMOD;
  abase = vsiz / aunit + 1;
  return ((abase * aunit) - vsiz) % (depot->align * abase);
}


static int dprecsize(int *head){
  assert(head);
  return DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + head[DP_RHIVSIZ] + head[DP_RHIPSIZ];
}


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;
}


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;
}


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;
}


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);
}


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->bucket[*bip];
  *offp = -1;
  *entp = -1;
  entoff = -1;
  while(off != 0){
    if(!dprechead(depot, off, head)) return -1;
    dpdbgouthead("dprecsearch", head);
    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;
}


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;
  dpdbgouthead("dprecrewrite", head);
  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;
}


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;
  dpdbgouthead("dprecappend", head);
  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;
}


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];
  }
  dpdbgouthead("dprecover", head);
  if(!dpseekwrite(depot->fd, hoff, head, hsiz) ||
     !dpseekwrite(depot->fd, voff, vbuf, vsiz)) return FALSE;
  return TRUE;
}


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 */
