par2serial-cc / src /utils.c
clarenceleo's picture
Add utils.c implementation
20a991d verified
Raw
History Blame Contribute Delete
8.53 kB
/*
* 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;
}