Skip to content
Snippets Groups Projects
Directory.c 4.50 KiB
// 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"

static uint64_t hash_string(char *str) {
    SHA256_CTX sha256;
    SHA256_Init(&sha256);
    SHA256_Update(&sha256, str, strlen(str));
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256_Final(hash, &sha256);

    uint64_t truncated_hash = 0;
    for (int i = 0; i < 8; i++) {
        truncated_hash |= hash[i];

        if (i != 7) {
            // No shift because it is the last byte.
            truncated_hash <<= 8;
        }
    }

    return truncated_hash;
}

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;
        }
    }

    fclose(fp);
}

Directory *Directory_init(char database_filename[100]) {
    Directory *directory = (Directory *)malloc(sizeof(Directory));
    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) {
    BPTree_destroy(&(*directory)->index);
    free(*directory);
    *directory = NULL;
}

void Directory_print(Directory *directory) {
    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");

    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 (ftell(fp) == file_size) {
            break;
        } else {
            printf("------------------------\n");
        }
    }

    printf("========================\n");
    fclose(fp);
}

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) - DirectoryRecord_size_on_disk();
    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 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();
        fclose(fp);

        DirectoryRecord *record = ByteArray_to_DirectoryRecord(byte_array);
        ByteArray_destroy(&byte_array);
        return record;
    }

    return NULL;
}