summaryrefslogtreecommitdiff
path: root/idreg.ha
blob: 1cdd74adf4130c0692029fe3e560a5584f9b0178 (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
use hash::fnv;
use htab;
use strings;
use types;

type IdRegistry = struct {
	idtoname: []str,
	nametoid: htab::table, // u32
};

def HTAB_EMPTY = htab::table {
	// XXX: harec limitation...
	table = &htab::empty,
	nslots = 1,
	...
};

def IDREG_EMPTY = IdRegistry {
	nametoid = HTAB_EMPTY,
	...
};

fn newidreg() IdRegistry =
	IdRegistry {
		idtoname = [],
		nametoid = htab::new(0, size(u32)),
	};

fn idreg_count(reg: *IdRegistry) u32 =
	len(reg.idtoname): u32;

fn idreg_exists(reg: *IdRegistry, id: u32) bool =
	id < len(reg.idtoname);

fn idreg_getname(reg: *IdRegistry, id: u32) str =
	reg.idtoname[id];

fn idreg_eqfunc(ctx: *opaque, entry: *opaque) bool = {
	const ctx = ctx: *(*IdRegistry, str);
	return ctx.1 == ctx.0.idtoname[*(entry: *u32)];
};

fn idreg_lookup(reg: *IdRegistry, name: str) (u32 | void) = {
	const hash = fnv::string64(name);
	match (htab::get(&reg.nametoid, hash,
		&idreg_eqfunc, &(reg, name), size(u32))) {
	case let entry: *opaque =>
		return *(entry: *u32);
	case null => void;
	};
};

fn idreg_register(reg: *IdRegistry, name: str) u32 = {
	assert(idreg_lookup(reg, name) is void, "already registered");
	assert(len(reg.idtoname) < types::U32_MAX, "registry full");

	const id = len(reg.idtoname): u32;
	append(reg.idtoname, strings::dup(name));

	const hash = fnv::string64(name);
	const entry = htab::add(&reg.nametoid, hash, size(u32));
	*(entry: *u32) = id;

	return id;
};

fn idreg_clear(reg: *IdRegistry) void = {
	strings::freeall(reg.idtoname);
	htab::finish(&reg.nametoid);
	reg.idtoname = [];
	reg.nametoid = htab::new(0, size(u32));
};