#include "dseg.h" #include #include #include #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; void dseg_init(struct dseg* dseg) { dseg->entries = ccc_alloc(DEFAULT_SIZE * sizeof(struct dseg_entry*)); dseg->cap = DEFAULT_SIZE; } static void dseg_entry_destroy(struct dseg_entry* entry) { free(entry->symbol); } void dseg_destroy(struct dseg* dseg) { for (integral_t i = 0; i < dseg->cap; i++) { if (dseg->entries[i] == NULL) continue; dseg_entry_destroy(dseg->entries[i]); free(dseg->entries[i]); } free(dseg->entries); } static inline integral_t advance_hash( integral_t hash, integral_t val, integral_t cap ) { return ((hash << 5) - hash + val) % cap;; } static integral_t hash_string(const char* str, integral_t cap) { integral_t hash = 0, i = 0; while (str[i] != 0) hash = 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_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; } } static struct dseg_entry** dseg_cell( const struct dseg* dseg, const struct dseg_entry* ent ) { integral_t orig_idx = hash_ent(ent, dseg->cap); integral_t idx = orig_idx; do { if (dseg->entries[idx] == NULL || ent_eq(dseg->entries[idx], ent)) return &dseg->entries[idx]; } while ((idx = (idx + 1) % dseg->cap) != orig_idx); return NULL; } static void rehash_dseg(struct dseg* dseg) { struct dseg_entry** old_ents = dseg->entries; integral_t old_cap = dseg->cap; dseg->cap = (dseg->cap + 1) << 1; dseg->entries = ccc_alloc(dseg->cap * sizeof(struct dseg_entry*)); for (integral_t i = 0; i < old_cap; i++) { if (old_ents[i] == NULL) continue; struct dseg_entry** cell = dseg_cell(dseg, old_ents[i]); if (cell == NULL) { fprintf(stderr, "ccc: data segment rehash failed, likely a bug\n"); exit(1); } *cell = old_ents[i]; } free(old_ents); } const char* dseg_put(struct dseg* dseg, struct dseg_entry entry) { struct dseg_entry** cell = dseg_cell(dseg, &entry); while (cell == NULL) { rehash_dseg(dseg); cell = dseg_cell(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(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 dseg* dseg) { fprintf(outfile, "section .data\n"); for (integral_t i = 0; i < dseg->cap; i++) { const struct dseg_entry* ent = dseg->entries[i]; if (dseg->entries[i] == NULL) continue; fprintf(outfile, "\t%s ", ent->symbol); switch (ent->type) { case ENT_STRING: emit_string(outfile, ent); break; } fprintf(outfile, "\n"); } }