diff --git a/src/table.c b/src/table.c index 1f3bd0f..ca23df3 100644 --- a/src/table.c +++ b/src/table.c @@ -7,7 +7,8 @@ /* ** Initialize the table by alloocating necessary memory chunks. */ -PTable pit_table_initialize(int record_size, int flags) { +PTable pit_table_initialize(int record_size, int flags) +{ PTable pt = calloc(1, sizeof(Table)); pt->flags = flags; @@ -16,6 +17,7 @@ PTable pit_table_initialize(int record_size, int flags) { pt->number_of_records = 0; pt->auto_increment = 0; pt->current = 0; + pt->index_size = TABLE_INCREMENT; pt->slots = calloc(TABLE_INCREMENT, pt->record_size); pt->index = calloc(TABLE_INCREMENT, sizeof(char *)); @@ -25,14 +27,16 @@ PTable pit_table_initialize(int record_size, int flags) { /* ** Return the address of next avaiable slot within pt->slots chunk. */ -static char *table_available_slot(PTable pt) { +static char *table_available_slot(PTable pt) +{ return pt->slots + pt->number_of_records * pt->record_size; } /* ** Return the address of last stored record. */ -static char *table_last_record(PTable pt) { +static char *table_last_record(PTable pt) +{ if (pt->number_of_records == 0) { return pt->slots; } else { @@ -43,28 +47,48 @@ static char *table_last_record(PTable pt) { /* ** Return the address of next available pointer within pt->index chunk. */ -static char **table_available_index(PTable pt) { - return pt->index + pt->auto_increment; +static char **table_available_index(PTable pt) +{ + return HAS_ID(pt) ? (pt->index + pt->auto_increment) : NULL; } /* ** Return the address of last pointer within pt->index chunk. */ -static char **table_last_index(PTable pt) { - if (pt->auto_increment == 0) { - return pt->index; - } else { - return table_last_index(pt) - sizeof(char *); +static char **table_last_index(PTable pt) +{ + if (HAS_ID(pt)) { + if (pt->auto_increment == 0) { + return pt->index; + } else { + return table_last_index(pt) - sizeof(char *); + } } + return NULL; } /* -** Extend the table involves three steps. First, we reallocate pt->slots -** chunk adding TABLE_INCREMENT empty slots. Then we add the same number -** of empty indices to the the pt->index chunk. Finally, we adjust -** existing indices to make them point to reallocated record slots. +** Re-allocate the index and set newly added memory chunk to 0. */ -static PTable table_extend(PTable pt) { +static char **table_extend_index(PTable pt) +{ + if (HAS_ID(pt)) { + pt->index_size += TABLE_INCREMENT; + pt->index = realloc(pt->index, pt->index_size * sizeof(char *)); + memset(table_available_index(pt), 0, TABLE_INCREMENT * sizeof(char *)); + return pt->index; + } + + return NULL; +} + +/* +** Extend the table involves three steps. First, reallocate pt->slots +** chunk adding TABLE_INCREMENT empty slots. Then extend the index. +** Finally, adjust existing indices to point to reallocated record slots. +*/ +static PTable table_extend(PTable pt) +{ register int i; register char **pi; @@ -74,17 +98,16 @@ static PTable table_extend(PTable pt) { */ pt->slots = realloc(pt->slots, pt->number_of_slots * pt->record_size); memset(table_available_slot(pt), 0, TABLE_INCREMENT * pt->record_size); - /* - ** Re-allocate the index and set newly added memory chunk to 0. - */ - pt->index = realloc(pt->index, pt->number_of_slots * sizeof(char *)); - memset(table_available_index(pt), 0, TABLE_INCREMENT * sizeof(char *)); - /* - ** Reassign index entries to point to the re-allocatted records. - */ - for (i = 0, pi = pt->index; i < pt->auto_increment; i++, pi++) { - if (*pi != NULL) { - *pi = pt->slots + i * pt->record_size; + + if (HAS_ID(pt)) { + /* + ** Extend the index and make its entries point to reallocatted records. + */ + table_extend_index(pt); + for (i = 0, pi = pt->index; i < pt->auto_increment; i++, pi++) { + if (*pi != NULL) { + *pi = pt->slots + i * pt->record_size; + } } } return pt; @@ -93,29 +116,30 @@ static PTable table_extend(PTable pt) { /* ** Find a record by id and return its address. */ -char *pit_table_find(PTable pt, int id) { - // TODO: retrn NULL (or raise?) if table doesn't have id. - if (pt->number_of_records == 0 || id <= 0 || id > pt->auto_increment) { - return NULL; - } else { - return *(pt->index + id - 1); +char *pit_table_find(PTable pt, int id) +{ + if (HAS_ID(pt)) { + if (pt->number_of_records == 0 || id <= 0 || id > pt->auto_increment) { + return NULL; + } else { + return *(pt->index + id - 1); + } } + return NULL; } /* -** Delete a record by its ID. Return the address of deleted record or NULL +** Delete a record by its id. Return the address of deleted record or NULL ** if the record was not found. */ -char *pit_table_delete(PTable pt, int id) { - // TODO: return NULL (or die?) if table doesn't have id. +char *pit_table_delete(PTable pt, int id) +{ register char *pr = (char *)pit_table_find(pt, id); if (pr) { - register int i; - register char **pi; register char *last = table_last_record(pt); /* - ** Overwrite current record by shifting over remaining records + ** Overwrite current record by shifting over remaining slots. */ if (pr != last) { memmove(pr, pr + pt->record_size, last - pr); @@ -124,15 +148,20 @@ char *pit_table_delete(PTable pt, int id) { ** Set the slot occupied by the last record to zero. */ memset(last, 0, pt->record_size); - /* - ** Set current record pointer to NULL, then update the rest of - ** the index to point to the shifted records. - */ - pi = pt->index + id - 1; - *pi++ = NULL; - for (i = id; i < pt->auto_increment; i++) { - *pi++ -= pt->record_size; + if (HAS_ID(pt)) { + register int i; + register char **pi; + /* + ** Set current record pointer to NULL, then update the rest of + ** the index to point to the shifted records. + */ + pi = pt->index + id - 1; + *pi++ = NULL; + + for (i = id; i < pt->auto_increment; i++) { + *pi++ -= pt->record_size; + } } pt->number_of_records--; } @@ -144,58 +173,70 @@ char *pit_table_delete(PTable pt, int id) { ** Insert a record and return its address. The table gets extended ** as necessary. */ -char *pit_table_insert(PTable pt, char *record) { - register char **pi; +char *pit_table_insert(PTable pt, char *record) +{ + register char *pr; register time_t now = time(NULL); if (pt->number_of_records >= pt->number_of_slots) { pt = table_extend(pt); + } else { + if (HAS_ID(pt) && pt->auto_increment >= pt->index_size) { + table_extend_index(pt); + } } - pi = table_available_index(pt); - *pi = table_available_slot(pt); - memmove(table_available_slot(pt), record, pt->record_size); - + pr = table_available_slot(pt); + memmove(pr, record, pt->record_size); pt->number_of_records++; - pt->auto_increment++; - /* - ** Update record id if the table has primary key. The id must be the first - ** record field of type "unsigned long". - */ - if (pt->flags & TABLE_HAS_ID) { + + if (HAS_ID(pt)) { + /* + ** Save current slot address in the index. + */ + register char **pi = table_available_index(pt); + *pi = pr; + /* + ** Update record id if the table has primary key. The id must be the first + ** record field of type "unsigned long". + */ + pt->auto_increment++; *(int *)*pi = pt->auto_increment; } /* ** Update created_at and/or updated_at which must be last one or two record ** fields of type "time_t". */ - if (pt->flags & TABLE_HAS_CREATED_AT || pt->flags & TABLE_HAS_UPDATED_AT) { - *(time_t *)(*pi + pt->record_size - sizeof(time_t)) = now; + if (HAS_CREATED_AT(pt) || HAS_UPDATED_AT(pt)) { + *(time_t *)(pr + pt->record_size - sizeof(time_t)) = now; } - if (pt->flags & TABLE_HAS_CREATED_AT && pt->flags & TABLE_HAS_UPDATED_AT) { - *(time_t *)(*pi + pt->record_size - sizeof(time_t) * 2) = now; + if (HAS_CREATED_AT(pt) && HAS_UPDATED_AT(pt)) { + *(time_t *)(pr + pt->record_size - sizeof(time_t) * 2) = now; } - return *pi; + return pr; } /* ** Find current record. */ -char *pit_table_current(PTable pt) { +char *pit_table_current(PTable pt) +{ return pit_table_find(pt, pt->current); } /* ** Set current record as indicated by the id, then find and return it. */ -char *pit_table_mark(PTable pt, int id) { +char *pit_table_mark(PTable pt, int id) +{ return pit_table_find(pt, pt->current = id); } /* ** Release pt->slots and pt->index memory chunks, then free the table itself. */ -void pit_table_free(PTable pt) { +void pit_table_free(PTable pt) +{ if (pt) { if (pt->index) { free(pt->index); @@ -211,13 +252,14 @@ void pit_table_free(PTable pt) { ** Save the contents of the table to file. The file handle should be ** open with for writing. */ -int pit_table_save(FILE *file, PTable pt) { +int pit_table_save(FILE *file, PTable pt) +{ register int written = 0; /* ** Save table header data: flags, record_size, number_of_slots, - ** number_of_records, and auto_increment, current. + ** number_of_records, auto_increment, current, and index_size. */ - written += fwrite(pt, sizeof(int), 6, file); + written += fwrite(pt, sizeof(int), 7, file); /* ** Save the records. Note that we save the actual (not allocated) data. */ @@ -229,29 +271,32 @@ int pit_table_save(FILE *file, PTable pt) { ** Load the contents of the table from file. The file handle should be ** open with for reading. */ -PTable pit_table_load(FILE *file) { +PTable pit_table_load(FILE *file) +{ PTable pt; register int read = 0; register int i; - char *pr, **pi; + char *pr, **pi = NULL; pt = calloc(1, sizeof(Table)); /* ** First read the header. */ - read += fread(pt, sizeof(int), 6, file); + read += fread(pt, sizeof(int), 7, file); /* ** Now allocate slots and index based on the original number of slots. */ /*** printf("Allocating %d slots\n", pt->number_of_slots); ***/ pt->slots = pr = calloc(pt->number_of_slots, pt->record_size); - pt->index = pi = calloc(pt->number_of_slots, sizeof(char *)); + if (HAS_ID(pt)) { + pt->index = pi = calloc(pt->index_size, sizeof(char *)); + } /* ** Now read the records into the slots and rebuild the index if the ** table has primary key. */ read += fread(pt->slots, pt->record_size, pt->number_of_records, file); - if (pt->flags & TABLE_HAS_ID) { + if (HAS_ID(pt)) { for(i = 1; i <= pt->number_of_slots; i++, pi++) { if ((int)*pr == i) { *pi = pr; @@ -275,8 +320,8 @@ typedef struct { void dump(Record *prec) { if (prec) { - printf("(%08lX) id: %08d, value: %d, created_at: %d, updated_at: %d\n", - (int)prec, prec->id, prec->value, prec->created_at, prec->updated_at); + printf("(%08lX) id: %08d, value: %d, created_at: %ld, updated_at: %ld\n", + (unsigned long)prec, prec->id, prec->value, (time_t)prec->created_at, (time_t)prec->updated_at); } else { printf("(NULL)\n"); } @@ -316,11 +361,7 @@ int main() { pit_table_mark(pt, prec->id); printf("current: %d\n", pt->current); - // for (i = 20; i < total; i++) { - // printf("Deleting %d\n", i + 1); - // prec = (Record *)pit_table_delete(pt, i + 1); - // } - printf("Deleting %d\n", 1L); + printf("Deleting %d\n", 1); prec = (Record *)pit_table_delete(pt, 1); printf("current: %d\n", pt->current); dump_all(pt); diff --git a/src/table.h b/src/table.h index 7a5a610..4e1353f 100644 --- a/src/table.h +++ b/src/table.h @@ -6,12 +6,18 @@ #define TABLE_HAS_UPDATED_AT 4 #define TABLE_HAS_TIMESTAMPS (TABLE_HAS_CREATED_AT | TABLE_HAS_UPDATED_AT) +#define HAS_ID(pt) (pt && (pt->flags & TABLE_HAS_ID)) +#define HAS_CREATED_AT(pt) (pt && (pt->flags & TABLE_HAS_CREATED_AT)) +#define HAS_UPDATED_AT(pt) (pt && (pt->flags & TABLE_HAS_UPDATED_AT)) +#define HAS_TIMESTAMPS(pt) (HAS_CREATED_AT(pt) && HAS_UPDATED_AT(pt)) + typedef struct _Table { int flags; /* Bit mask with table flags. */ int record_size; /* Record size in bytes; all records are of fixed size. */ int number_of_slots; /* Number of slots allocated, each slot is 'record_size' long. */ int number_of_records; /* Number of records currently stored in slots. */ int auto_increment; /* Current value of record id. */ + int index_size; /* The size of the index. */ int current; /* The id of currently selected record, one per table. */ char *slots; /* Memory chunk to store records; compacted when a record gets deleted (no holes). */ char **index; /* Memory chunk to store pointers to individual records, holes for deleted record IDs. */