/* * par2serial-cc: utils.c - Common utilities implementation */ #include "utils.h" /* ══════════════════════════════════════════════════════════ * Memory Arena * ══════════════════════════════════════════════════════════ */ static ArenaBlock *arena_new_block(size_t min_size) { size_t sz = min_size > ARENA_BLOCK_SIZE ? min_size : ARENA_BLOCK_SIZE; ArenaBlock *b = (ArenaBlock *)malloc(sizeof(ArenaBlock) + sz); if (!b) { fprintf(stderr, "OOM in arena\n"); exit(1); } b->next = NULL; b->size = sz; b->used = 0; return b; } Arena *arena_create(void) { Arena *a = (Arena *)calloc(1, sizeof(Arena)); a->head = a->current = arena_new_block(ARENA_BLOCK_SIZE); a->total_alloc = 0; return a; } void *arena_alloc(Arena *a, size_t size) { /* align to 8 bytes */ size = (size + 7) & ~(size_t)7; if (a->current->used + size > a->current->size) { ArenaBlock *nb = arena_new_block(size); a->current->next = nb; a->current = nb; } void *p = a->current->data + a->current->used; a->current->used += size; a->total_alloc += size; return p; } char *arena_strdup(Arena *a, const char *s) { size_t len = strlen(s); char *p = (char *)arena_alloc(a, len + 1); memcpy(p, s, len + 1); return p; } char *arena_strndup(Arena *a, const char *s, size_t n) { char *p = (char *)arena_alloc(a, n + 1); memcpy(p, s, n); p[n] = '\0'; return p; } void arena_destroy(Arena *a) { ArenaBlock *b = a->head; while (b) { ArenaBlock *next = b->next; free(b); b = next; } free(a); } /* ══════════════════════════════════════════════════════════ * String Buffer * ══════════════════════════════════════════════════════════ */ void strbuf_init(StrBuf *sb) { sb->data = NULL; sb->len = 0; sb->cap = 0; } static void strbuf_grow(StrBuf *sb, size_t need) { if (sb->len + need + 1 > sb->cap) { sb->cap = sb->cap ? sb->cap * 2 : 256; while (sb->cap < sb->len + need + 1) sb->cap *= 2; sb->data = (char *)realloc(sb->data, sb->cap); if (!sb->data) { fprintf(stderr, "OOM in strbuf\n"); exit(1); } } } void strbuf_append(StrBuf *sb, const char *fmt, ...) { va_list ap; va_start(ap, fmt); /* probe length */ va_list ap2; va_copy(ap2, ap); int n = vsnprintf(NULL, 0, fmt, ap2); va_end(ap2); if (n < 0) { va_end(ap); return; } strbuf_grow(sb, (size_t)n); vsnprintf(sb->data + sb->len, (size_t)n + 1, fmt, ap); sb->len += (size_t)n; va_end(ap); } void strbuf_append_char(StrBuf *sb, char c) { strbuf_grow(sb, 1); sb->data[sb->len++] = c; sb->data[sb->len] = '\0'; } void strbuf_append_indent(StrBuf *sb, int indent) { for (int i = 0; i < indent; i++) strbuf_append(sb, " "); } char *strbuf_detach(StrBuf *sb) { char *s = sb->data; sb->data = NULL; sb->len = sb->cap = 0; return s; } void strbuf_free(StrBuf *sb) { free(sb->data); sb->data = NULL; sb->len = sb->cap = 0; } /* ══════════════════════════════════════════════════════════ * Error Reporting * ══════════════════════════════════════════════════════════ */ static int error_count = 0; static int warn_count = 0; void p2s_error(SourceLoc loc, const char *fmt, ...) { fprintf(stderr, "\033[1;31merror\033[0m"); if (loc.filename) fprintf(stderr, " [%s:%d:%d]", loc.filename, loc.line, loc.col); fprintf(stderr, ": "); va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); error_count++; } void p2s_warn(SourceLoc loc, const char *fmt, ...) { fprintf(stderr, "\033[1;33mwarning\033[0m"); if (loc.filename) fprintf(stderr, " [%s:%d:%d]", loc.filename, loc.line, loc.col); fprintf(stderr, ": "); va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); warn_count++; } void p2s_note(const char *fmt, ...) { fprintf(stderr, "\033[1;36mnote\033[0m: "); va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); } void p2s_fatal(const char *fmt, ...) { fprintf(stderr, "\033[1;31mfatal\033[0m: "); va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); exit(1); } /* ══════════════════════════════════════════════════════════ * Hash Map (separate chaining) * ══════════════════════════════════════════════════════════ */ static uint32_t hash_str(const char *s) { uint32_t h = 5381; while (*s) h = h * 33 + (unsigned char)*s++; return h; } HashMap *hashmap_create(void) { HashMap *m = (HashMap *)calloc(1, sizeof(HashMap)); m->cap = HASHMAP_INIT_CAP; m->buckets = (HashEntry **)calloc(m->cap, sizeof(HashEntry *)); m->len = 0; return m; } void hashmap_put(HashMap *m, const char *key, void *val) { uint32_t idx = hash_str(key) % m->cap; /* check existing */ for (HashEntry *e = m->buckets[idx]; e; e = e->next) { if (strcmp(e->key, key) == 0) { e->val = val; return; } } /* insert */ HashEntry *e = (HashEntry *)malloc(sizeof(HashEntry)); e->key = strdup(key); e->val = val; e->next = m->buckets[idx]; m->buckets[idx] = e; m->len++; /* rehash at 75% load */ if (m->len > m->cap * 3 / 4) { size_t newcap = m->cap * 2; HashEntry **newb = (HashEntry **)calloc(newcap, sizeof(HashEntry *)); for (size_t i = 0; i < m->cap; i++) { HashEntry *cur = m->buckets[i]; while (cur) { HashEntry *next = cur->next; uint32_t ni = hash_str(cur->key) % newcap; cur->next = newb[ni]; newb[ni] = cur; cur = next; } } free(m->buckets); m->buckets = newb; m->cap = newcap; } } void *hashmap_get(HashMap *m, const char *key) { uint32_t idx = hash_str(key) % m->cap; for (HashEntry *e = m->buckets[idx]; e; e = e->next) if (strcmp(e->key, key) == 0) return e->val; return NULL; } bool hashmap_has(HashMap *m, const char *key) { uint32_t idx = hash_str(key) % m->cap; for (HashEntry *e = m->buckets[idx]; e; e = e->next) if (strcmp(e->key, key) == 0) return true; return false; } void hashmap_destroy(HashMap *m) { for (size_t i = 0; i < m->cap; i++) { HashEntry *e = m->buckets[i]; while (e) { HashEntry *next = e->next; free(e->key); free(e); e = next; } } free(m->buckets); free(m); } /* ══════════════════════════════════════════════════════════ * File I/O * ══════════════════════════════════════════════════════════ */ char *read_file(const char *path, size_t *out_len) { FILE *f = fopen(path, "rb"); if (!f) { fprintf(stderr, "Cannot open file: %s\n", path); return NULL; } fseek(f, 0, SEEK_END); long len = ftell(f); fseek(f, 0, SEEK_SET); char *buf = (char *)malloc((size_t)len + 1); if (!buf) { fclose(f); return NULL; } fread(buf, 1, (size_t)len, f); buf[len] = '\0'; fclose(f); if (out_len) *out_len = (size_t)len; return buf; }