libutil

C Utility Library
git clone http://git.omkov.net/libutil
Log | Tree | Refs | README | LICENCE | Download

libutil/src/map.c (162 lines, 4.3 KiB) -rw-r--r-- file download

8a5f06f Jamozed 2022-02-09 20:04:50
0
// util/map.c, version 0.1.2
6217745 Jamozed 2021-12-08 20:00:24
1
// Map utility source file from libutil
6217745 Jamozed 2021-12-08 20:00:24
2
// Copyright (C) 2021, Jakob Wakeling
7f427d9 Jamozed 2022-03-06 12:55:13
3
// MIT Licence
6217745 Jamozed 2021-12-08 20:00:24
4
7af2131 Jamozed 2022-01-06 00:39:34
5
/*
7af2131 Jamozed 2022-01-06 00:39:34
6
	This file uses the currently non-standard 'typeof' operator. Its use is
7af2131 Jamozed 2022-01-06 00:39:34
7
	considered acceptable because it is supported by both GCC and Clang, and
7af2131 Jamozed 2022-01-06 00:39:34
8
	POSIX extensions are used here anyway. Additionally, it is expected that the
7af2131 Jamozed 2022-01-06 00:39:34
9
	'typeof' operator will become standard in C23.
7af2131 Jamozed 2022-01-06 00:39:34
10
*/
7af2131 Jamozed 2022-01-06 00:39:34
11
6217745 Jamozed 2021-12-08 20:00:24
12
#include "alloc.h"
6217745 Jamozed 2021-12-08 20:00:24
13
#include "map.h"
6217745 Jamozed 2021-12-08 20:00:24
14
#include "util.h"
6217745 Jamozed 2021-12-08 20:00:24
15
7af2131 Jamozed 2022-01-06 00:39:34
16
#include <stddef.h>
6217745 Jamozed 2021-12-08 20:00:24
17
#include <stdio.h>
6217745 Jamozed 2021-12-08 20:00:24
18
#include <stdlib.h>
6217745 Jamozed 2021-12-08 20:00:24
19
#include <string.h>
6217745 Jamozed 2021-12-08 20:00:24
20
7af2131 Jamozed 2022-01-06 00:39:34
21
#define LOAD_FACTOR 0.90
7af2131 Jamozed 2022-01-06 00:39:34
22
7af2131 Jamozed 2022-01-06 00:39:34
23
UINT map_initial_capacity = 64;
6217745 Jamozed 2021-12-08 20:00:24
24
6217745 Jamozed 2021-12-08 20:00:24
25
static void map_resize(map *m);
7af2131 Jamozed 2022-01-06 00:39:34
26
static u64  hash(const char *s, UINT l);
6217745 Jamozed 2021-12-08 20:00:24
27
6217745 Jamozed 2021-12-08 20:00:24
28
/* Initialise a map. */
7af2131 Jamozed 2022-01-06 00:39:34
29
map map_init(void (*free)(void *)) {
7af2131 Jamozed 2022-01-06 00:39:34
30
	return (map){ NULL, 0, 0, free };
6217745 Jamozed 2021-12-08 20:00:24
31
}
6217745 Jamozed 2021-12-08 20:00:24
32
7af2131 Jamozed 2022-01-06 00:39:34
33
/* Uninitialise a map. */
6217745 Jamozed 2021-12-08 20:00:24
34
void map_free(map *m) {
7af2131 Jamozed 2022-01-06 00:39:34
35
	for (UINT i = 0; i < m->ac; i += 1) {
7af2131 Jamozed 2022-01-06 00:39:34
36
		if (m->a[i].h == 0) { continue; } free(m->a[i].k);
7af2131 Jamozed 2022-01-06 00:39:34
37
		if (m->free != NULL) { m->free(m->a[i].v); }
6217745 Jamozed 2021-12-08 20:00:24
38
	}
6217745 Jamozed 2021-12-08 20:00:24
39
	
7af2131 Jamozed 2022-01-06 00:39:34
40
	free(m->a); *m = (map){ NULL, 0, 0, NULL };
6217745 Jamozed 2021-12-08 20:00:24
41
}
6217745 Jamozed 2021-12-08 20:00:24
42
7af2131 Jamozed 2022-01-06 00:39:34
43
#define SWAP(a, b) { register typeof (a) t = a; a = b; b = t; }
7af2131 Jamozed 2022-01-06 00:39:34
44
#define DIB(i) ((i + m->ac - (m->a[i].h % m->ac)) % m->ac)
7af2131 Jamozed 2022-01-06 00:39:34
45
7af2131 Jamozed 2022-01-06 00:39:34
46
/* Insert a key-value pair into a map. The key is duplicated. */
7af2131 Jamozed 2022-01-06 00:39:34
47
void map_insert(map *m, char *k, void *v) {
6217745 Jamozed 2021-12-08 20:00:24
48
	if (m->ac == 0 || m->al >= ((f64)m->ac * LOAD_FACTOR)) { map_resize(m); }
7af2131 Jamozed 2022-01-06 00:39:34
49
	UINT h = hash(k, strlen(k)), i = h % m->ac; k = strdup(k);
6217745 Jamozed 2021-12-08 20:00:24
50
	
7af2131 Jamozed 2022-01-06 00:39:34
51
	for (UINT dist = 0;; i = (i + 1) % m->ac, dist += 1) {
7af2131 Jamozed 2022-01-06 00:39:34
52
		if (m->a[i].h == 0) {
7af2131 Jamozed 2022-01-06 00:39:34
53
			/* If an empty bucket is found, insert here */
7af2131 Jamozed 2022-01-06 00:39:34
54
			m->a[i] = (typeof (*m->a)){ h, k, v };
7af2131 Jamozed 2022-01-06 00:39:34
55
			m->al += 1; return;
7af2131 Jamozed 2022-01-06 00:39:34
56
		}
7af2131 Jamozed 2022-01-06 00:39:34
57
		
7af2131 Jamozed 2022-01-06 00:39:34
58
		/* Calculate tsid, the DIB of the item at the current index */
7af2131 Jamozed 2022-01-06 00:39:34
59
		UINT tsid = (i + m->ac - (m->a[i].h % m->ac)) % m->ac;
7af2131 Jamozed 2022-01-06 00:39:34
60
		
7af2131 Jamozed 2022-01-06 00:39:34
61
		if (dist > tsid) {
7af2131 Jamozed 2022-01-06 00:39:34
62
			SWAP(m->a[i].h, h);
7af2131 Jamozed 2022-01-06 00:39:34
63
			SWAP(m->a[i].k, k);
7af2131 Jamozed 2022-01-06 00:39:34
64
			SWAP(m->a[i].v, v);
7af2131 Jamozed 2022-01-06 00:39:34
65
			
7af2131 Jamozed 2022-01-06 00:39:34
66
			dist = tsid;
7af2131 Jamozed 2022-01-06 00:39:34
67
		}
7af2131 Jamozed 2022-01-06 00:39:34
68
	}
6217745 Jamozed 2021-12-08 20:00:24
69
}
6217745 Jamozed 2021-12-08 20:00:24
70
7af2131 Jamozed 2022-01-06 00:39:34
71
/* Lookup the value associated with a key from a map. */
7af2131 Jamozed 2022-01-06 00:39:34
72
void *map_lookup(map *m, char *k) {
7af2131 Jamozed 2022-01-06 00:39:34
73
	UINT h = hash(k, strlen(k)), i = h % m->ac;
6217745 Jamozed 2021-12-08 20:00:24
74
	
7af2131 Jamozed 2022-01-06 00:39:34
75
	for (UINT dist = 0;; i = (i + 1) % m->ac, dist += 1) {
7af2131 Jamozed 2022-01-06 00:39:34
76
		if (m->a[i].h == 0) { return NULL; }
7af2131 Jamozed 2022-01-06 00:39:34
77
		
7af2131 Jamozed 2022-01-06 00:39:34
78
		if (dist > DIB(i)) { return NULL; }
7af2131 Jamozed 2022-01-06 00:39:34
79
		if ((m->a[i].h == h) && (strcmp(m->a[i].k, k) == 0)) {
7af2131 Jamozed 2022-01-06 00:39:34
80
			return m->a[i].v;
7af2131 Jamozed 2022-01-06 00:39:34
81
		}
7af2131 Jamozed 2022-01-06 00:39:34
82
	}
6217745 Jamozed 2021-12-08 20:00:24
83
}
6217745 Jamozed 2021-12-08 20:00:24
84
7af2131 Jamozed 2022-01-06 00:39:34
85
/* Remove a key-value pair from a map. */
7af2131 Jamozed 2022-01-06 00:39:34
86
void *map_remove(map *m, char *k) {
7af2131 Jamozed 2022-01-06 00:39:34
87
	UINT h = hash(k, strlen(k)), i = h % m->ac;
7af2131 Jamozed 2022-01-06 00:39:34
88
	
7af2131 Jamozed 2022-01-06 00:39:34
89
	for (UINT dist = 0;; i = (i + 1) % m->ac, dist += 1) {
7af2131 Jamozed 2022-01-06 00:39:34
90
		if (m->a[i].h == 0) { return NULL; }
7af2131 Jamozed 2022-01-06 00:39:34
91
		
7af2131 Jamozed 2022-01-06 00:39:34
92
		if (dist > DIB(i)) { return NULL; }
7af2131 Jamozed 2022-01-06 00:39:34
93
		if ((m->a[i].h == h) && (strcmp(m->a[i].k, k) == 0)) {
7af2131 Jamozed 2022-01-06 00:39:34
94
			/* If the element to be removed is found, then deallocate it */
7af2131 Jamozed 2022-01-06 00:39:34
95
			if (m->free != NULL) { m->free(m->a[i].v); } free(m->a[i].k);
7af2131 Jamozed 2022-01-06 00:39:34
96
			m->a[i] = (typeof (*m->a)){ 0, NULL, NULL }; m->al -= 1;
7af2131 Jamozed 2022-01-06 00:39:34
97
			
7af2131 Jamozed 2022-01-06 00:39:34
98
			/*  */
7af2131 Jamozed 2022-01-06 00:39:34
99
			for (UINT j = (i + 1) % m->ac;; i = j, j = (j + 1) % m->ac) {
7af2131 Jamozed 2022-01-06 00:39:34
100
				if (m->a[j].h == 0 || DIB(j) == 0) { break; }
7af2131 Jamozed 2022-01-06 00:39:34
101
				
7af2131 Jamozed 2022-01-06 00:39:34
102
				SWAP(m->a[i].h, m->a[j].h);
7af2131 Jamozed 2022-01-06 00:39:34
103
				SWAP(m->a[i].k, m->a[j].k);
7af2131 Jamozed 2022-01-06 00:39:34
104
				SWAP(m->a[i].v, m->a[j].v);
7af2131 Jamozed 2022-01-06 00:39:34
105
			}
7af2131 Jamozed 2022-01-06 00:39:34
106
			
7af2131 Jamozed 2022-01-06 00:39:34
107
			/*
7af2131 Jamozed 2022-01-06 00:39:34
108
				TODO I am unsure if I want to have this procedure return the
7af2131 Jamozed 2022-01-06 00:39:34
109
				removed value or simply an acknowledgement of its removal
7af2131 Jamozed 2022-01-06 00:39:34
110
			*/
7af2131 Jamozed 2022-01-06 00:39:34
111
			return (void *)true;
7af2131 Jamozed 2022-01-06 00:39:34
112
		}
7af2131 Jamozed 2022-01-06 00:39:34
113
	}
6217745 Jamozed 2021-12-08 20:00:24
114
}
6217745 Jamozed 2021-12-08 20:00:24
115
7af2131 Jamozed 2022-01-06 00:39:34
116
/* Print a basic representation of a map to stdout. */
6217745 Jamozed 2021-12-08 20:00:24
117
void map_print(map *m) {
7af2131 Jamozed 2022-01-06 00:39:34
118
	for (UINT i = 0; i < m->ac; i += 1) if (m->a[i].h != 0) {
7af2131 Jamozed 2022-01-06 00:39:34
119
		printf("%s -> %s\n", m->a[i].k, (char *)m->a[i].v);
6217745 Jamozed 2021-12-08 20:00:24
120
	}
6217745 Jamozed 2021-12-08 20:00:24
121
}
6217745 Jamozed 2021-12-08 20:00:24
122
7af2131 Jamozed 2022-01-06 00:39:34
123
/* Print a debug representation of a map to stdout. */
6217745 Jamozed 2021-12-08 20:00:24
124
void map_debug(map *m) {
7af2131 Jamozed 2022-01-06 00:39:34
125
	for (UINT i = 0; i < m->ac; i += 1) {
7af2131 Jamozed 2022-01-06 00:39:34
126
		if (m->a[i].h == 0) { printf("[%zu] %lu\n", i, m->a[i].h); }
7af2131 Jamozed 2022-01-06 00:39:34
127
		else printf(
7af2131 Jamozed 2022-01-06 00:39:34
128
			"[%zu] %lu, %s -> %s, DIB: %zu\n",
7af2131 Jamozed 2022-01-06 00:39:34
129
			i, m->a[i].h, m->a[i].k, (char *)m->a[i].v, DIB(i)
7af2131 Jamozed 2022-01-06 00:39:34
130
		);
7af2131 Jamozed 2022-01-06 00:39:34
131
	}
6217745 Jamozed 2021-12-08 20:00:24
132
}
6217745 Jamozed 2021-12-08 20:00:24
133
6217745 Jamozed 2021-12-08 20:00:24
134
/* Double the number of buckets in a map. */
6217745 Jamozed 2021-12-08 20:00:24
135
static void map_resize(map *m) {
7af2131 Jamozed 2022-01-06 00:39:34
136
	/* If the map is empty, simply allocate it without rehashing */
7af2131 Jamozed 2022-01-06 00:39:34
137
	if (m->ac == 0) {
7af2131 Jamozed 2022-01-06 00:39:34
138
		m->ac = map_initial_capacity;
7af2131 Jamozed 2022-01-06 00:39:34
139
		m->a = xcalloc(m->ac, sizeof (*m->a)); return;
7af2131 Jamozed 2022-01-06 00:39:34
140
	}
6217745 Jamozed 2021-12-08 20:00:24
141
	
6217745 Jamozed 2021-12-08 20:00:24
142
	/* Otherwise rehash every element into a new resized map */
7af2131 Jamozed 2022-01-06 00:39:34
143
	map old = *m; m->ac *= 2; m->a = xcalloc(m->ac, sizeof (*m->a)); m->al = 0;
7af2131 Jamozed 2022-01-06 00:39:34
144
	
7af2131 Jamozed 2022-01-06 00:39:34
145
	for (UINT i = 0; i < old.ac; i += 1) {
7af2131 Jamozed 2022-01-06 00:39:34
146
		if (old.a[i].h == 0) { continue; }
6217745 Jamozed 2021-12-08 20:00:24
147
		
7af2131 Jamozed 2022-01-06 00:39:34
148
		map_insert(m, old.a[i].k, old.a[i].v);
7af2131 Jamozed 2022-01-06 00:39:34
149
		free(old.a[i].k);
6217745 Jamozed 2021-12-08 20:00:24
150
	}
7af2131 Jamozed 2022-01-06 00:39:34
151
	
7af2131 Jamozed 2022-01-06 00:39:34
152
	free(old.a);
7af2131 Jamozed 2022-01-06 00:39:34
153
}
7af2131 Jamozed 2022-01-06 00:39:34
154
7af2131 Jamozed 2022-01-06 00:39:34
155
/* Compute the hash of some data. Will not return 0. */
7af2131 Jamozed 2022-01-06 00:39:34
156
static u64 hash(const char *dat, UINT len) {
7af2131 Jamozed 2022-01-06 00:39:34
157
	register u64 fnv = 0xCBF29CE484222325;
7af2131 Jamozed 2022-01-06 00:39:34
158
	for (; len; len -= 1, dat += 1) { fnv ^= *dat; fnv *= 0x00000100000001B3; }
7af2131 Jamozed 2022-01-06 00:39:34
159
	fnv |= fnv == 0; return fnv;
6217745 Jamozed 2021-12-08 20:00:24
160
}
161