From: teodor Date: Mon, 4 Oct 2004 17:57:14 +0000 (+0000) Subject: Add flat file manager (need testing) X-Git-Url: http://sigaev.ru/git/gitweb.cgi?a=commitdiff_plain;h=c6456fa0302c33d9619ed992747803855011b1b9;p=tedtools.git Add flat file manager (need testing) --- diff --git a/Makefile b/Makefile index c10b9ed..e58d726 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ LIB=-L. -ltedtools -lm OBJS=tlog.o tmalloc.o tools.o prs_hmap.o sfxstr.o \ regis.o prs_inf.o shmem.o tcp.o udp.o connpool.o \ - psort.o + psort.o flatdb.o PROGS=sfxtest hextest inftest kilter psortex .SUFFIXES: .o.c diff --git a/flatdb.c b/flatdb.c new file mode 100644 index 0000000..eebbeee --- /dev/null +++ b/flatdb.c @@ -0,0 +1,266 @@ +#include +#include +#include +#include +#include +#include + +#include "tlog.h" +#include "tmalloc.h" + +#include "flatdb.h" + +static FDBFreeSpace* +findFreeSpace(FDB *db, size_t length) { + FDBFreeSpace *ptr = db->space; + + while(ptr && ptr - db->space < db->listcur) { + if ( ptr->length >= length ) + return ptr; + ptr++; + } + + return NULL; +} + +static int +cmpFS(const void* a, const void* b) { + if ( ((FDBFreeSpace*)a)->offset == ((FDBFreeSpace*)b)->offset ) + return 0; + return ( ((FDBFreeSpace*)a)->offset > ((FDBFreeSpace*)b)->offset ) ? 1 : -1; +} + +void +FDBVacuumFreeSpace(FDB *db) { + FDBFreeSpace *ptr=db->space+1, *ok=db->space; + if ( db->listcur < 2 ) + return; + + qsort( db->space, db->listcur, sizeof(FDBFreeSpace), cmpFS); + + while( ptr - db->space < db->listcur ) { + tassert( ok->offset + ok->length <= ptr->offset ); + if ( ok->offset + ok->length == ptr->offset || ptr->length==0 ) { + ok->length += ptr->length; + } else { + ok++; + if ( ok != ptr-1 ) + memcpy(ok, ptr, sizeof(FDBFreeSpace)); + } + ptr++; + } + + db->listcur = ok - db->space + 1; +} + +int +FDBOpen(FDB *db, char *file, int readonly) { + FDBHeader header; + int rc; + + memset(db, 0, sizeof(FDB)); + + if ( readonly ) { + db->readonly=1; + db->fd = open(file, O_RDONLY | O_SHLOCK); + } else { + db->fd = open(file, O_CREAT | O_RDWR | O_EXLOCK, 0666); + } + + if ( db->fd < 0 ) { + memset(db, 0, sizeof(FDB)); + tlog(TL_CRIT,"FDBOpen: open failed: %s", strerror(errno)); + return FDB_ERROR; + } + + rc = read(db->fd, &header, sizeof(FDBHeader)); + + if ( rc<0 ) { + close(db->fd); + tlog(TL_CRIT,"FDBOpen: read failed: %s", strerror(errno)); + return FDB_ERROR; + } else if ( rc==0 ) { + memset(&header, 0, sizeof(FDBHeader)); + } else if ( rc != sizeof(FDBHeader) ) { + close(db->fd); + tlog(TL_CRIT,"FDBOpen: header fault: %d bytes only", rc); + return FDB_ERROR; + } else if ( header.isopened ) { + close(db->fd); + tlog(TL_CRIT,"FDBOpen: file wasn't closed correctly"); + return FDB_ERROR; + } + + + if ( !db->readonly ) { + if ( header.freespace ) { + db->listlen = db->listcur = (header.lenfreespace / sizeof(FDBFreeSpace)); + db->space = (FDBFreeSpace*)tmalloc( header.lenfreespace + sizeof(FDBFreeSpace) ); + + if ( lseek(db->fd, header.freespace, SEEK_SET)!=header.freespace || + read( db->fd, db->space, header.lenfreespace ) != header.lenfreespace ) { + close(db->fd); + tlog(TL_CRIT,"FDBOpen: free space read failed: %s", strerror(errno)); + return FDB_ERROR; + } + } else { + db->listlen = 8; + db->space = (FDBFreeSpace*)tmalloc( db->listlen*sizeof(FDBFreeSpace) ); + } + + header.freespace = 0; + header.lenfreespace = 0; + header.isopened = 1; + + if ( lseek(db->fd, 0, SEEK_SET)!=0 || write(db->fd, &header, sizeof(FDBHeader)) != sizeof(FDBHeader) ) { + close(db->fd); + if ( db->space ) tfree( db->space ); + tlog(TL_CRIT,"FDBOpen: can't modify header: %s", strerror(errno)); + return FDB_ERROR; + } + } + + return FDB_OK; +} + + +int +FDBClose(FDB *db) { + if ( !db->readonly) { + FDBHeader header; + + memset(&header,0,sizeof(FDBHeader)); + + if ( db->listcur ) { + FDBFreeSpace *ptr; + + vacuumFreeSpace(db); + header.lenfreespace = sizeof(FDBFreeSpace)*db->listcur; + ptr = findFreeSpace( db, header.lenfreespace ); + + if ( ptr ) { + header.freespace = ptr->offset; + if ( lseek(db->fd, ptr->offset, SEEK_SET) != ptr->offset ) + tlog(TL_CRIT|TL_EXIT,"FDBClose: lseek failed: %s", strerror(errno)); + } else { + if ( (header.freespace = lseek(db->fd, 0, SEEK_END)) < 0 ) + tlog(TL_CRIT|TL_EXIT,"FDBClose: lseek failed: %s", strerror(errno)); + } + + if ( write(db->fd, db->space, header.lenfreespace) != header.lenfreespace ) + tlog(TL_CRIT|TL_EXIT,"FDBClose: write failed: %s", strerror(errno)); + } + + header.isopened=0; + + if ( lseek(db->fd,0,SEEK_SET)!=0 || write(db->fd, &header, sizeof(FDBHeader)) != sizeof(FDBHeader) ) + tlog(TL_CRIT|TL_EXIT,"FDBClose: header write failed: %s", strerror(errno)); + } + + close(db->fd); + + if ( db->space ) + tfree( db->space ); + + return FDB_OK; +} + +static int +readLen(FDB *db, off_t offset, size_t *size) { + if ( lseek(db->fd,offset,SEEK_SET)!=offset) + return FDB_ERROR; + + if ( read(db->fd,size,sizeof(size_t)) != sizeof(size_t) ) + return FDB_ERROR; + + return FDB_OK; +} + +int +FDBDelete(FDB *db, off_t offset, size_t length) { + if ( db->readonly ) + return FDB_ERROR; + + if ( length==0 ) + if ( readLen(db, offset, &length) != FDB_OK ) + return FDB_ERROR; + + if ( db->listcur >= db->listlen ) { + db->listlen *= 2; + db->space = (FDBFreeSpace*) trealloc( db->space, db->listlen * sizeof(FDBFreeSpace) ); + } + + db->space[ db->listcur ].offset=offset; + db->space[ db->listcur ].length=length; + + db->listcur++; + + return FDB_OK; +} + + +int +FDBGet(FDB *db, off_t offset, size_t length, FDBRecord **record) { + size_t rc; + + *record=NULL; + + if ( db->readonly ) + return FDB_ERROR; + + if ( length==0 ) + if ( readLen(db, offset, &length) != FDB_OK ) + return FDB_ERROR; + + *record = (FDBRecord*)tmalloc( length ); + + if ( lseek(db->fd,offset,SEEK_SET)!=offset) + return FDB_ERROR; + + if ( (rc=read(db->fd,*record,length)) != length ) { + (*record)->length = rc; + tlog(TL_CRIT,"FDBGet: read (%d bytes) less than needed (%d bytes): %s", rc, length, strerror(errno)); + return FDB_INCORRECT; + } + + if ( (*record)->length != length ) { + tlog(TL_ALARM, "FDBGet: wrong length in opts: %d bytes and %d bytes really", length, (*record)->length); + if ( (*record)->length > length ) { + rc = (*record)->length; + tfree( *record ); + return FDBGet(db, offset, rc, record); + } + } + + return FDB_OK; +} + + +int +FDBPut(FDB *db, FDBRecord *record, off_t *offset ) { + FDBFreeSpace *ptr; + + if ( db->readonly ) + return FDB_ERROR; + + ptr = findFreeSpace( db, record->length ); + if ( ptr ) { + *offset = ptr->offset; + ptr->length -= record->length; + if ( ptr->length == 0 ) + vacuumFreeSpace(db); + if ( lseek(db->fd, *offset, SEEK_SET) != *offset ) + tlog(TL_CRIT|TL_EXIT,"FDBPut: lseek failed: %s", strerror(errno)); + } else { + if ( (*offset = lseek(db->fd, 0, SEEK_END)) < 0 ) + tlog(TL_CRIT|TL_EXIT,"FDBPut: lseek failed: %s", strerror(errno)); + } + + if ( write(db->fd, record, record->length) != record->length ) + tlog(TL_CRIT|TL_EXIT,"FDBPut: write lseek failed: %s", strerror(errno)); + + return FDB_OK; + +} + + diff --git a/flatdb.h b/flatdb.h new file mode 100644 index 0000000..16ef5c5 --- /dev/null +++ b/flatdb.h @@ -0,0 +1,47 @@ +#ifndef __FLAT_DB__ +#define __FLAT_DB__ + +#include + +typedef struct { + off_t freespace; + size_t lenfreespace; + u_int32_t + unused:31, + isopened:1; +} FDBHeader; + +typedef struct { + size_t length; + char *data; +} FDBRecord; + +#define RECHDRSZ (sizeof(size_t)) + +typedef struct { + off_t offset; + size_t length; +} FDBFreeSpace; + +typedef struct { + int fd; /* file descriptor */ + u_int32_t unused:31, + readonly:1; + u_int32_t listlen; + u_int32_t listcur; + FDBFreeSpace *space; +} FDB; + +int FDBOpen(FDB *db, char *file, int readonly); +int FDBClose(FDB *db); +int FDBPut(FDB *db, FDBRecord *record /*in*/, off_t *offset /*out*/); +int FDBGet(FDB *db, off_t offset /* in */, size_t length /*in, may =0, then stored size*/, FDBRecord **record /*out*/); +int FDBDelete(FDB *db, off_t offset /* in */, size_t length /*in, may =0, then stored size*/ ); +void FDBVacuumFreeSpace(FDB *db); + +#define FDB_OK 0 +#define FDB_NOTFOUND 1 +#define FDB_ERROR 2 +#define FDB_INCORRECT 3 + +#endif