Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
hashmap.h
Go to the documentation of this file.
1/* -*- C -*-
2 * Serene programming language
3 * Copyright (C) 2019-2026 Sameer Rahmani <[email protected]>
4 *
5 * This library is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21/// @file
22/// This is an implementation of Compressed Hash-Array Mapped Prefix-tree,
23/// which is a bit-partitioned, persistent, immutable Hashed array mapped trie.
24///
25/// For more information, have a look at the `Optimizing Hash-Array Mapped Tries
26/// for Fast and Lean Immutable JVM Collections` paper.
27///
28///
29/// TL;DR:
30/// - Every struct is immutable
31/// - Nodes are persistent and immutable.
32/// - Nodes are heap allocated
33/// - Heads can be stack allocated
34/// - We create a new head for any change and probably will create inner nodes
35/// as well depending on the change
36/// - New heads will share structure with the older version
37/// - This is not a ordered hashmap
38/// - The hashing function HAS TO BE DETERMINISTIC
39/// - Don't use pointers value as the data to hash, use the data that they
40/// point
41/// to instead.
42
43#include <stddef.h>
44#include <stdint.h>
45
46#include "serene/rt/errors.h"
47
48typedef struct srn_context_t srn_context_t;
49
50// -----------------------------------------------------------------------------
51// Tunables
52// -----------------------------------------------------------------------------
53/// branching factor (power of two)
54#define HMAP_BR 32u
55#define HMAP_SHIFT 5u
56#define HMAP_MASK (HMAP_BR - 1u)
57#define HMAP_HASH_SIZE 32u ///< By default we use 32bit hashes
58#define HMAP_HASH_TYPE uint32_t
59#define HMAP_BITMAP_TYPE uint32_t
60
63typedef void *hmap_data_t;
64
65/// Note: For key equality we use the memcpy function
66typedef struct hmap_key_t {
67 /// len 0 -> data == nullptr
68 void *data;
69 size_t len;
71
77
78typedef struct hmap_node_t {
81 /// This is really a (void **) which each entry in the array
82 /// will be either (mutually exclusive):
83 /// - A pointer to a hmap_kv_entry_t (or a collision node), which mean the
84 /// actual key/value is in that slot.
85 /// - A pointer to the next hmap_node_t (a child of the current node)
86 /// that is determined by the corresponding bit in the `datamap` and `nodemap`
87 /// bitmaps.
88 ///
89 /// For example, if datamap is `0b...110` and nodemap is `0b...001` then
90 /// the first entry, is a child node (a pointer to hmap_node_t) and second and
91 /// third entries are pointers to `hmap_kv_entry_t` or simply the key/value
94
95typedef struct hmap_t {
96 /// Number of key/value pairs in the map
97 size_t len;
99 /// The context that owns every allocation the map retains. `hmap_empty`
100 /// sets it and every version returned by `hmap_insert` inherits it.
101 /// Inserts allocate trie nodes, kv entries, and copied keys in this
102 /// context's block chain, never in the inserting caller's, so the map
103 /// stays valid for as long as the pinned context lives.
106
107/// If we ever want to modify some of these behaviours for a new instance
108/// of hashmap, we should use this control type.
109typedef struct hmap_control_t {
110 bool (*cmp)(const hmap_t *hmap, const hmap_key_t *a, const hmap_key_t *b);
111 hmap_hash_t (*hash)(const hmap_t *hmap, const hmap_key_t *k);
112 hmap_t (*insert)(const struct hmap_control_t *ctl, const hmap_t *hmap, hmap_key_t *k, void *v);
113 void *(*lookup)(
114 const struct hmap_control_t *ctl, const hmap_t *hmap, const hmap_key_t *k, void *default_value
115 );
116 /// Clone `k` into the map's `map_ctx` before the map retains it, so the
117 /// map never holds a pointer into the inserting caller's block. Every
118 /// control must provide it.
119 hmap_key_t *(*copy_key)(const hmap_t *hmap, const hmap_key_t *k);
120
122
124
125/// Create, initialize and return a new hashmap pinned to `ctx`. The pinned
126/// context owns every allocation the map (and all of its future versions)
127/// retains, so pick a context that lives at least as long as the map.
128[[nodiscard]] [[gnu::nonnull(1)]]
130
131/// Insert the given key `k` with the value `v` in the given hash `hmap` and
132/// return the new map.
133/// The new map will share parts of its structure with the old map and never
134/// mutate the old map.
135/// All retained allocations go through the map's `map_ctx`. The control's
136/// `copy_key` clones the key into it, so `k` may point at stack or transient
137/// memory. The value `v` is stored as is and must outlive the map.
138[[nodiscard]] [[gnu::nonnull(1, 2)]]
139hmap_t hmap_insert(const hmap_t *hmap, hmap_key_t *k, void *v);
140
141/// Just like the `hmap_insert` function but, it receives a `hmap_control_t` to
142/// customize the comparison and hashing function.
143[[nodiscard]] [[gnu::nonnull(1, 2, 3)]]
144hmap_t hmap_insert_ctl(const hmap_control_t *ctl, const hmap_t *hmap, hmap_key_t *k, void *v);
145
146/// Lookup the given `k` in the given `hmap` and return the value if it's been
147/// found. Otherwise return the `default_value` value. Since a `nullptr` is a
148/// valid value for any keys, returning a `nullptr` is not a good option to
149/// indicate that the key does not exist, So it's safer to use explicit values
150/// to differenciate between a missing key and a legit nullptr as a value.
151[[nodiscard]] [[gnu::nonnull(1, 2)]]
152void *hmap_lookup(const hmap_t *hmap, const hmap_key_t *k, void *default_value);
153
154/// Just like the `hmap_lookup` function but, it receives a `hmap_control_t` to
155/// customize the comparison and hashing function.
156void *hmap_lookup_ctl(
157 const hmap_control_t *ctl, const hmap_t *hmap, const hmap_key_t *k, void *default_value
158);
159/// This is a simple hack to mock the hash function during tests to force a high
160/// collision hashing function. Outside of tests, this will be a wrapper around
161/// `srn_hash`.
162[[gnu::nonnull(1)]]
163hmap_hash_t hmap_hash(const srn_context_t *ctx, const void *data, size_t len);
164
165/// Create a new key out of the given `data`, with the given len.
166[[gnu::nonnull(1, 2)]]
167hmap_key_t *hmap_make_key(srn_context_t *ctx, void *data, size_t len);
168
169/// The default `copy_key` hook. Clone the key struct and its bytes into the
170/// map's `map_ctx` so the map never retains pointers into a caller's block.
171[[gnu::nonnull(1, 2)]]
172hmap_key_t *hmap_copy_key(const hmap_t *hmap, const hmap_key_t *k);
Error handling for the runtime.
hmap_control_t hmap_default_control
Definition hashmap.c:690
#define HMAP_BITMAP_TYPE
Definition hashmap.h:59
void * hmap_data_t
Definition hashmap.h:63
hmap_t hmap_insert(const hmap_t *hmap, hmap_key_t *k, void *v)
Insert the given key k with the value v in the given hash hmap and return the new map.
Definition hashmap.c:661
#define HMAP_HASH_TYPE
Definition hashmap.h:58
hmap_hash_t hmap_hash(const srn_context_t *ctx, const void *data, size_t len)
This is a simple hack to mock the hash function during tests to force a high collision hashing functi...
Definition hashmap.c:77
hmap_t hmap_empty(const srn_context_t *ctx)
Create, initialize and return a new hashmap pinned to ctx.
Definition hashmap.c:655
struct hmap_t hmap_t
hmap_key_t * hmap_make_key(srn_context_t *ctx, void *data, size_t len)
Create a new key out of the given data, with the given len.
Definition hashmap.c:669
HMAP_BITMAP_TYPE hmap_bitmap_t
Definition hashmap.h:62
void * hmap_lookup_ctl(const hmap_control_t *ctl, const hmap_t *hmap, const hmap_key_t *k, void *default_value)
Just like the hmap_lookup function but, it receives a hmap_control_t to customize the comparison and ...
Definition hashmap.c:638
hmap_t hmap_insert_ctl(const hmap_control_t *ctl, const hmap_t *hmap, hmap_key_t *k, void *v)
Just like the hmap_insert function but, it receives a hmap_control_t to customize the comparison and ...
Definition hashmap.c:594
HMAP_HASH_TYPE hmap_hash_t
Definition hashmap.h:61
hmap_key_t * hmap_copy_key(const hmap_t *hmap, const hmap_key_t *k)
The default copy_key hook.
Definition hashmap.c:678
void * hmap_lookup(const hmap_t *hmap, const hmap_key_t *k, void *default_value)
Lookup the given k in the given hmap and return the value if it's been found.
Definition hashmap.c:665
If we ever want to modify some of these behaviours for a new instance of hashmap, we should use this ...
Definition hashmap.h:109
hmap_t(* insert)(const struct hmap_control_t *ctl, const hmap_t *hmap, hmap_key_t *k, void *v)
Definition hashmap.h:112
bool(* cmp)(const hmap_t *hmap, const hmap_key_t *a, const hmap_key_t *b)
Definition hashmap.h:110
hmap_hash_t(* hash)(const hmap_t *hmap, const hmap_key_t *k)
Definition hashmap.h:111
Note: For key equality we use the memcpy function.
Definition hashmap.h:66
void * data
len 0 -> data == nullptr
Definition hashmap.h:68
size_t len
Definition hashmap.h:69
Definition hashmap.h:72
hmap_key_t * k
Definition hashmap.h:74
void * v
Definition hashmap.h:75
hmap_hash_t hash
Definition hashmap.h:73
hmap_data_t * entries
This is really a (void **) which each entry in the array will be either (mutually exclusive):
Definition hashmap.h:92
hmap_bitmap_t datamap
Definition hashmap.h:79
hmap_bitmap_t nodemap
Definition hashmap.h:80
const srn_context_t * map_ctx
The context that owns every allocation the map retains.
Definition hashmap.h:104
hmap_node_t * root
Definition hashmap.h:98
size_t len
Number of key/value pairs in the map.
Definition hashmap.h:97