+/*
+ * Copyright (c) 2004 Teodor Sigaev <teodor@sigaev.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include "tlog.h"
#include "tmalloc.h"
return NULL;
}
+static void
+addFreeSpace(FDB *db, off_t offset, size_t length) {
+ 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++;
+}
+
static int
cmpFS(const void* a, const void* b) {
if ( ((FDBFreeSpace*)a)->offset == ((FDBFreeSpace*)b)->offset )
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);
+ /* merge spaces */
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 {
+ ptr->length=0;
+ } else
ok++;
- if ( ok != ptr-1 )
- memcpy(ok, ptr, sizeof(FDBFreeSpace));
- }
ptr++;
}
- db->listcur = ok - db->space + 1;
+ /* remove void spaces */
+ ptr = ok = db->space;
+ while( ptr - db->space < db->listcur ) {
+ if ( ptr->length != 0 ) {
+ if ( ok != ptr )
+ memcpy(ok,ptr,sizeof(FDBFreeSpace));
+ ok++;
+ }
+ ptr++;
+ }
+ db->listcur = ok - db->space;
}
int
tlog(TL_CRIT,"FDBOpen: free space read failed: %s", strerror(errno));
return FDB_ERROR;
}
+ FDBVacuumFreeSpace(db);
} else {
db->listlen = 8;
db->space = (FDBFreeSpace*)tmalloc( db->listlen*sizeof(FDBFreeSpace) );
header.lenfreespace = 0;
header.isopened = 1;
- if ( lseek(db->fd, 0, SEEK_SET)!=0 || write(db->fd, &header, sizeof(FDBHeader)) != sizeof(FDBHeader) ) {
+ if ( lseek(db->fd, 0, SEEK_SET)!=0 ||
+ write(db->fd, &header, sizeof(FDBHeader)) != sizeof(FDBHeader) ||
+ fsync(db->fd) ) {
close(db->fd);
if ( db->space ) tfree( db->space );
tlog(TL_CRIT,"FDBOpen: can't modify header: %s", strerror(errno));
if ( db->listcur ) {
FDBFreeSpace *ptr;
- vacuumFreeSpace(db);
+ FDBVacuumFreeSpace(db);
+
header.lenfreespace = sizeof(FDBFreeSpace)*db->listcur;
ptr = findFreeSpace( db, header.lenfreespace );
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));
+ tlog(TL_CRIT|TL_EXIT,"FDBClose: lseek failed: %s", strerror(errno));
+ header.lenfreespace += sizeof(FDBFreeSpace);
+ addFreeSpace(db, header.freespace, header.lenfreespace);
}
if ( write(db->fd, db->space, header.lenfreespace) != header.lenfreespace )
header.isopened=0;
- if ( lseek(db->fd,0,SEEK_SET)!=0 || write(db->fd, &header, sizeof(FDBHeader)) != sizeof(FDBHeader) )
+ if ( lseek(db->fd,0,SEEK_SET)!=0 ||
+ write(db->fd, &header, sizeof(FDBHeader)) != sizeof(FDBHeader) ||
+ fsync(db->fd))
tlog(TL_CRIT|TL_EXIT,"FDBClose: header write failed: %s", strerror(errno));
}
return FDB_OK;
}
+
int
FDBDelete(FDB *db, off_t offset, size_t length) {
if ( db->readonly )
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++;
+ addFreeSpace(db, offset, length);
return FDB_OK;
}
*record=NULL;
- if ( db->readonly )
+ if ( offset < sizeof(FDBHeader) )
return FDB_ERROR;
if ( length==0 )
if ( ptr ) {
*offset = ptr->offset;
ptr->length -= record->length;
- if ( ptr->length == 0 )
- vacuumFreeSpace(db);
+ if ( ptr->length == 0 ) {
+ if ( (ptr - db->space) + 1 != db->listcur )
+ memmove(ptr, ptr+1, (db->listcur - (ptr - db->space) + 1) * sizeof(FDBFreeSpace));
+ db->listcur--;
+ } else
+ ptr->offset += record->length;
if ( lseek(db->fd, *offset, SEEK_SET) != *offset )
tlog(TL_CRIT|TL_EXIT,"FDBPut: lseek failed: %s", strerror(errno));
} else {
}
if ( write(db->fd, record, record->length) != record->length )
- tlog(TL_CRIT|TL_EXIT,"FDBPut: write lseek failed: %s", strerror(errno));
+ tlog(TL_CRIT|TL_EXIT,"FDBPut: write failed: %s", strerror(errno));
return FDB_OK;