diff --git a/.gitignore b/.gitignore index b80e521a2fafc7925c175948ed1fba7ab2a1b8d1..3e1f9e86baacaa3d9578b5936e3a78f2e232fddb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.o +src/directory_database src/program diff --git a/src/Array.c b/src/Array.c index 98a6a533a96645cc1855ac7b3518697904d6a459..2d7daa5a88932e6de9a1326a2736cddd7d193e58 100644 --- a/src/Array.c +++ b/src/Array.c @@ -91,7 +91,7 @@ void IntegerArray_print(IntegerArray *array) { printf("["); for (int i = 0; i < array->size; i++) { - printf("%ld", array->items[i]); + printf("%lu", array->items[i]); if (i < array->size - 1) { printf(", "); @@ -147,3 +147,21 @@ void BPTreeNodeArray_delete_at_index(BPTreeNodeArray *array, int index) { array->size--; } + +ByteArray *ByteArray_init(int capacity) { + ByteArray *array = (ByteArray *)malloc(sizeof(ByteArray)); + array->items = (uint8_t *)malloc(sizeof(uint8_t) * capacity); + array->size = 0; + return array; +} + +void ByteArray_destroy(ByteArray **array) { + free((*array)->items); + free(*array); + *array = NULL; +} + +void ByteArray_append(ByteArray *array, uint8_t item) { + array->items[array->size] = item; + array->size++; +} diff --git a/src/Array.h b/src/Array.h index a853833d41f82fc73722dc0426e008d01020fb1c..ba8d3848961daf89dc37e3da99cbdc4f4000d2b5 100644 --- a/src/Array.h +++ b/src/Array.h @@ -4,6 +4,8 @@ #include <stdbool.h> #include <stdint.h> +// IntegerArray + typedef struct IntegerArray { uint64_t *items; int size; @@ -23,6 +25,8 @@ void IntegerArray_print(IntegerArray *array); void IntegerArray_clear(IntegerArray *array); void IntegerArray_copy(IntegerArray *src, IntegerArray *dest); +// BPTreeNodeArray + typedef struct BPTreeNode BPTreeNode; typedef struct BPTreeNodeArray { @@ -37,4 +41,16 @@ void BPTreeNodeArray_insert_at_index(BPTreeNodeArray *array, int index, BPTreeNo void BPTreeNodeArray_append(BPTreeNodeArray *array, BPTreeNode *item); void BPTreeNodeArray_delete_at_index(BPTreeNodeArray *array, int index); +// ByteArray + +typedef struct ByteArray { + uint8_t *items; + int size; +} ByteArray; + +ByteArray *ByteArray_init(int capacity); +void ByteArray_destroy(ByteArray **array); + +void ByteArray_append(ByteArray *array, uint8_t item); + #endif diff --git a/src/Directory.c b/src/Directory.c index 874f0dede9b0bdfd813540b18ac60f5bb52e8d14..fe59d12efae6959fc423728d555fa82cebbc3d1d 100644 --- a/src/Directory.c +++ b/src/Directory.c @@ -1,10 +1,15 @@ +// CAUTION : l'implémentation ne va pas fonctionner avec les records supprimés. +// Je devrais optimiser l'implémentation avec le FILE. + #include "Directory.h" #include <openssl/sha.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include "Array.h" #include "DirectoryRecord.h" #include "bptree.h" @@ -28,38 +33,79 @@ static uint64_t hash_string(char *str) { return truncated_hash; } -Directory *Directory_init() { +static long get_file_size(FILE *fp) { + fseek(fp, 0, SEEK_END); + long file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + return file_size; +} + +static void rebuild_index(Directory *directory) { + FILE *fp; + fp = fopen(directory->database_filename, "rb"); + + if (fp == NULL) { + return; + } + + long file_size = get_file_size(fp); + + while (true) { + uint64_t data_ptr = (uint64_t)ftell(fp); + fseek(fp, 1, SEEK_CUR); + char phone_number[PHONE_NUMBER_MAX_LENGTH]; + phone_number[PHONE_NUMBER_MAX_LENGTH - 1] = '\0'; + fread(phone_number, 1, PHONE_NUMBER_MAX_LENGTH_WITHOUT_NULL_CHARACTER, fp); + bptree_insert(directory->index, hash_string(phone_number), data_ptr); + fseek(fp, DirectoryRecord_size_on_disk() - 1 - PHONE_NUMBER_MAX_LENGTH_WITHOUT_NULL_CHARACTER, SEEK_CUR); + + if (ftell(fp) == file_size) { + break; + } + } +} + +Directory *Directory_init(char database_filename[100]) { Directory *directory = (Directory *)malloc(sizeof(Directory)); - directory->records_length = 0; - // WARNING - directory->records = (DirectoryRecord **)malloc(sizeof(Directory *) * 1000); + strcpy(directory->database_filename, database_filename); directory->index = bptree_init(DEFAULT_ORDER); + rebuild_index(directory); + bptree_print(directory->index, 0); return directory; } void Directory_destroy(Directory **directory) { - for (int i = 0; i < (*directory)->records_length; i++) { - DirectoryRecord_destroy(&(*directory)->records[i]); - } - - free((*directory)->records); bptree_destroy(&(*directory)->index); free(*directory); *directory = NULL; } void Directory_print(Directory *directory) { - if (directory->records_length == 0) { - printf("===>There are no recorded members in the directory.\n"); - return; + FILE *fp; + fp = fopen(directory->database_filename, "rb"); + + if (fp == NULL) { + printf("===>The directory is empty.\n"); } + long file_size = get_file_size(fp); printf("========================\n"); - for (int i = 0; i < directory->records_length; i++) { - DirectoryRecord_print(directory->records[i]); + while (true) { + ByteArray *byte_array = ByteArray_init(DirectoryRecord_size_on_disk()); + + fread(byte_array->items, 1, DirectoryRecord_size_on_disk(), fp); + byte_array->size = DirectoryRecord_size_on_disk(); + + DirectoryRecord *record = ByteArray_to_DirectoryRecord(byte_array); + DirectoryRecord_print(record); + DirectoryRecord_destroy(&record); + + ByteArray_destroy(&byte_array); - if (i < directory->records_length - 1) { + if (ftell(fp) == file_size) { + break; + } else { printf("------------------------\n"); } } @@ -67,18 +113,49 @@ void Directory_print(Directory *directory) { printf("========================\n"); } -void Directory_append(Directory *directory, DirectoryRecord *record) { - int index = directory->records_length; - directory->records[index] = record; - directory->records_length += 1; - bptree_insert(directory->index, hash_string(record->phone_number), (uint64_t)index); +bool Directory_append(Directory *directory, DirectoryRecord *record) { + uint64_t a; + if (bptree_search(directory->index, hash_string(record->phone_number), &a)) { + return false; + } + + // Writes the record to the file. + FILE *fp; + fp = fopen(directory->database_filename, "a+b"); + + if (fp == NULL) { + exit(EXIT_FAILURE); + } + + ByteArray *byte_array = DirectoryRecord_to_ByteArray(record); + fwrite(byte_array->items, 1, byte_array->size, fp); + ByteArray_destroy(&byte_array); + + uint64_t data_ptr = (uint64_t)ftell(fp) - 55; + fclose(fp); + + bptree_insert(directory->index, hash_string(record->phone_number), data_ptr); + return true; } DirectoryRecord *Directory_search(Directory *directory, char phone_number[11]) { - uint64_t index; - if (bptree_search(directory->index, hash_string(phone_number), &index)) { - return directory->records[(int)index]; - } else { - return NULL; + uint64_t data_ptr; + if (bptree_search(directory->index, hash_string(phone_number), &data_ptr)) { + FILE *fp; + fp = fopen(directory->database_filename, "rb"); + + if (fp == NULL) { + exit(EXIT_FAILURE); + } + + fseek(fp, data_ptr, SEEK_SET); + ByteArray *byte_array = ByteArray_init(DirectoryRecord_size_on_disk()); + fread(byte_array->items, 1, DirectoryRecord_size_on_disk(), fp); + byte_array->size = DirectoryRecord_size_on_disk(); + DirectoryRecord *record = ByteArray_to_DirectoryRecord(byte_array); + ByteArray_destroy(&byte_array); + return record; } + + return NULL; } diff --git a/src/Directory.h b/src/Directory.h index 1245d91b09140d25fa6c80410f00dfa1d710d2d5..1d34825bad6f154b353dfa05a6cd7fc03836ef80 100644 --- a/src/Directory.h +++ b/src/Directory.h @@ -7,15 +7,14 @@ #define DEFAULT_ORDER 2 typedef struct Directory { - int records_length; - DirectoryRecord **records; + char database_filename[100]; BPTreeNode *index; } Directory; -Directory *Directory_init(); +Directory *Directory_init(char database_filename[100]); void Directory_destroy(Directory **directory); void Directory_print(Directory *directory); -void Directory_append(Directory *directory, DirectoryRecord *record); +bool Directory_append(Directory *directory, DirectoryRecord *record); DirectoryRecord *Directory_search(Directory *directory, char phone_number[11]); #endif diff --git a/src/DirectoryRecord.c b/src/DirectoryRecord.c index 4e16eb8c57de4ac8c504f9ab5e0ec90b5a346ed5..7ca5d49d72093921250c3110264b8db5142e27e0 100644 --- a/src/DirectoryRecord.c +++ b/src/DirectoryRecord.c @@ -1,15 +1,30 @@ #include "DirectoryRecord.h" #include <stdbool.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -DirectoryRecord *DirectoryRecord_init(bool deleted, char phone_number[11], char name[21], char surname[21], int birth_date_year, int birth_date_month, int birth_date_day) { +#include "Array.h" + +int DirectoryRecord_size_on_disk() { + int size = 1; + size += PHONE_NUMBER_MAX_LENGTH_WITHOUT_NULL_CHARACTER; + size += NAME_MAX_LENGTH_WITHOUT_NULL_CHARACTER; + size += SURNAME_MAX_LENGTH_WITHOUT_NULL_CHARACTER; + size += BIRTH_DATE_SIZE_IN_BYTES; + return size; +} + +DirectoryRecord *DirectoryRecord_init(bool deleted, char phone_number[PHONE_NUMBER_MAX_LENGTH], char name[NAME_MAX_LENGTH], char surname[SURNAME_MAX_LENGTH], int birth_date_year, int birth_date_month, int birth_date_day) { DirectoryRecord *record = (DirectoryRecord *)malloc(sizeof(DirectoryRecord)); record->deleted = deleted; + memset(record->phone_number, 0, PHONE_NUMBER_MAX_LENGTH); strcpy(record->phone_number, phone_number); + memset(record->name, 0, NAME_MAX_LENGTH); strcpy(record->name, name); + memset(record->surname, 0, SURNAME_MAX_LENGTH); strcpy(record->surname, surname); record->birth_date_year = birth_date_year; record->birth_date_month = birth_date_month; @@ -28,3 +43,67 @@ void DirectoryRecord_print(DirectoryRecord *record) { printf("Surname: %s\n", record->surname); printf("Birth Date: %d-%.2d-%.2d\n", record->birth_date_year, record->birth_date_month, record->birth_date_day); } + +ByteArray *DirectoryRecord_to_ByteArray(DirectoryRecord *record) { + int capacity = DirectoryRecord_size_on_disk(); + ByteArray *array = ByteArray_init(capacity); + ByteArray_append(array, (uint8_t)record->deleted); + + for (int j = 0; j < PHONE_NUMBER_MAX_LENGTH - 1; j++) { + ByteArray_append(array, (uint8_t)record->phone_number[j]); + } + + for (int j = 0; j < NAME_MAX_LENGTH - 1; j++) { + ByteArray_append(array, (uint8_t)record->name[j]); + } + + for (int j = 0; j < SURNAME_MAX_LENGTH - 1; j++) { + ByteArray_append(array, (uint8_t)record->surname[j]); + } + + // Birth Date : Year + ByteArray_append(array, (uint8_t)((record->birth_date_year >> 8) & 255)); + ByteArray_append(array, (uint8_t)(record->birth_date_year & 255)); + // Birth Date : Month + ByteArray_append(array, (uint8_t)record->birth_date_month); + // Birth Date : Day + ByteArray_append(array, (uint8_t)record->birth_date_day); + + return array; +} + +DirectoryRecord *ByteArray_to_DirectoryRecord(ByteArray *byte_array) { + int i = 0; + bool deleted = (bool)byte_array->items[i]; + i++; + + char phone_number[PHONE_NUMBER_MAX_LENGTH]; + phone_number[PHONE_NUMBER_MAX_LENGTH - 1] = '\0'; + + for (int j = 0; j < PHONE_NUMBER_MAX_LENGTH_WITHOUT_NULL_CHARACTER; j++) { + phone_number[j] = (char)byte_array->items[i]; + i++; + } + + char name[NAME_MAX_LENGTH]; + name[NAME_MAX_LENGTH - 1] = '\0'; + + for (int j = 0; j < NAME_MAX_LENGTH_WITHOUT_NULL_CHARACTER; j++) { + name[j] = (char)byte_array->items[i]; + i++; + } + + char surname[SURNAME_MAX_LENGTH]; + surname[SURNAME_MAX_LENGTH - 1] = '\0'; + + for (int j = 0; j < SURNAME_MAX_LENGTH_WITHOUT_NULL_CHARACTER; j++) { + surname[j] = (char)byte_array->items[i]; + i++; + } + + int birth_date_year = (int)byte_array->items[i] << 8 | (int)byte_array->items[i + 1]; + int birth_date_month = (int)byte_array->items[i + 2]; + int birth_date_day = (int)byte_array->items[i + 3]; + + return DirectoryRecord_init(deleted, phone_number, name, surname, birth_date_year, birth_date_month, birth_date_day); +} diff --git a/src/DirectoryRecord.h b/src/DirectoryRecord.h index 77a6898c8c4514875b717cc44070dfbc8f23baf0..2e84d1b27700bede8ce396e68d8554c457f1ace5 100644 --- a/src/DirectoryRecord.h +++ b/src/DirectoryRecord.h @@ -2,19 +2,37 @@ #define DIRECTORY_RECORD_H #include <stdbool.h> +#include <stdint.h> + +#include "Array.h" + +#define PHONE_NUMBER_MAX_LENGTH_WITHOUT_NULL_CHARACTER 10 +#define PHONE_NUMBER_MAX_LENGTH 1 + PHONE_NUMBER_MAX_LENGTH_WITHOUT_NULL_CHARACTER + +#define NAME_MAX_LENGTH_WITHOUT_NULL_CHARACTER 20 +#define NAME_MAX_LENGTH 1 + NAME_MAX_LENGTH_WITHOUT_NULL_CHARACTER + +#define SURNAME_MAX_LENGTH_WITHOUT_NULL_CHARACTER 20 +#define SURNAME_MAX_LENGTH 1 + SURNAME_MAX_LENGTH_WITHOUT_NULL_CHARACTER + +#define BIRTH_DATE_SIZE_IN_BYTES 4 typedef struct DirectoryRecord { bool deleted; - char phone_number[11]; - char name[21]; - char surname[21]; + char phone_number[PHONE_NUMBER_MAX_LENGTH]; + char name[NAME_MAX_LENGTH]; + char surname[SURNAME_MAX_LENGTH]; int birth_date_year; int birth_date_month; int birth_date_day; } DirectoryRecord; -DirectoryRecord *DirectoryRecord_init(bool deleted, char phone_number[11], char name[21], char surname[21], int birth_date_year, int birth_date_month, int birth_date_day); +int DirectoryRecord_size_on_disk(); + +DirectoryRecord *DirectoryRecord_init(bool deleted, char phone_number[PHONE_NUMBER_MAX_LENGTH], char name[NAME_MAX_LENGTH], char surname[SURNAME_MAX_LENGTH], int birth_date_year, int birth_date_month, int birth_date_day); void DirectoryRecord_destroy(DirectoryRecord **record); void DirectoryRecord_print(DirectoryRecord *record); +ByteArray *DirectoryRecord_to_ByteArray(DirectoryRecord *record); +DirectoryRecord *ByteArray_to_DirectoryRecord(ByteArray* byte_array); #endif diff --git a/src/main.c b/src/main.c index c8af2786a3d4471039f98d5fb257dddf4e33010d..a63f424ec0a47647cd2e88bca62c391bd5a5cb75 100644 --- a/src/main.c +++ b/src/main.c @@ -43,8 +43,14 @@ void append_record(Directory *directory) { printf("\n===>The procedure has been cancelled.\n"); } else { DirectoryRecord *record = DirectoryRecord_init(false, phone_number, name, surname, birth_date_year, birth_date_month, birth_date_day); - Directory_append(directory, record); - printf("\n===>The record has been successfully saved.\n"); + + if (Directory_append(directory, record)) { + printf("\n===>The record has been saved in the directory.\n"); + } else { + printf("\n===>This phone number is already associated with a record. The record has not been saved.\n"); + } + + DirectoryRecord_destroy(&record); } } @@ -58,23 +64,24 @@ void search_record(Directory *directory) { printf("\n"); if (record == NULL) { - printf("===>There are no records for this phone number.\n"); + printf("===>No records were found for this phone number.\n"); } else { printf("========================\n"); DirectoryRecord_print(record); + DirectoryRecord_destroy(&record); printf("========================\n"); } } int main() { - Directory *directory = Directory_init(); + Directory *directory = Directory_init("directory_database"); while (true) { - printf("Press 1 to add a member.\n"); - printf("Press 2 to search for a member via their phone number.\n"); - printf("Press 3 to delete a member.\n"); - printf("Press 4 to display all members.\n"); - printf("Press 5 to exit the program.\n"); + printf("Enter 1 to add a member.\n"); + printf("Enter 2 to search for a member via their phone number.\n"); + printf("Enter 3 to delete a member.\n"); + printf("Enter 4 to display all members.\n"); + printf("Enter 5 to exit the program.\n"); printf("What do you want to do? "); int action; scanf("%d", &action); @@ -110,10 +117,6 @@ int main() { system("clear"); } - // Directory_append(directory, DirectoryRecord_init(false, "0794592180", "Florian", "Burgener", 2000, 10, 03)); - // Directory_append(directory, DirectoryRecord_init(false, "0799494969", "Daisy", "Luna", 1995, 05, 31)); - // Directory_print(directory); - Directory_destroy(&directory); return EXIT_SUCCESS; }