summaryrefslogtreecommitdiff
path: root/dseg.c
blob: 7236b888d1fc1edb7a179694318cbb9365ef78aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include "dseg.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define DSEG_PANIC(msg) {fprintf(stderr, "dseg error: " msg "\n"); exit(1);}
#define DEFAULT_SIZE 16
#define STRING_PATTERN "str%llu"

static integral_t string_counter = 0;

static integral_t hash_string(const char* str, integral_t cap) {
    integral_t hash = 0, i = 0;
    while (str[i] != 0) ADVANCE_HASH(hash, str[i++], cap);
    return hash;
}

static integral_t hash_ent(const struct dseg_entry* ent, integral_t cap) {
    switch (ent->type) {
        case ENT_STRING:
            return hash_string(ent->key.string, cap);
    }
    DSEG_PANIC("hash function not defined for entry type");
}

static bool ent_eq(const struct dseg_entry* a, const struct dseg_entry* b) {
    if (a->type != b->type) return false;
    switch (a->type) {
        case ENT_STRING:
            return strcmp(a->key.string, b->key.string) == 0;
    }
    DSEG_PANIC("equality function not defined for entry type");
}

static void ent_destroy(struct dseg_entry* entry) {
    free(entry->symbol);
}

void dseg_init(struct hash_map* dseg) {
    hm_init(
        dseg,
        (hm_hash_fn) hash_ent,
        (hm_eq_fn) ent_eq,
        (hm_destroy_fn) ent_destroy);
}

void dseg_destroy(struct hash_map* dseg) {
    hm_destroy(dseg);
}

static void ent_assign_key(struct dseg_entry* ent) {
    switch (ent->type) {
        case ENT_STRING:
            integral_t strnum = string_counter++;
            int req_sz = snprintf(NULL, 0, STRING_PATTERN, strnum);
            if (req_sz < 0) CCC_PANIC;
            req_sz += 1; // null terminator
            ent->symbol = ccc_alloc(req_sz);
            snprintf(ent->symbol, req_sz, STRING_PATTERN, strnum);
            break;
    }
}

const char* dseg_put(struct hash_map* dseg, struct dseg_entry entry) {
    struct dseg_entry** cell = (struct dseg_entry**) hm_cell_w(dseg, &entry);
    if (*cell != NULL) return (*cell)->symbol;
    struct dseg_entry* new_ent = ccc_alloc(sizeof(struct dseg_entry));
    *cell = new_ent;
    *new_ent = entry;
    ent_assign_key(new_ent);
    return new_ent->symbol;
}

static inline bool is_printable(char c) {
    return ' ' <= c && c <= '~';
}

void emit_string_data(FILE* outfile, const struct dseg_entry* ent) {
    const char* str = ent->key.string;
    fprintf(outfile, "db ");
    for (integral_t i = 0; str[i] != 0;) {
        if (is_printable(str[i])) {
            fprintf(outfile, "\"%c", str[i]);
            while (is_printable(str[++i])) fputc(str[i], outfile);
            fprintf(outfile, "\", ");
        } else {
            fprintf(outfile, "0x%x, ", str[i++]);
        }
    }
    fprintf(outfile, "0x0");
}

void emit_dseg(FILE* outfile, const struct hash_map* dseg) {
    fprintf(outfile, "section .data\n");
    for (integral_t i = 0; i < dseg->cap; i++) {
        const struct dseg_entry* ent = dseg->entries[i];
        if (ent == NULL) continue;

        fprintf(outfile, "\t%s ", ent->symbol);
        switch (ent->type) {
            case ENT_STRING:
                emit_string_data(outfile, ent);
                break;
        }
        fprintf(outfile, "\n");
    }
}