/*#io UDB ioDoc( docCopyright("Steve Dekorte", 2004) docLicense("BSD revised") docObject("UDB") docDescription("") */ #include "SkipDB.h" #include "UDB.h" #include #include #include UDB *UDB_new(void) { UDB *self = (UDB *)calloc(1, sizeof(UDB)); self->index = UDBIndex_new(); self->records = UDBRecords_new(); UDB_setPath_(self, "default"); return self; } void UDB_free(UDB *self) { UDB_close(self); free(self->path); UDBIndex_free(self->index); UDBRecords_free(self->records); free(self); } void UDB_delete(UDB *self) { UDB_close(self); UDBIndex_delete(self->index); UDBRecords_delete(self->records); } void UDB_setPath_(UDB *self, const char *path) { self->path = strcpy((char *)realloc(self->path, strlen(path)+1), path); UDBIndex_setPath_(self->index, path); UDBRecords_setPath_(self->records, path); } void UDB_setSecondaryPath_(UDB *self, const char *secondaryPath) { UDBIndex_setPath_(self->index, self->path); UDBIndex_setLogPath_(self->index, secondaryPath); UDBRecords_setPath_(self->records, secondaryPath); UDBRecords_setLogPath_(self->records, self->path); } char *UDB_path(UDB *self) { return self->path; } void UDB_open(UDB *self) { UDBIndex_open(self->index); UDBRecords_open(self->records); if (UDBRecords_isCommitted(self->records)) { printf("UDB: found committed transaction that was not completed - completing now.\n"); UDB_commitTransaction(self); } self->isOpen = 1; } int UDB_isOpen(UDB *self) { return self->isOpen; } void UDB_close(UDB *self) { UDBIndex_close(self->index); UDBRecords_close(self->records); self->isOpen = 0; } // transactions --------------------------------------------------- int UDB_isInTransaction(UDB *self) { if (!self->withinTransaction) { printf("UDB error - mutation operations must be within a transaction\n"); exit(1); return 0; } return 1; } void UDB_beginTransaction(UDB *self) { UDBIndex_begin(self->index); UDBRecords_begin(self->records); self->withinTransaction = 1; } void UDB_commitTransaction(UDB *self) { UDBIndex_commit(self->index); UDBRecords_commit(self->records); self->withinTransaction = 0; } // ops --------------------------------------------------- PID_TYPE UDB_nextPid(UDB *self) { return UDBIndex_nextPid(self->index); } PID_TYPE UDB_allocPid(UDB *self) { return UDBIndex_allocPid(self->index); } void UDB_append_withPid_(UDB *self, Datum d, PID_TYPE pid) { UDBRecord *record = UDBRecords_newRecord(self->records); UDBRecord_pid_(record, pid); UDBRecord_saveWithDatum_(record, d); UDBIndex_setPos_forPid_(self->index, UDBRecord_pos(record), pid); } PID_TYPE UDB_put_(UDB *self, Datum d) // returns pid { if (UDB_isInTransaction(self)) { PID_TYPE pid = UDBIndex_allocPid(self->index); UDB_append_withPid_(self, d, pid); return pid; } return 0; } void UDB_at_put_(UDB *self, PID_TYPE pid, Datum d) { if (UDB_isInTransaction(self)) { UDB_append_withPid_(self, d, pid); } } UDBRecord *UDB_recordAtPid_(UDB *self, PID_TYPE pid) { PID_TYPE pos = UDBIndex_posForPid_(self->index, pid); if (!pos) return NULL; return UDBRecords_recordAtPos_(self->records, pos); } Datum UDB_at_(UDB *self, PID_TYPE pid) { Datum d; UDBRecord *record = UDB_recordAtPid_(self, pid); if (!record) { d.size = 0; d.data = NULL; //printf("missing record with pid %i\n", pid); return d; } return UDBRecord_readDatum(record); } void UDB_removeAt_(UDB *self, PID_TYPE pid) { if (UDB_isInTransaction(self)) { UDBRecord *record = UDB_recordAtPid_(self, pid); if (!record) { printf("UDB error: missing record with pid %" PID_FORMAT " for remove\n", pid); record = UDB_recordAtPid_(self, pid); return; } UDBRecords_removeRecord_(self->records, record); UDBIndex_setPos_forPid_(self->index, 0, pid); } } // compact --------------------------------------------------- UDBRecord *UDB_firstEmptyRecord(UDB *self) { UDBRecord *record = UDBRecords_firstRecord(self->records); while (record) { if (UDBRecord_isEmpty(record)) return record; record = UDBRecords_nextRecord(self->records); } return NULL; } int UDB_compact(UDB *self) { int count = 0; int compactions; do { compactions = UDB_compactStepFor_(self, 0.1); count += compactions; } while (compactions); return count; } //#define DEBUG 1 int UDB_compactStep(UDB *self) { return UDB_compactStepFor_(self, 0.0); } int UDB_compactStepFor_(UDB *self, double maxSeconds) { UDBRecord *firstEmptyRecord = UDBRecords_firstEmptyRecord(self->records); if (!firstEmptyRecord) { return 0; } if (!UDBRecord_isEmpty(firstEmptyRecord)) { printf("firstEmptyRecord not empty!\n"); firstEmptyRecord = UDBRecords_firstEmptyRecord(self->records); UDBRecord_isEmpty(firstEmptyRecord); exit(1); } #ifdef DEBUG printf("empty record pid: %i pos: %i size: %i isEmpty: %i\n", UDBRecord_pid(firstEmptyRecord), UDBRecord_pos(firstEmptyRecord), UDBRecord_totalSize(firstEmptyRecord), UDBRecord_isEmpty(firstEmptyRecord)); #endif if (firstEmptyRecord) { UDBRecord *record = UDBRecords_recordAfter_(self->records, firstEmptyRecord); if (!record) { // reached end of file PID_TYPE size = UDBRecord_pos(firstEmptyRecord); UDBRecords_truncate_(self->records, size); return 0; } #ifdef DEBUG printf("next record pid: %i pos: %i size: %i isEmpty: %i\n\n", UDBRecord_pid(record), UDBRecord_pos(record), UDBRecord_totalSize(record), UDBRecord_isEmpty(record)); #endif UDB_beginTransaction(self); if (UDBRecord_isEmpty(record)) { // coalese this empty record into the first empty record PID_TYPE newSize = UDBRecord_totalSize(firstEmptyRecord) - sizeof(UDBRecordHeader) + UDBRecord_totalSize(record); UDBRecord_size_(firstEmptyRecord, newSize); UDBRecord_saveHeader(firstEmptyRecord); #ifdef DEBUG printf("merge\n"); #endif UDBIndex_setPos_forPid_(self->index, 0, UDBRecord_pid(record)); } else { // swap places with the first empty record PID_TYPE oldEmptyPos = UDBRecord_pos(firstEmptyRecord); PID_TYPE newEmptyPos = oldEmptyPos + UDBRecord_totalSize(record); UDBRecords_firstEmptyRecordPos_(self->records, newEmptyPos); UDBRecord_moveToPos_(record, UDBRecord_pos(firstEmptyRecord)); UDBRecord_setPos_(firstEmptyRecord, newEmptyPos); UDBRecord_saveHeader(firstEmptyRecord); #ifdef DEBUG printf("move\n"); #endif UDBIndex_setPos_forPid_(self->index, newEmptyPos, UDBRecord_pid(firstEmptyRecord)); UDBIndex_setPos_forPid_(self->index, oldEmptyPos, UDBRecord_pid(record)); } UDB_commitTransaction(self); return 1; } return 0; } void UDB_show(UDB *self) { UDBRecord *record = UDBRecords_firstRecord(self->records); printf("UDB Records:\n"); while (record) { UDBRecord_show(record); record = UDBRecords_nextRecord(self->records); } } void UDB_showIndex(UDB *self) { UDBIndex_show(self->index); }