mirror of
https://github.com/michaeldv/pit.git
synced 2025-12-08 15:43:25 +00:00
Cleaned up table engine to make sure index gets updated correctly
This commit is contained in:
203
src/table.c
203
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);
|
||||
|
||||
@@ -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. */
|
||||
|
||||
Reference in New Issue
Block a user