← index
hashtable/open_address/oa-count.c
Source: C2x.Examples/src/hashtable/open_address/oa-count.c
#include "oa_hashtable.h"
#include <ctype.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static int compare_node_ptrs(const void *a, const void *b) {
    const struct Node *const *na = (const struct Node *const *)a;
    const struct Node *const *nb = (const struct Node *const *)b;
    return strcmp((*na)->text, (*nb)->text);
}

void print_sorted_word_table(const struct HashTable *table) {
    if (table == nullptr || table->slots == nullptr || table->bucket_size == 0)
        return;

    if (table->count == 0) {
        printf("Sorted Tokens: {\n}\n");
        return;
    }

    struct Node **nodes = malloc(sizeof(*nodes) * table->count);
    if (nodes == nullptr) {
        fprintf(stderr, "Error could not allocate sort buffer.\n");
        return;
    }

    size_t index = 0;
    for (size_t i = 0; i < table->bucket_size; ++i) {
        if (table->slots[i].state == SLOT_OCCUPIED) {
            nodes[index++] = &table->slots[i].node;
        }
    }

    qsort(nodes, index, sizeof(*nodes), compare_node_ptrs);

    printf("Sorted Tokens: {\n");
    for (size_t i = 0; i < index; ++i) {
        const struct Node *n = nodes[i];
        if (n->value != nullptr)
            printf("\t[%s] = %zu\n", n->text, *(size_t *)n->value);
    }
    printf("}\n");

    free(nodes);
}

void print_word_table(const struct HashTable *table) {
    if (table == nullptr || table->slots == nullptr || table->bucket_size == 0)
        return;

    printf("Tokens: {\n");
    for (size_t i = 0; i < table->bucket_size; ++i) {
        if (table->slots[i].state != SLOT_OCCUPIED)
            continue;

        const struct Node *n = &table->slots[i].node;
        if (n->value != nullptr)
            printf("\t[%s] = %zu\n", n->text, *(size_t *)n->value);
    }
    printf("}\n");
}

bool count_words(FILE *fptr, size_t *unique_count, size_t *token_count) {
    if (fptr == nullptr || token_count == nullptr || unique_count == nullptr)
        return false;

    *unique_count = 0;
    *token_count = 0;

    static constexpr size_t BUFFER_SIZE = 1024u * 4u;
    char buffer[BUFFER_SIZE];
    size_t count = 0;
    struct HashTable table;
    size_t bytes_read = 0;
    size_t word_size = BUFFER_SIZE;
    size_t index = 0;
    char *word = malloc(word_size + 1);
    if (word == nullptr) {
        fprintf(stderr, "Error out of memory\n");
        return false;
    }

    if (!hash_init(&table, DEFAULT_TABLE_SIZE)) {
        fprintf(stderr, "Failed to initialize hash table.\n");
        free(word);
        return false;
    }

    while ((bytes_read = fread(buffer, sizeof(buffer[0]), BUFFER_SIZE, fptr)) > 0) {
        for (size_t i = 0; i < bytes_read; ++i) {
            unsigned char c = (unsigned char)buffer[i];

            if (isalnum(c)) {
                if (index < word_size) {
                    word[index++] = (char)tolower(c);
                } else {
                    word_size *= 2;
                    char *temp = realloc(word, word_size + 1);
                    if (temp == nullptr) {
                        fprintf(stderr, "Error on allocation out of memory\n");
                        free(word);
                        hash_cleanup(&table);
                        return false;
                    }
                    word = temp;
                    word[index++] = (char)tolower(c);
                }
            } else {
                if (index > 0) {
                    word[index] = 0;
                    index = 0;
                    (*token_count)++;

                    struct Node *n = hash_insert(&table, word);
                    if (n == nullptr) {
                        fprintf(stderr, "Allocation failed.\n");
                        free(word);
                        hash_cleanup(&table);
                        return false;
                    }

                    if (n->value == nullptr) {
                        n->cleanup = cleanup_ptr;
                        n->value = malloc(sizeof(size_t));
                        if (n->value == nullptr) {
                            fprintf(stderr, "Allocation failed.\n");
                            free(word);
                            hash_cleanup(&table);
                            return false;
                        }
                        *(size_t *)n->value = 1;
                        count++;
                    } else {
                        (*(size_t *)n->value)++;
                    }

                    if (word_size > BUFFER_SIZE) {
                        free(word);
                        word_size = BUFFER_SIZE;
                        word = malloc(word_size + 1);
                        if (word == nullptr) {
                            fprintf(stderr, "Allocation failed.\n");
                            hash_cleanup(&table);
                            return false;
                        }
                    }
                }
            }
        }
    }

    if (ferror(fptr)) {
        fprintf(stderr, "Read error.\n");
        free(word);
        hash_cleanup(&table);
        return false;
    }

    if (index > 0) {
        word[index] = 0;
        (*token_count)++;

        struct Node *n = hash_insert(&table, word);
        if (n == nullptr) {
            fprintf(stderr, "Allocation failed.\n");
            free(word);
            hash_cleanup(&table);
            return false;
        }

        if (n->value == nullptr) {
            n->cleanup = cleanup_ptr;
            n->value = malloc(sizeof(size_t));
            if (n->value == nullptr) {
                fprintf(stderr, "Allocation failed.\n");
                free(word);
                hash_cleanup(&table);
                return false;
            }
            *(size_t *)n->value = 1;
            count++;
        } else {
            (*(size_t *)n->value)++;
        }
    }

    free(word);

    print_sorted_word_table(&table);
    hash_cleanup(&table);
    *unique_count = count;
    return true;
}

int main(int argc, char **argv) {
    if (argc <= 1) {
        size_t num = 0;
        size_t count = 0;

        if (count_words(stdin, &count, &num)) {
            printf("Contains: %zu tokens, %zu unique\n", num, count);
            return EXIT_SUCCESS;
        }

        fprintf(stderr, "Error has occurred.\n");
        return EXIT_FAILURE;
    }

    size_t total = 0;
    size_t unique_total = 0;
    int status = EXIT_SUCCESS;
    size_t errors = 0;

    for (int i = 1; i < argc; ++i) {
        FILE *fptr = fopen(argv[i], "rb");
        if (fptr == nullptr) {
            fprintf(stderr, "Could not open file: %s\n", argv[i]);
            status = EXIT_FAILURE;
            errors++;
            continue;
        }

        size_t num = 0;
        size_t count = 0;

        if (count_words(fptr, &count, &num)) {
            printf("%s Contains: %zu tokens, %zu unique\n", argv[i], num, count);
            total += num;
            unique_total += count;
        } else {
            fprintf(stderr, "Error has occurred.\n");
            status = EXIT_FAILURE;
            errors++;
        }

        fclose(fptr);
    }

    if (argc > 2)
        printf("%zu total tokens, %zu total unique\n", total, unique_total);

    if (errors > 0)
        fprintf(stderr, "Encountered %zu file errors.\n", errors);

    return status;
}