summaryrefslogtreecommitdiff
path: root/lib9p/map.h
blob: f42bb2cd6ac30f25514c867321d62afddf6dad66 (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
107
108
109
110
111
112
113
114
115
116
/* lib9p/map.h - A really dumb map/dict data structur
 *
 * Copyright (C) 2024  Luke T. Shumaker <lukeshu@lukeshu.com>
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

#include "internal.h"

/**
 * `#define` `NAME`, `KEY_T`, `VAL_T`, and `CAP`; then `#include
 * "map.h".
 */

#ifndef NAME
	#error NAME must be defined
#endif
#ifndef KEY_T
	#error KEY_T must be defined
#endif
#ifndef VAL_T
	#error VAL_T must be defined
#endif
#ifndef CAP
	#error CAP must be defined
#endif

#ifndef MAP_KEY
#define MAP_KV(TNAME)            CAT3(_,TNAME,_kv)
#define MAP_METHOD(TNAME, MNAME) CAT3(TNAME,_,MNAME)
#define MAP_FOREACH(m, k, v) \
	for (size_t i = 0; i < ARRAY_LEN((m)->items); i++) \
		if ( ({ k = (m)->items[i].key; v = &(m)->items[i].val; (m)->items[i].set; }) )
#endif

/* This implementation is just an array that we brute-force search
 * over for a slot.  I don't want to use the heap, which means
 * statically-sized maps, and I'm probably going to choose a low
 * static size, so this is fine.  */

struct MAP_KV(NAME) {
	bool                    set;
	KEY_T                   key;
	VAL_T                   val;
};

struct NAME {
	size_t                  len;
	struct MAP_KV(NAME)     items[CAP];
};

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"

/**
 * Load an item from the map; return a pointer to the in-map value, or
 * NULL if the item is not in the map.
 */
static VAL_T *MAP_METHOD(NAME,load)(struct NAME *m, KEY_T k) {
	if (!m->len)
		return NULL;
	for (size_t i = 0; i < ARRAY_LEN(m->items); i++)
		if (m->items[i].set && m->items[i].key == k)
			return &(m->items[i].val);
	return NULL;
}

/**
 * Store an item into the map, perhaps replacing an existing value.
 * Return a pointer to the in-map value, or NULL if the map is full.
 */
static VAL_T *MAP_METHOD(NAME,store)(struct NAME *m, KEY_T k, VAL_T v) {
	VAL_T *old = MAP_METHOD(NAME,load)(m, k);
	if (old) {
		*old = v;
		return old;
	}
	if (m->len == ARRAY_LEN(m->items))
		return NULL;
	for (size_t i = 0; i < ARRAY_LEN(m->items); i++)
		if (!m->items[i].set) {
			m->len++;
			m->items[i].set = true;
			m->items[i].key = k;
			m->items[i].val = v;
			return &(m->items[i].val);
		}
	assert_notreached("should have returned from inside for() loop");
}

/**
 * Delete an item from the map.  Returns true if an item was deleted,
 * false if no such item was in the map.
 */
static bool MAP_METHOD(NAME,del)(struct NAME *m, KEY_T k) {
	if (!m->len)
		return NULL;
	for (size_t i = 0; i < ARRAY_LEN(m->items); i++)
		if (m->items[i].set && m->items[i].key == k) {
			m->items[i].set = false;
			m->len--;
			return true;
		}
	return false;
}

#pragma GCC diagnostic pop

#undef NAME
#undef KEY_T
#undef VAL_T
#undef CAP

/* Keep the linter happy.  */
#ifndef _LIB9P_MAP_H_
#define _LIB9P_MAP_H_
#endif /* _LIB9P_MAP_H_ */