From b96ad0e1d7d6e2c4f5360e8a120414fb740b2b60 Mon Sep 17 00:00:00 2001 From: dam Date: Fri, 26 Aug 2022 20:12:33 +0000 Subject: Using new database_t struct and companion functions. --- main.c | 293 ++++++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 188 insertions(+), 105 deletions(-) diff --git a/main.c b/main.c index 19b9d23..0441eff 100644 --- a/main.c +++ b/main.c @@ -31,16 +31,25 @@ typedef /*struct __attribute__((__packed__))*/ struct { uint8_t state; } task_t; +typedef /*struct __attribute__((__packed__))*/ struct { + task_t* tasks; + uint32_t count; + uint32_t capacity; +} database_t; + const char* DB_BIN_FILE_SIGNATURE = "TTBF:1.0"; const uint8_t DB_BIN_FILE_SIGNATURE_SIZE = sizeof(DB_BIN_FILE_SIGNATURE); const size_t SIZEOF_TASK_T = sizeof(task_t); +const size_t SIZEOF_DATABASE_T = sizeof(database_t); const char* DAYS_OF_WEEK[] = { "sun", "mon", "tue", "wed", "thu", "fri", "sat" }; const uint8_t DAYS_ON_WEEK = sizeof(DAYS_OF_WEEK)/sizeof(char*); -task_t* tasks = NULL; -uint32_t tasks_count = 0; -uint32_t tasks_capacity = 0; uint32_t selected_task = -1; +database_t database = { + .tasks = NULL, + .count = 0, + .capacity = 0, +}; // Given an UTF8 encoded string, truncate it to length without breaking any UTF8 character. @@ -63,6 +72,8 @@ size_t truncate_string_utf8(char* string, size_t length) { return length - idx; } +// Uses strchr to replace all instances of find by replace. +// Returns string. char* replace_char(char* string, char find, char replace){ char* idx = string; while((idx = strchr(idx, find)) != NULL) { @@ -83,42 +94,108 @@ void print_task(const task_t* task) { printf("t[6]: '%" PRIu32 "'\n", task->time[6]); } -void initialization() { - // Make sure architecture uses 8bits per char. - assert(CHAR_BIT == 8); +// TODO Maybe inline? +bool add_task(database_t* db, const task_t* task) { + assert(db != NULL); + assert(task != NULL); + + uint32_t db_capacity = db->capacity; + if (db->count + 1 > db_capacity) { + uint32_t new_database_capacity = db_capacity == 0 ? 16 : db_capacity << 1; + + // TODO Funcky way to calculate nex power of two. + new_database_capacity--; + new_database_capacity |= new_database_capacity >> 1; + new_database_capacity |= new_database_capacity >> 2; + new_database_capacity |= new_database_capacity >> 4; + new_database_capacity |= new_database_capacity >> 8; + new_database_capacity |= new_database_capacity >> 16; + new_database_capacity++; + + task_t* new_database_tasks = realloc(db->tasks, new_database_capacity * SIZEOF_TASK_T); + if (new_database_tasks == NULL) { + fprintf(stderr, "Failed to expand database memory.\n"); + return false; + } + db->tasks = new_database_tasks; + } + memcpy(&(db->tasks[db->count]), task, SIZEOF_TASK_T); + db->count++; + + return true; +} + +// TODO Maybe inline? +bool remove_task(database_t* db, uint32_t index) { + assert(db != NULL); + assert(index < db->count); + + /* + | ~ ~ 6 + 0 1 2 3 4 5 6 - 3 - 1 = 2 + + | ~ 6 + 0 1 2 3 4 5 6 - 4 - 1 = 1 + + | ~ 6 + 0 1 2 3 4 5 6 - 5 - 1 = 0 + */ + memmove(&db->tasks[index], &db->tasks[index+1], (db->count - index - 1) * SIZEOF_TASK_T); + db->count--; + + if (db->capacity > 16 && db->count < (db->capacity >> 2)) { + uint32_t new_tasks_capacity = db->capacity << 1; + task_t* new_tasks = realloc(db->tasks, new_tasks_capacity * SIZEOF_TASK_T); + if (new_tasks == NULL) { + fprintf(stderr, "Failed to shrink database memory.\n"); + return false; + } + db->tasks = new_tasks; + } + return true; } -void store_database(const task_t* database, uint32_t number_of_entries, const char* path_name) { +void clear_database(database_t* db) { + free(db->tasks); + db->tasks = NULL; + db->count = 0; + db->capacity = 0; +} + +// Stores data from database into binary file. +// Returns success. +bool store_database(const database_t* db, const char* path_name) { - assert(database != NULL); + assert(db != NULL); assert(path_name != NULL); // Open file. FILE* file = fopen(path_name, "w"); if (file == NULL) { - fprintf(stderr, "Failed to open database file: %s.\n", strerror(errno)); // TODO Fix message. - return; + fprintf(stderr, "Failed to open file '%s' while storing database: %s.\n", path_name, strerror(errno)); + return false; } fwrite(DB_BIN_FILE_SIGNATURE, sizeof(char), DB_BIN_FILE_SIGNATURE_SIZE, file); - fwrite(&number_of_entries, sizeof(number_of_entries), 1, file); - fwrite(database, SIZEOF_TASK_T, number_of_entries, file); + fwrite(&db->count, sizeof(db->count), 1, file); + fwrite(db->tasks, SIZEOF_TASK_T, db->count, file); fclose(file); + return true; } // Loads data from binary file into database. -// Returns number of loaded entries. -uint32_t load_database(task_t** database, const char* path_name) { +// Returns success. +bool load_database(database_t* db, const char* path_name) { - assert(database != NULL); + assert(db != NULL); assert(path_name != NULL); // Open file. FILE* file = fopen(path_name, "r"); if (file == NULL) { - fprintf(stderr, "Failed to open database file '%s': %s.\n", path_name, strerror(errno)); // TODO Fix message. - return 0; + fprintf(stderr, "Failed to open file '%s' while loading database: %s.\n", path_name, strerror(errno)); + return false; } // Validate file signature. @@ -126,34 +203,38 @@ uint32_t load_database(task_t** database, const char* path_name) { fread(&file_signature, sizeof(char), DB_BIN_FILE_SIGNATURE_SIZE, file); if (strncmp(file_signature, DB_BIN_FILE_SIGNATURE, DB_BIN_FILE_SIGNATURE_SIZE) != 0) { fprintf(stderr, "Invalid file signature.\n"); - return 0; + return false; } + // Discard all previous data. + clear_database(&database); + // Read number of entries. - uint32_t number_of_entries; - fread(&number_of_entries, sizeof(number_of_entries), 1, file); + fread(&db->count, sizeof(db->count), 1, file); // Read database entries. - *database = calloc(number_of_entries, SIZEOF_TASK_T); - fread(*database, SIZEOF_TASK_T, number_of_entries, file); + db->tasks = calloc(db->count, SIZEOF_TASK_T); + fread(db->tasks, SIZEOF_TASK_T, db->count, file); // Make sure we are reading all the file. assert(fgetc(file) == EOF); fclose(file); - return number_of_entries; + return true; } -void export_database(const task_t* database, uint32_t number_of_entries, const char* path_name) { +// Exports data into CSV file. +// Returns success. +bool export_to_csv(const database_t* db, const char* path_name) { - assert(database != NULL); + assert(db != NULL); assert(path_name != NULL); FILE* file = fopen(path_name, "w"); if (file == NULL) { - fprintf(stderr, "Failed to open database file '%s': %s.\n", path_name, strerror(errno)); // TODO Fix message. - return; + fprintf(stderr, "Failed to open file '%s' while exporting to CSV: %s.\n", path_name, strerror(errno)); + return false; } fprintf(file, "%s,%s,%s,%s,%s,%s,%s,%s\n", @@ -167,8 +248,8 @@ void export_database(const task_t* database, uint32_t number_of_entries, const c DAYS_OF_WEEK[6] ); - for (uint32_t idx = 0; idx < number_of_entries; idx++) { - const task_t* task = &database[idx]; + for (uint32_t idx = 0; idx < db->count; idx++) { + const task_t* task = &db->tasks[idx]; fprintf(file, "%s,%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 "\n", task->name, task->time[0], @@ -182,23 +263,23 @@ void export_database(const task_t* database, uint32_t number_of_entries, const c } fclose(file); + return true; } -uint32_t import_database(task_t** database, const char* path_name) { - - assert(database != NULL); +// Imports CSV file into database. +// Returns success. +bool import_from_csv(database_t* db, const char* path_name) { + assert(db != NULL); assert(path_name != NULL); FILE* file = fopen(path_name, "r"); if (file == NULL) { - fprintf(stderr, "Failed to open file '%s' while importing database: %s.\n", path_name, strerror(errno)); - return 0; + fprintf(stderr, "Failed to open file '%s' while importing from CSV: %s.\n", path_name, strerror(errno)); + return false; } uint32_t number_of_entries = 0; - uint32_t capacity = 0; task_t task; - task_t* database_reallocation = NULL; char *line_buffer = NULL; size_t line_buffer_size = 0; ssize_t read_characters = 0; @@ -206,7 +287,7 @@ uint32_t import_database(task_t** database, const char* path_name) { // Skip header line. fscanf(file, "%*[^\n]\n"); - + // Parse CSV file. while(true) { @@ -243,42 +324,19 @@ uint32_t import_database(task_t** database, const char* path_name) { &task.time[6] ) != 7) { replace_char(line_buffer, '\n', ' '); -// fprintf(stderr, "Discarding invalid line '%s' and continuing.\n", line_buffer); + fprintf(stderr, "Discarding invalid line '%s' and continuing.\n", line_buffer); continue; } - // Expand database capacity when required. + // Add new database entry. number_of_entries++; - if (number_of_entries > capacity) { - capacity = capacity == 0 ? 16 : capacity << 1; - database_reallocation = realloc(*database, capacity * SIZEOF_TASK_T); - if (database_reallocation == NULL) { - fprintf(stderr, "Failed to allocate memory while importing database.\n"); - number_of_entries--; - break; - } - *database = database_reallocation; - } - - memcpy(&((*database)[number_of_entries-1]), &task, SIZEOF_TASK_T); + add_task(db, &task); } fclose(file); free(line_buffer); - // Trim any excess memory allocated for database. - if (capacity > number_of_entries) { -// *database = realloc(*database, number_of_entries * SIZEOF_TASK_T); // TODO dangerous stuff going on here. - database_reallocation = realloc(*database, number_of_entries * SIZEOF_TASK_T); - if (database_reallocation == NULL) { - fprintf(stderr, "Failed to allocate memory while importing database.\n"); - } - else { - *database = database_reallocation; - } - } - - return number_of_entries; + return true; } @@ -319,32 +377,34 @@ void prototype(int level) { /////////////////////////////////////////////////////////////////////////// // Prepare some data for testing. - - tasks_count = 3; - tasks = calloc(tasks_count, SIZEOF_TASK_T); - + /* task_t tmp[] = { { - .name = "TASK-00 : Sample task name.", - .time = { 3,2,1,0,1,2,3 }, + .name = "ALPHA-TASK", + .time = { 0, 0, 0, 0, 0, 0, 0 }, }, { - .name = "TASK-01 : A wild task appears", - .time = { 1,2,3,4,5,6,7 }, + .name = "BETA-TASK", + .time = { 1, 1, 1, 1, 1, 1, 1 }, }, { - .name = "BAZINGA : Not a task!", - .time = { 11,22,33,44,55,66,77 }, + .name = "DELTA-TASK", + .time = { 2, 2, 2, 2, 2, 2, 2 }, } - }; - memcpy(tasks, &tmp, SIZEOF_TASK_T * tasks_count); + }; + uint32_t number_of_items = sizeof(tmp) / sizeof(task_t); + database.tasks = calloc(number_of_items, SIZEOF_TASK_T); + database.capacity = number_of_items; + database.count = number_of_items; + memcpy(database.tasks, &tmp, SIZEOF_TASK_T * number_of_items); + */ /////////////////////////////////////////////////////////////////////////// // Store database from memory to binary file. if (level & T_SBIN) { fprintf(stderr, "# store database ------------------------------ \\\n"); - store_database(tasks, tasks_count, DB_BIN_PATH_NAME); + store_database(&database, DB_BIN_PATH_NAME); fprintf(stderr, done); } @@ -352,14 +412,16 @@ void prototype(int level) { // Load database from binary file to memory. if (level & T_LBIN) { fprintf(stderr, "# load database ------------------------------- \\\n"); - task_t* task_load = NULL; - uint32_t number_of_entries_load = load_database(&task_load, DB_BIN_PATH_NAME); + database_t db_load; + memset(&db_load, '\0', SIZEOF_DATABASE_T); - fprintf(stderr, "loaded %" PRIu32 " entries.\n", number_of_entries_load); - for (uint32_t idx = 0; idx < number_of_entries_load; idx++) { - print_task(&task_load[idx]); + load_database(&db_load, DB_BIN_PATH_NAME); + fprintf(stderr, "loaded %" PRIu32 " entries.\n", db_load.count); + + for (uint32_t idx = 0; idx < db_load.count; idx++) { + print_task(&db_load.tasks[idx]); } - free(task_load); + clear_database(&db_load); fprintf(stderr, done); } @@ -367,7 +429,7 @@ void prototype(int level) { // Export database to CSV file. if (level & T_ECSV) { fprintf(stderr, "# export to CSV ------------------------------- \\\n"); - export_database(tasks, tasks_count, DB_CSV_PATH_NAME); + export_to_csv(&database, DB_CSV_PATH_NAME); fprintf(stderr, done); } @@ -375,14 +437,16 @@ void prototype(int level) { // Import database from CSV file. if (level & T_ICSV) { fprintf(stderr, "# import from CSV ----------------------------- \\\n"); - task_t* task_import = NULL; - uint32_t number_of_entries_import = import_database(&task_import, DB_CSV_PATH_NAME); + database_t db_import; + memset(&db_import, '\0', SIZEOF_DATABASE_T); + + import_from_csv(&db_import, DB_CSV_PATH_NAME); + fprintf(stderr, "imported %" PRIu32 " entries.\n", db_import.count); - fprintf(stderr, "imported %" PRIu32 " entries.\n", number_of_entries_import); - for (uint32_t idx = 0; idx < number_of_entries_import; idx++) { - print_task(&task_import[idx]); + for (uint32_t idx = 0; idx < db_import.count; idx++) { + print_task(&db_import.tasks[idx]); } - free(task_import); + clear_database(&db_import); fprintf(stderr, done); } @@ -394,12 +458,6 @@ void prototype(int level) { fprintf(stderr, "sizeof(super_t) = %zu bytes (%zu bits : %6.3f W64b)\n", tts, tts*8, ((double)tts)*8.0/64.0); fprintf(stderr, done); } - - /////////////////////////////////////////////////////////////////////////// - // Release memory and exit. - -// free(tasks); -// return; } @@ -456,10 +514,23 @@ void initialize_layouts() { // layout->table_headers = { " T ", " M ", " T ", " W ", " T ", " F ", " S ", " S ", " # " }; } +void initialization() { + // Make sure architecture uses 8bits per char. + assert(CHAR_BIT == 8); + + // TODO May use the memset or set the properties during instancing. + // Prepare memory. + memset(&database, '\0', SIZEOF_DATABASE_T); +} + void free_memory() { + clear_database(&database); + free(line_buffer); - free(tasks); // TODO Deallocate tasks. + line_buffer = NULL; + free(layouts); + layouts = NULL; } void draw_header() { @@ -521,9 +592,9 @@ void draw_table() { }; move(1, 0); - for (uint32_t idx = 0; idx < tasks_count; idx++){ + for (uint32_t idx = 0; idx < database.count; idx++){ - task = &tasks[idx]; + task = &database.tasks[idx]; // Enable highlight color on selected entry. if (idx == selected_task) { @@ -585,11 +656,8 @@ void destroy_win(WINDOW *local_win); int main(int argc, char *argv[]) { initialization(); - - // TODO Parse commands using: https://stackoverflow.com/questions/9642732/parsing-command-line-arguments-in-c if (argc > 1) { -// const char* command_version = { "--version", "-v" }; char* action; bool do_action = false; @@ -625,6 +693,22 @@ int main(int argc, char *argv[]) { return EXIT_SUCCESS; } + action = "--t_lbin"; + do_action = strncmp(argv[idx], action, strlen(action)) == 0; + if (do_action) { + prototype(T_LBIN); + return EXIT_SUCCESS; + } + + action = "--t_csv2bin"; + do_action = strncmp(argv[idx], action, strlen(action)) == 0; + if (do_action) { + clear_database(&database); + import_from_csv(&database, DB_CSV_PATH_NAME); + store_database(&database, DB_BIN_PATH_NAME); + return EXIT_SUCCESS; + } + action = "--test"; do_action = strncmp(argv[idx], action, strlen(action)) == 0; if (do_action) { @@ -638,8 +722,7 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - tasks_count = load_database(&tasks, DB_BIN_PATH_NAME); - tasks_capacity = tasks_count; + load_database(&database, DB_BIN_PATH_NAME); initialize_layouts(); @@ -691,7 +774,7 @@ int main(int argc, char *argv[]) { break; case KEY_DOWN: - selected_task = (selected_task+1) == tasks_count ? selected_task : selected_task + 1; + selected_task = (selected_task+1) == database.count ? selected_task : selected_task + 1; destroy_win(my_win); my_win = create_newwin(height, width, ++starty,startx); break; -- cgit v1.2.3