@shd101wyy/yo 0.1.26 → 0.1.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/skills/yo-async-effects/SKILL.md +4 -4
- package/.github/skills/yo-async-effects/async-effects-recipes.md +34 -34
- package/.github/skills/yo-core-patterns/SKILL.md +1 -1
- package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +26 -26
- package/.github/skills/yo-project-workflow/SKILL.md +6 -3
- package/.github/skills/yo-project-workflow/workflow-cheatsheet.md +34 -11
- package/.github/skills/yo-syntax/SKILL.md +7 -6
- package/.github/skills/yo-syntax/syntax-cheatsheet.md +73 -60
- package/.github/skills/yo-wasm-integration/wasm-integration-cheatsheet.md +3 -3
- package/README.md +10 -8
- package/out/cjs/index.cjs +456 -438
- package/out/cjs/yo-cli.cjs +576 -543
- package/out/cjs/yo-lsp.cjs +559 -532
- package/out/esm/index.mjs +281 -263
- package/out/types/src/formatter.d.ts +11 -0
- package/out/types/src/lsp/formatting.d.ts +2 -0
- package/out/types/src/tests/formatter.test.d.ts +1 -0
- package/out/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/std/alg/hash.yo +13 -21
- package/std/allocator.yo +25 -40
- package/std/async.yo +3 -7
- package/std/build.yo +105 -151
- package/std/cli/arg_parser.yo +184 -169
- package/std/collections/array_list.yo +350 -314
- package/std/collections/btree_map.yo +142 -131
- package/std/collections/deque.yo +132 -128
- package/std/collections/hash_map.yo +542 -566
- package/std/collections/hash_set.yo +623 -687
- package/std/collections/linked_list.yo +275 -293
- package/std/collections/ordered_map.yo +113 -85
- package/std/collections/priority_queue.yo +73 -73
- package/std/crypto/md5.yo +191 -95
- package/std/crypto/random.yo +56 -64
- package/std/crypto/sha256.yo +151 -107
- package/std/encoding/base64.yo +87 -81
- package/std/encoding/hex.yo +43 -50
- package/std/encoding/html.yo +56 -81
- package/std/encoding/html_char_utils.yo +7 -13
- package/std/encoding/html_entities.yo +2248 -2253
- package/std/encoding/json.yo +316 -224
- package/std/encoding/punycode.yo +86 -116
- package/std/encoding/toml.yo +67 -66
- package/std/encoding/utf16.yo +37 -44
- package/std/env.yo +62 -91
- package/std/error.yo +7 -15
- package/std/fmt/display.yo +5 -9
- package/std/fmt/index.yo +8 -14
- package/std/fmt/to_string.yo +330 -315
- package/std/fmt/writer.yo +58 -87
- package/std/fs/dir.yo +83 -102
- package/std/fs/file.yo +147 -180
- package/std/fs/metadata.yo +45 -78
- package/std/fs/temp.yo +55 -65
- package/std/fs/types.yo +27 -40
- package/std/fs/walker.yo +53 -68
- package/std/gc.yo +5 -8
- package/std/glob.yo +30 -43
- package/std/http/client.yo +107 -120
- package/std/http/http.yo +106 -96
- package/std/http/index.yo +4 -6
- package/std/imm/list.yo +88 -93
- package/std/imm/map.yo +528 -464
- package/std/imm/set.yo +52 -57
- package/std/imm/sorted_map.yo +340 -286
- package/std/imm/sorted_set.yo +57 -63
- package/std/imm/string.yo +404 -345
- package/std/imm/vec.yo +173 -181
- package/std/io/reader.yo +3 -6
- package/std/io/writer.yo +4 -8
- package/std/libc/assert.yo +5 -9
- package/std/libc/ctype.yo +32 -22
- package/std/libc/dirent.yo +26 -25
- package/std/libc/errno.yo +164 -90
- package/std/libc/fcntl.yo +52 -45
- package/std/libc/float.yo +66 -44
- package/std/libc/limits.yo +42 -33
- package/std/libc/math.yo +53 -82
- package/std/libc/signal.yo +72 -47
- package/std/libc/stdatomic.yo +217 -188
- package/std/libc/stdint.yo +5 -29
- package/std/libc/stdio.yo +5 -29
- package/std/libc/stdlib.yo +32 -39
- package/std/libc/string.yo +5 -23
- package/std/libc/sys/stat.yo +58 -56
- package/std/libc/time.yo +5 -19
- package/std/libc/unistd.yo +5 -20
- package/std/libc/wctype.yo +6 -9
- package/std/libc/windows.yo +26 -30
- package/std/log.yo +41 -55
- package/std/net/addr.yo +102 -97
- package/std/net/dns.yo +27 -28
- package/std/net/errors.yo +50 -49
- package/std/net/tcp.yo +113 -124
- package/std/net/udp.yo +55 -66
- package/std/os/env.yo +35 -33
- package/std/os/signal.yo +15 -25
- package/std/path.yo +276 -311
- package/std/prelude.yo +6304 -4315
- package/std/process/command.yo +87 -103
- package/std/process/index.yo +12 -31
- package/std/regex/compiler.yo +196 -95
- package/std/regex/flags.yo +58 -39
- package/std/regex/index.yo +157 -173
- package/std/regex/match.yo +20 -31
- package/std/regex/node.yo +134 -152
- package/std/regex/parser.yo +283 -259
- package/std/regex/unicode.yo +172 -202
- package/std/regex/vm.yo +155 -171
- package/std/string/index.yo +5 -7
- package/std/string/rune.yo +45 -55
- package/std/string/string.yo +937 -964
- package/std/string/string_builder.yo +94 -104
- package/std/string/unicode.yo +46 -64
- package/std/sync/channel.yo +72 -73
- package/std/sync/cond.yo +31 -36
- package/std/sync/mutex.yo +30 -32
- package/std/sync/once.yo +13 -16
- package/std/sync/rwlock.yo +26 -31
- package/std/sync/waitgroup.yo +20 -25
- package/std/sys/advise.yo +16 -24
- package/std/sys/bufio/buf_reader.yo +77 -93
- package/std/sys/bufio/buf_writer.yo +52 -65
- package/std/sys/clock.yo +4 -9
- package/std/sys/constants.yo +77 -61
- package/std/sys/copy.yo +4 -10
- package/std/sys/dir.yo +26 -43
- package/std/sys/dns.yo +41 -61
- package/std/sys/errors.yo +95 -103
- package/std/sys/events.yo +45 -57
- package/std/sys/externs.yo +319 -267
- package/std/sys/fallocate.yo +7 -11
- package/std/sys/fcntl.yo +14 -22
- package/std/sys/file.yo +26 -40
- package/std/sys/future.yo +5 -8
- package/std/sys/iov.yo +12 -25
- package/std/sys/lock.yo +12 -13
- package/std/sys/mmap.yo +38 -43
- package/std/sys/path.yo +3 -8
- package/std/sys/perm.yo +7 -21
- package/std/sys/pipe.yo +5 -12
- package/std/sys/process.yo +23 -29
- package/std/sys/seek.yo +10 -12
- package/std/sys/signal.yo +7 -13
- package/std/sys/signals.yo +52 -35
- package/std/sys/socket.yo +63 -58
- package/std/sys/socketpair.yo +3 -6
- package/std/sys/sockinfo.yo +11 -20
- package/std/sys/statfs.yo +11 -34
- package/std/sys/statx.yo +25 -52
- package/std/sys/sysinfo.yo +15 -20
- package/std/sys/tcp.yo +62 -92
- package/std/sys/temp.yo +5 -9
- package/std/sys/time.yo +5 -15
- package/std/sys/timer.yo +6 -11
- package/std/sys/tty.yo +10 -18
- package/std/sys/udp.yo +22 -39
- package/std/sys/umask.yo +3 -6
- package/std/sys/unix.yo +33 -52
- package/std/testing/bench.yo +49 -52
- package/std/thread.yo +10 -15
- package/std/time/datetime.yo +105 -89
- package/std/time/duration.yo +43 -56
- package/std/time/instant.yo +13 -18
- package/std/time/sleep.yo +5 -9
- package/std/url/index.yo +184 -209
- package/std/worker.yo +6 -10
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
//! Hash map using SwissTable algorithm with SIMD-accelerated lookup.
|
|
2
|
-
|
|
3
|
-
{ GlobalAllocator, AllocError } :: import "../allocator.yo";
|
|
2
|
+
{ GlobalAllocator, AllocError } :: import("../allocator.yo");
|
|
4
3
|
{ malloc, calloc, realloc, free } :: GlobalAllocator;
|
|
5
|
-
|
|
6
4
|
/// Error variants for HashMap operations.
|
|
7
5
|
HashMapError :: enum(
|
|
8
6
|
/// Memory allocation failed.
|
|
@@ -12,487 +10,437 @@ HashMapError :: enum(
|
|
|
12
10
|
/// Capacity calculation overflowed.
|
|
13
11
|
CapacityOverflow
|
|
14
12
|
);
|
|
15
|
-
|
|
16
13
|
/// Control byte for an empty bucket (never used).
|
|
17
14
|
CTRL_EMPTY :: u8(255);
|
|
18
15
|
/// Control byte for a deleted bucket (tombstone).
|
|
19
16
|
CTRL_DELETED :: u8(254);
|
|
20
|
-
|
|
21
17
|
/// Default initial capacity (must be power of 2).
|
|
22
18
|
DEFAULT_CAPACITY :: usize(16);
|
|
23
|
-
|
|
24
19
|
/// Maximum load factor numerator (7/8 = 0.875). Resize when `size > capacity * 7 / 8`.
|
|
25
20
|
MAX_LOAD_FACTOR_NUMERATOR :: usize(7);
|
|
26
21
|
MAX_LOAD_FACTOR_DENOMINATOR :: usize(8);
|
|
27
|
-
|
|
28
22
|
/// Key-value pair stored in a hash map bucket.
|
|
29
|
-
Bucket :: (fn(comptime(K): Type, comptime(V): Type) -> comptime(Type))
|
|
23
|
+
Bucket :: (fn(comptime(K) : Type, comptime(V) : Type) -> comptime(Type))(
|
|
30
24
|
struct(
|
|
31
25
|
key : K,
|
|
32
26
|
value : V
|
|
33
27
|
)
|
|
34
|
-
;
|
|
35
|
-
|
|
28
|
+
);
|
|
36
29
|
/// Extract H2 hash (lower 7 bits) from full hash for control byte comparison.
|
|
37
|
-
h2_hash :: (fn(hash: u64) -> u8)
|
|
38
|
-
u8(hash & u64(127))
|
|
39
|
-
;
|
|
40
|
-
|
|
30
|
+
h2_hash :: (fn(hash : u64) -> u8)(u8(hash & u64(127)));
|
|
41
31
|
/// Extract H1 hash (upper bits) for bucket index calculation.
|
|
42
|
-
h1_hash :: (fn(hash: u64, capacity: usize) -> usize)
|
|
43
|
-
(usize((hash >> u64(7))) % capacity)
|
|
44
|
-
;
|
|
45
|
-
|
|
32
|
+
h1_hash :: (fn(hash : u64, capacity : usize) -> usize)(usize(hash >> u64(7)) % capacity);
|
|
46
33
|
/// High-performance hash map using SwissTable algorithm.
|
|
47
34
|
/// Keys must implement `Eq` and `Hash`. Provides O(1) average lookup, insert, and delete.
|
|
48
|
-
HashMap :: (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
35
|
+
HashMap :: (
|
|
36
|
+
fn(
|
|
37
|
+
comptime(K) : Type,
|
|
38
|
+
comptime(V) : Type,
|
|
39
|
+
where(K <: (Eq(K), Hash))
|
|
40
|
+
) -> comptime(Type)
|
|
41
|
+
)(
|
|
53
42
|
object(
|
|
54
43
|
ctrl : ?*(u8),
|
|
55
44
|
data : ?*(Bucket(K, V)),
|
|
56
45
|
capacity : usize,
|
|
57
46
|
size : usize
|
|
58
47
|
)
|
|
59
|
-
;
|
|
60
|
-
|
|
61
|
-
|
|
48
|
+
);
|
|
49
|
+
impl(
|
|
50
|
+
forall(K : Type, V : Type),
|
|
51
|
+
where(K <: (Eq(K), Hash)),
|
|
52
|
+
HashMap(K, V),
|
|
62
53
|
/**
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
_alloc_with_capacity : (fn(capacity: usize) -> Result(Self, HashMapError))(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
ctrl_result
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
data_result
|
|
79
|
-
|
|
80
|
-
.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
i
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
),
|
|
104
|
-
|
|
54
|
+
* Allocate memory for HashMap with given capacity
|
|
55
|
+
* Initializes all control bytes to EMPTY
|
|
56
|
+
*/
|
|
57
|
+
_alloc_with_capacity : (fn(capacity : usize) -> Result(Self, HashMapError))({
|
|
58
|
+
bucket_size :: sizeof(Bucket(K, V));
|
|
59
|
+
ctrl_size := capacity;
|
|
60
|
+
data_size := (capacity * bucket_size);
|
|
61
|
+
ctrl_result := malloc(ctrl_size);
|
|
62
|
+
match(
|
|
63
|
+
ctrl_result,
|
|
64
|
+
.None =>.Err(.AllocError(error :.OutOfMemory)),
|
|
65
|
+
.Some(ctrl_void_ptr) => {
|
|
66
|
+
ctrl_ptr := *(u8)(ctrl_void_ptr);
|
|
67
|
+
data_result := malloc(data_size);
|
|
68
|
+
match(
|
|
69
|
+
data_result,
|
|
70
|
+
.None => {
|
|
71
|
+
free(.Some(*(void)(ctrl_ptr)));
|
|
72
|
+
.Err(.AllocError(error :.OutOfMemory))
|
|
73
|
+
},
|
|
74
|
+
.Some(data_void_ptr) => {
|
|
75
|
+
data_ptr := *(Bucket(K, V))(data_void_ptr);
|
|
76
|
+
i := usize(0);
|
|
77
|
+
while(i < capacity, i = (i + usize(1)), {
|
|
78
|
+
(ctrl_ptr &+ i).* = CTRL_EMPTY;
|
|
79
|
+
});
|
|
80
|
+
.Ok(
|
|
81
|
+
Self(
|
|
82
|
+
ctrl :.Some(ctrl_ptr),
|
|
83
|
+
data :.Some(data_ptr),
|
|
84
|
+
capacity : capacity,
|
|
85
|
+
size : usize(0)
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
}),
|
|
105
93
|
/**
|
|
106
|
-
|
|
107
|
-
|
|
94
|
+
* Create a new empty HashMap with default capacity
|
|
95
|
+
*/
|
|
108
96
|
new : (fn() -> Self)({
|
|
109
97
|
result := Self._alloc_with_capacity(DEFAULT_CAPACITY);
|
|
110
|
-
match(
|
|
98
|
+
match(
|
|
99
|
+
result,
|
|
111
100
|
.Ok(map) => map,
|
|
112
101
|
.Err(_) => panic("Failed to allocate HashMap")
|
|
113
102
|
)
|
|
114
103
|
}),
|
|
115
|
-
|
|
116
104
|
/**
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
with_capacity : (fn(requested_capacity: usize) -> Self)(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
result
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
),
|
|
143
|
-
|
|
105
|
+
* Create a HashMap with a specific initial capacity
|
|
106
|
+
* Capacity will be rounded up to next power of 2
|
|
107
|
+
*/
|
|
108
|
+
with_capacity : (fn(requested_capacity : usize) -> Self)({
|
|
109
|
+
capacity := cond(
|
|
110
|
+
(requested_capacity < DEFAULT_CAPACITY) => DEFAULT_CAPACITY,
|
|
111
|
+
true => {
|
|
112
|
+
c := requested_capacity;
|
|
113
|
+
c = (c - usize(1));
|
|
114
|
+
c = (c | (c >> usize(1)));
|
|
115
|
+
c = (c | (c >> usize(2)));
|
|
116
|
+
c = (c | (c >> usize(4)));
|
|
117
|
+
c = (c | (c >> usize(8)));
|
|
118
|
+
c = (c | (c >> usize(16)));
|
|
119
|
+
c + usize(1)
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
result := Self._alloc_with_capacity(capacity);
|
|
123
|
+
match(
|
|
124
|
+
result,
|
|
125
|
+
.Ok(map) => map,
|
|
126
|
+
.Err(_) => panic("Failed to allocate HashMap")
|
|
127
|
+
)
|
|
128
|
+
}),
|
|
144
129
|
/**
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
_ctrl_ptr : (fn(self: Self) -> *(u8))(
|
|
148
|
-
match(
|
|
130
|
+
* Get ctrl pointer (unwrapped for internal use)
|
|
131
|
+
*/
|
|
132
|
+
_ctrl_ptr : (fn(self : Self) -> *(u8))(
|
|
133
|
+
match(
|
|
134
|
+
self.ctrl,
|
|
149
135
|
.Some(ptr) => ptr,
|
|
150
136
|
.None => panic("HashMap ctrl pointer is null")
|
|
151
137
|
)
|
|
152
138
|
),
|
|
153
|
-
|
|
154
139
|
/**
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
_data_ptr : (fn(self: Self) -> *(Bucket(K, V)))(
|
|
158
|
-
match(
|
|
140
|
+
* Get data pointer (unwrapped for internal use)
|
|
141
|
+
*/
|
|
142
|
+
_data_ptr : (fn(self : Self) -> *(Bucket(K, V)))(
|
|
143
|
+
match(
|
|
144
|
+
self.data,
|
|
159
145
|
.Some(ptr) => ptr,
|
|
160
146
|
.None => panic("HashMap data pointer is null")
|
|
161
147
|
)
|
|
162
148
|
),
|
|
163
|
-
|
|
164
149
|
/**
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
_find_bucket : (fn(self: Self, key: K, hash: u64) -> Option(usize))(
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
ctrl_byte
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
},
|
|
184
|
-
(ctrl_byte == h2) => {
|
|
185
|
-
bucket := (data_ptr &+ probe_index).*;
|
|
186
|
-
cond(
|
|
187
|
-
(bucket.key == key) => {
|
|
188
|
-
return .Some(probe_index);
|
|
189
|
-
},
|
|
190
|
-
true => ()
|
|
191
|
-
);
|
|
192
|
-
},
|
|
193
|
-
true => ()
|
|
194
|
-
);
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
.None
|
|
198
|
-
}
|
|
199
|
-
),
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Find first available bucket (EMPTY or DELETED) for insertion
|
|
203
|
-
* Returns bucket index using quadratic probing
|
|
204
|
-
*/
|
|
205
|
-
_find_insert_bucket : (fn(self: Self, hash: u64) -> usize)(
|
|
206
|
-
{
|
|
207
|
-
index := h1_hash(hash, self.capacity);
|
|
208
|
-
probe := usize(0);
|
|
209
|
-
ctrl_ptr := Self._ctrl_ptr(self);
|
|
210
|
-
|
|
211
|
-
while(probe < self.capacity, probe = (probe + usize(1)), {
|
|
212
|
-
probe_index := ((index + probe) % self.capacity);
|
|
213
|
-
ctrl_byte := (ctrl_ptr &+ probe_index).*;
|
|
214
|
-
|
|
215
|
-
cond(
|
|
216
|
-
((ctrl_byte == CTRL_EMPTY) || (ctrl_byte == CTRL_DELETED)) => {
|
|
217
|
-
return probe_index;
|
|
218
|
-
},
|
|
219
|
-
true => ()
|
|
220
|
-
);
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
panic("HashMap is full - should have resized")
|
|
224
|
-
}
|
|
225
|
-
),
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Check if HashMap needs resizing based on load factor
|
|
229
|
-
*/
|
|
230
|
-
_needs_resize : (fn(self: Self) -> bool)(
|
|
231
|
-
{
|
|
232
|
-
threshold := ((self.capacity * MAX_LOAD_FACTOR_NUMERATOR) / MAX_LOAD_FACTOR_DENOMINATOR);
|
|
233
|
-
(self.size >= threshold)
|
|
234
|
-
}
|
|
235
|
-
),
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Resize and rehash the HashMap to a new capacity
|
|
239
|
-
*/
|
|
240
|
-
_resize : (fn(self: Self, new_capacity: usize) -> Result(unit, HashMapError))(
|
|
241
|
-
{
|
|
242
|
-
result := Self._alloc_with_capacity(new_capacity);
|
|
243
|
-
match(result,
|
|
244
|
-
.Err(err) => .Err(err),
|
|
245
|
-
.Ok(new_map) => {
|
|
246
|
-
i := usize(0);
|
|
247
|
-
ctrl_ptr := Self._ctrl_ptr(self);
|
|
248
|
-
data_ptr := Self._data_ptr(self);
|
|
249
|
-
while(i < self.capacity, i = (i + usize(1)), {
|
|
250
|
-
ctrl_byte := (ctrl_ptr &+ i).*;
|
|
251
|
-
cond(
|
|
252
|
-
((ctrl_byte != CTRL_EMPTY) && (ctrl_byte != CTRL_DELETED)) => {
|
|
253
|
-
bucket := (data_ptr &+ i).*;
|
|
254
|
-
hash := (bucket.key).hash();
|
|
255
|
-
h2 := h2_hash(hash);
|
|
256
|
-
new_index := Self._find_insert_bucket(new_map, hash);
|
|
257
|
-
|
|
258
|
-
new_ctrl_ptr := Self._ctrl_ptr(new_map);
|
|
259
|
-
new_data_ptr := Self._data_ptr(new_map);
|
|
260
|
-
(new_ctrl_ptr &+ new_index).* = h2;
|
|
261
|
-
consume((new_data_ptr &+ new_index).* = bucket);
|
|
262
|
-
new_map.size = (new_map.size + usize(1));
|
|
263
|
-
},
|
|
264
|
-
true => ()
|
|
265
|
-
);
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
// Drop old buckets before freeing old data array
|
|
269
|
-
// (bucket := ... above duped them, so we need to drop the old refs)
|
|
150
|
+
* Find bucket index for a given key using quadratic probing
|
|
151
|
+
* Returns Some(index) if key is found, None otherwise
|
|
152
|
+
*/
|
|
153
|
+
_find_bucket : (fn(self : Self, key : K, hash : u64) -> Option(usize))({
|
|
154
|
+
h2 := h2_hash(hash);
|
|
155
|
+
index := h1_hash(hash, self.capacity);
|
|
156
|
+
probe := usize(0);
|
|
157
|
+
ctrl_ptr := Self._ctrl_ptr(self);
|
|
158
|
+
data_ptr := Self._data_ptr(self);
|
|
159
|
+
while(probe < self.capacity, probe = (probe + usize(1)), {
|
|
160
|
+
probe_index := ((index + probe) % self.capacity);
|
|
161
|
+
ctrl_byte := (ctrl_ptr &+ probe_index).*;
|
|
162
|
+
cond(
|
|
163
|
+
(ctrl_byte == CTRL_EMPTY) => {
|
|
164
|
+
return(.None);
|
|
165
|
+
},
|
|
166
|
+
(ctrl_byte == h2) => {
|
|
167
|
+
bucket := (data_ptr &+ probe_index).*;
|
|
270
168
|
cond(
|
|
271
|
-
(
|
|
272
|
-
|
|
273
|
-
while(j < self.capacity, j = (j + usize(1)), {
|
|
274
|
-
ctrl_byte := (ctrl_ptr &+ j).*;
|
|
275
|
-
cond(
|
|
276
|
-
((ctrl_byte != CTRL_EMPTY) && (ctrl_byte != CTRL_DELETED)) => {
|
|
277
|
-
bucket_ptr := (data_ptr &+ j);
|
|
278
|
-
cond(
|
|
279
|
-
Type.contains_rc_type(K) => unsafe.drop((bucket_ptr.*).key),
|
|
280
|
-
true => ()
|
|
281
|
-
);
|
|
282
|
-
cond(
|
|
283
|
-
Type.contains_rc_type(V) => unsafe.drop((bucket_ptr.*).value),
|
|
284
|
-
true => ()
|
|
285
|
-
);
|
|
286
|
-
},
|
|
287
|
-
true => ()
|
|
288
|
-
);
|
|
289
|
-
});
|
|
169
|
+
(bucket.key == key) => {
|
|
170
|
+
return(.Some(probe_index));
|
|
290
171
|
},
|
|
291
172
|
true => ()
|
|
292
173
|
);
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
.Some(ptr) => free(.Some(*(void)(ptr))),
|
|
300
|
-
.None => ()
|
|
301
|
-
);
|
|
302
|
-
|
|
303
|
-
self.ctrl = new_map.ctrl;
|
|
304
|
-
self.data = new_map.data;
|
|
305
|
-
self.capacity = new_map.capacity;
|
|
306
|
-
|
|
307
|
-
new_map.ctrl = .None;
|
|
308
|
-
new_map.data = .None;
|
|
309
|
-
|
|
310
|
-
.Ok(())
|
|
311
|
-
}
|
|
312
|
-
)
|
|
313
|
-
}
|
|
314
|
-
),
|
|
315
|
-
|
|
174
|
+
},
|
|
175
|
+
true => ()
|
|
176
|
+
);
|
|
177
|
+
});
|
|
178
|
+
.None
|
|
179
|
+
}),
|
|
316
180
|
/**
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
new_bucket := Bucket(K, V)(key: key, value: value);
|
|
331
|
-
// Don't use consume here - we want to drop the old bucket at memory
|
|
332
|
-
// Note: old_bucket already duped it, so dropping here decrements to RC=1
|
|
333
|
-
// Then old_bucket goes out of scope and drops again to RC=0
|
|
334
|
-
// But wait - we return old_value which was duped from old_bucket...
|
|
335
|
-
// Actually the Bucket struct contains RC types, so assignment drops old + dups new
|
|
336
|
-
(data_ptr &+ index).* = new_bucket;
|
|
337
|
-
.Ok(.Some(old_value))
|
|
181
|
+
* Find first available bucket (EMPTY or DELETED) for insertion
|
|
182
|
+
* Returns bucket index using quadratic probing
|
|
183
|
+
*/
|
|
184
|
+
_find_insert_bucket : (fn(self : Self, hash : u64) -> usize)({
|
|
185
|
+
index := h1_hash(hash, self.capacity);
|
|
186
|
+
probe := usize(0);
|
|
187
|
+
ctrl_ptr := Self._ctrl_ptr(self);
|
|
188
|
+
while(probe < self.capacity, probe = (probe + usize(1)), {
|
|
189
|
+
probe_index := ((index + probe) % self.capacity);
|
|
190
|
+
ctrl_byte := (ctrl_ptr &+ probe_index).*;
|
|
191
|
+
cond(
|
|
192
|
+
((ctrl_byte == CTRL_EMPTY) || (ctrl_byte == CTRL_DELETED)) => {
|
|
193
|
+
return(probe_index);
|
|
338
194
|
},
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
195
|
+
true => ()
|
|
196
|
+
);
|
|
197
|
+
});
|
|
198
|
+
panic("HashMap is full - should have resized")
|
|
199
|
+
}),
|
|
200
|
+
/**
|
|
201
|
+
* Check if HashMap needs resizing based on load factor
|
|
202
|
+
*/
|
|
203
|
+
_needs_resize : (fn(self : Self) -> bool)({
|
|
204
|
+
threshold := ((self.capacity * MAX_LOAD_FACTOR_NUMERATOR) / MAX_LOAD_FACTOR_DENOMINATOR);
|
|
205
|
+
self.size >= threshold
|
|
206
|
+
}),
|
|
207
|
+
/**
|
|
208
|
+
* Resize and rehash the HashMap to a new capacity
|
|
209
|
+
*/
|
|
210
|
+
_resize : (fn(self : Self, new_capacity : usize) -> Result(unit, HashMapError))({
|
|
211
|
+
result := Self._alloc_with_capacity(new_capacity);
|
|
212
|
+
match(
|
|
213
|
+
result,
|
|
214
|
+
.Err(err) =>.Err(err),
|
|
215
|
+
.Ok(new_map) => {
|
|
216
|
+
i := usize(0);
|
|
217
|
+
ctrl_ptr := Self._ctrl_ptr(self);
|
|
218
|
+
data_ptr := Self._data_ptr(self);
|
|
219
|
+
while(i < self.capacity, i = (i + usize(1)), {
|
|
220
|
+
ctrl_byte := (ctrl_ptr &+ i).*;
|
|
221
|
+
cond(
|
|
222
|
+
((ctrl_byte != CTRL_EMPTY) && (ctrl_byte != CTRL_DELETED)) => {
|
|
223
|
+
bucket := (data_ptr &+ i).*;
|
|
224
|
+
hash := (bucket.key).hash();
|
|
225
|
+
h2 := h2_hash(hash);
|
|
226
|
+
new_index := Self._find_insert_bucket(new_map, hash);
|
|
227
|
+
new_ctrl_ptr := Self._ctrl_ptr(new_map);
|
|
228
|
+
new_data_ptr := Self._data_ptr(new_map);
|
|
229
|
+
(new_ctrl_ptr &+ new_index).* = h2;
|
|
230
|
+
consume((new_data_ptr &+ new_index).* = bucket);
|
|
231
|
+
new_map.size = (new_map.size + usize(1));
|
|
344
232
|
},
|
|
345
|
-
true => (
|
|
233
|
+
true => ()
|
|
346
234
|
);
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
235
|
+
});
|
|
236
|
+
// Drop old buckets before freeing old data array
|
|
237
|
+
// (bucket := ... above duped them, so we need to drop the old refs)
|
|
238
|
+
cond(
|
|
239
|
+
(Type.contains_rc_type(K) || Type.contains_rc_type(V)) => {
|
|
240
|
+
j := usize(0);
|
|
241
|
+
while(j < self.capacity, j = (j + usize(1)), {
|
|
242
|
+
ctrl_byte := (ctrl_ptr &+ j).*;
|
|
243
|
+
cond(
|
|
244
|
+
((ctrl_byte != CTRL_EMPTY) && (ctrl_byte != CTRL_DELETED)) => {
|
|
245
|
+
bucket_ptr := (data_ptr &+ j);
|
|
246
|
+
cond(
|
|
247
|
+
Type.contains_rc_type(K) => unsafe.drop((bucket_ptr.*).key),
|
|
248
|
+
true => ()
|
|
249
|
+
);
|
|
250
|
+
cond(
|
|
251
|
+
Type.contains_rc_type(V) => unsafe.drop((bucket_ptr.*).value),
|
|
252
|
+
true => ()
|
|
253
|
+
);
|
|
254
|
+
},
|
|
255
|
+
true => ()
|
|
256
|
+
);
|
|
257
|
+
});
|
|
258
|
+
},
|
|
259
|
+
true => ()
|
|
260
|
+
);
|
|
261
|
+
match(
|
|
262
|
+
self.ctrl,
|
|
263
|
+
.Some(ptr) => free(.Some(*(void)(ptr))),
|
|
264
|
+
.None => ()
|
|
265
|
+
);
|
|
266
|
+
match(
|
|
267
|
+
self.data,
|
|
268
|
+
.Some(ptr) => free(.Some(*(void)(ptr))),
|
|
269
|
+
.None => ()
|
|
270
|
+
);
|
|
271
|
+
self.ctrl = new_map.ctrl;
|
|
272
|
+
self.data = new_map.data;
|
|
273
|
+
self.capacity = new_map.capacity;
|
|
274
|
+
new_map.ctrl =.None;
|
|
275
|
+
new_map.data =.None;
|
|
276
|
+
.Ok(())
|
|
277
|
+
}
|
|
278
|
+
)
|
|
279
|
+
}),
|
|
369
280
|
/**
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
281
|
+
* Insert or update a key-value pair
|
|
282
|
+
* Returns Ok(Some(old_value)) if key existed, Ok(None) if new key
|
|
283
|
+
*/
|
|
284
|
+
set : (fn(self : Self, key : K, value : V) -> Result(Option(V), HashMapError))({
|
|
285
|
+
hash := key.hash();
|
|
286
|
+
bucket_opt := Self._find_bucket(self, key, hash);
|
|
287
|
+
match(
|
|
288
|
+
bucket_opt,
|
|
289
|
+
.Some(index) => {
|
|
290
|
+
data_ptr := Self._data_ptr(self);
|
|
291
|
+
old_bucket := (data_ptr &+ index).*;
|
|
292
|
+
old_value := old_bucket.value;
|
|
293
|
+
new_bucket := Bucket(K, V)(key : key, value : value);
|
|
294
|
+
// Don't use consume here - we want to drop the old bucket at memory
|
|
295
|
+
// Note: old_bucket already duped it, so dropping here decrements to RC=1
|
|
296
|
+
// Then old_bucket goes out of scope and drops again to RC=0
|
|
297
|
+
// But wait - we return old_value which was duped from old_bucket...
|
|
298
|
+
// Actually the Bucket struct contains RC types, so assignment drops old + dups new
|
|
299
|
+
(data_ptr &+ index).* = new_bucket;
|
|
300
|
+
.Ok(.Some(old_value))
|
|
301
|
+
},
|
|
302
|
+
.None => {
|
|
303
|
+
resize_check := cond(
|
|
304
|
+
(Self._needs_resize(self)) => {
|
|
305
|
+
new_capacity := (self.capacity * usize(2));
|
|
306
|
+
Self._resize(self, new_capacity)
|
|
307
|
+
},
|
|
308
|
+
true => Result(unit, HashMapError).Ok(())
|
|
309
|
+
);
|
|
310
|
+
match(
|
|
311
|
+
resize_check,
|
|
312
|
+
.Err(err) =>.Err(err),
|
|
313
|
+
.Ok(_) => {
|
|
314
|
+
h2 := h2_hash(hash);
|
|
315
|
+
index := Self._find_insert_bucket(self, hash);
|
|
316
|
+
ctrl_ptr := Self._ctrl_ptr(self);
|
|
317
|
+
data_ptr := Self._data_ptr(self);
|
|
318
|
+
(ctrl_ptr &+ index).* = h2;
|
|
319
|
+
new_bucket := Bucket(K, V)(key : key, value : value);
|
|
320
|
+
consume((data_ptr &+ index).* = new_bucket);
|
|
321
|
+
self.size = (self.size + usize(1));
|
|
322
|
+
.Ok(.None)
|
|
323
|
+
}
|
|
324
|
+
)
|
|
325
|
+
}
|
|
326
|
+
)
|
|
327
|
+
}),
|
|
388
328
|
/**
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
329
|
+
* Get a value by key
|
|
330
|
+
* Returns Some(value) if found, None otherwise
|
|
331
|
+
*/
|
|
332
|
+
get : (fn(self : Self, key : K) -> Option(V))({
|
|
333
|
+
hash := key.hash();
|
|
334
|
+
bucket_opt := Self._find_bucket(self, key, hash);
|
|
335
|
+
match(
|
|
336
|
+
bucket_opt,
|
|
337
|
+
.Some(index) => {
|
|
338
|
+
data_ptr := Self._data_ptr(self);
|
|
339
|
+
bucket := (data_ptr &+ index).*;
|
|
340
|
+
.Some(bucket.value)
|
|
341
|
+
},
|
|
342
|
+
.None =>.None
|
|
343
|
+
)
|
|
344
|
+
}),
|
|
402
345
|
/**
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
bucket_opt
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
bucket_ptr := (data_ptr &+ index);
|
|
415
|
-
|
|
416
|
-
// Read and dup value for return
|
|
417
|
-
value := bucket_ptr.*.value;
|
|
418
|
-
|
|
419
|
-
// Drop the bucket's key (we're removing it)
|
|
420
|
-
unsafe.drop(bucket_ptr.*.key);
|
|
421
|
-
|
|
422
|
-
// Drop the bucket's value extra ref (we duped it above)
|
|
423
|
-
unsafe.drop(bucket_ptr.*.value);
|
|
424
|
-
|
|
425
|
-
(ctrl_ptr &+ index).* = CTRL_DELETED;
|
|
426
|
-
self.size = (self.size - usize(1));
|
|
427
|
-
|
|
428
|
-
.Some(value)
|
|
429
|
-
},
|
|
430
|
-
.None => .None
|
|
431
|
-
)
|
|
432
|
-
}
|
|
433
|
-
),
|
|
434
|
-
|
|
346
|
+
* Check if a key exists in the map
|
|
347
|
+
*/
|
|
348
|
+
contains_key : (fn(self : Self, key : K) -> bool)({
|
|
349
|
+
hash := key.hash();
|
|
350
|
+
bucket_opt := Self._find_bucket(self, key, hash);
|
|
351
|
+
match(
|
|
352
|
+
bucket_opt,
|
|
353
|
+
.Some(_) => true,
|
|
354
|
+
.None => false
|
|
355
|
+
)
|
|
356
|
+
}),
|
|
435
357
|
/**
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
358
|
+
* Remove a key-value pair by key
|
|
359
|
+
* Returns Some(value) if the key was found and removed, None otherwise
|
|
360
|
+
*/
|
|
361
|
+
remove : (fn(self : Self, key : K) -> Option(V))({
|
|
362
|
+
hash := key.hash();
|
|
363
|
+
bucket_opt := Self._find_bucket(self, key, hash);
|
|
364
|
+
match(
|
|
365
|
+
bucket_opt,
|
|
366
|
+
.Some(index) => {
|
|
367
|
+
data_ptr := Self._data_ptr(self);
|
|
368
|
+
ctrl_ptr := Self._ctrl_ptr(self);
|
|
369
|
+
bucket_ptr := (data_ptr &+ index);
|
|
370
|
+
// Read and dup value for return
|
|
371
|
+
value := bucket_ptr.*.value;
|
|
372
|
+
// Drop the bucket's key (we're removing it)
|
|
373
|
+
unsafe.drop(bucket_ptr.*.key);
|
|
374
|
+
// Drop the bucket's value extra ref (we duped it above)
|
|
375
|
+
unsafe.drop(bucket_ptr.*.value);
|
|
376
|
+
(ctrl_ptr &+ index).* = CTRL_DELETED;
|
|
377
|
+
self.size = (self.size - usize(1));
|
|
378
|
+
.Some(value)
|
|
379
|
+
},
|
|
380
|
+
.None =>.None
|
|
381
|
+
)
|
|
382
|
+
}),
|
|
383
|
+
/**
|
|
384
|
+
* Get the number of key-value pairs in the map
|
|
385
|
+
*/
|
|
386
|
+
len : (fn(self : Self) -> usize)(
|
|
439
387
|
self.size
|
|
440
388
|
),
|
|
441
|
-
|
|
442
389
|
/**
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
is_empty : (fn(self: Self) -> bool)(
|
|
446
|
-
|
|
390
|
+
* Check if the map is empty
|
|
391
|
+
*/
|
|
392
|
+
is_empty : (fn(self : Self) -> bool)(
|
|
393
|
+
self.size == usize(0)
|
|
447
394
|
),
|
|
448
|
-
|
|
449
395
|
/**
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
clear : (fn(self: Self) -> unit)(
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
i
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
self.size = usize(0);
|
|
481
|
-
}
|
|
482
|
-
)
|
|
396
|
+
* Clear all key-value pairs
|
|
397
|
+
* Resets all control bytes to EMPTY
|
|
398
|
+
*/
|
|
399
|
+
clear : (fn(self : Self) -> unit)({
|
|
400
|
+
// Drop all occupied buckets before clearing
|
|
401
|
+
cond(
|
|
402
|
+
(Type.contains_rc_type(K) || Type.contains_rc_type(V)) => {
|
|
403
|
+
ctrl_ptr := Self._ctrl_ptr(self);
|
|
404
|
+
data_ptr := Self._data_ptr(self);
|
|
405
|
+
i := usize(0);
|
|
406
|
+
while(i < self.capacity, i = (i + usize(1)), {
|
|
407
|
+
ctrl_byte := (ctrl_ptr &+ i).*;
|
|
408
|
+
cond(
|
|
409
|
+
((ctrl_byte != CTRL_EMPTY) && (ctrl_byte != CTRL_DELETED)) => {
|
|
410
|
+
bucket_ptr := (data_ptr &+ i);
|
|
411
|
+
unsafe.drop(bucket_ptr.*);
|
|
412
|
+
},
|
|
413
|
+
true => ()
|
|
414
|
+
);
|
|
415
|
+
});
|
|
416
|
+
},
|
|
417
|
+
true => ()
|
|
418
|
+
);
|
|
419
|
+
ctrl_ptr := Self._ctrl_ptr(self);
|
|
420
|
+
i := usize(0);
|
|
421
|
+
while(i < self.capacity, i = (i + usize(1)), {
|
|
422
|
+
(ctrl_ptr &+ i).* = CTRL_EMPTY;
|
|
423
|
+
});
|
|
424
|
+
self.size = usize(0);
|
|
425
|
+
})
|
|
483
426
|
);
|
|
484
|
-
impl(
|
|
427
|
+
impl(
|
|
428
|
+
forall(K : Type, V : Type),
|
|
429
|
+
where(K <: (Eq(K), Hash)),
|
|
430
|
+
HashMap(K, V),
|
|
485
431
|
Dispose(
|
|
486
432
|
/**
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
dispose : (fn(self: Self) -> unit)({
|
|
433
|
+
* RAII destructor - automatically called when HashMap goes out of scope
|
|
434
|
+
*/
|
|
435
|
+
dispose : (fn(self : Self) -> unit)({
|
|
490
436
|
// Drop all occupied buckets before freeing memory
|
|
491
437
|
cond(
|
|
492
438
|
(Type.contains_rc_type(K) || Type.contains_rc_type(V)) => {
|
|
493
|
-
match(
|
|
439
|
+
match(
|
|
440
|
+
self.ctrl,
|
|
494
441
|
.Some(ctrl_ptr) => {
|
|
495
|
-
match(
|
|
442
|
+
match(
|
|
443
|
+
self.data,
|
|
496
444
|
.Some(data_ptr) => {
|
|
497
445
|
i := usize(0);
|
|
498
446
|
while(i < self.capacity, i = (i + usize(1)), {
|
|
@@ -514,192 +462,221 @@ impl(forall(K : Type, V : Type), where(K <: (Eq(K), Hash)), HashMap(K, V),
|
|
|
514
462
|
},
|
|
515
463
|
true => ()
|
|
516
464
|
);
|
|
517
|
-
|
|
518
|
-
|
|
465
|
+
match(
|
|
466
|
+
self.ctrl,
|
|
519
467
|
.Some(ptr) => free(.Some(*(void)(ptr))),
|
|
520
468
|
.None => ()
|
|
521
469
|
);
|
|
522
|
-
match(
|
|
470
|
+
match(
|
|
471
|
+
self.data,
|
|
523
472
|
.Some(ptr) => free(.Some(*(void)(ptr))),
|
|
524
473
|
.None => ()
|
|
525
474
|
);
|
|
526
475
|
})
|
|
527
|
-
)
|
|
528
|
-
|
|
476
|
+
)
|
|
477
|
+
);
|
|
529
478
|
/**
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
479
|
+
* Value iterator for HashMap - yields Bucket(K, V) by value
|
|
480
|
+
* Scans ctrl bytes to find occupied slots.
|
|
481
|
+
*/
|
|
533
482
|
HashMapIter :: (fn(comptime(K) : Type, comptime(V) : Type) -> comptime(Type))(
|
|
534
483
|
struct(
|
|
535
|
-
_map
|
|
484
|
+
_map : HashMap(K, V),
|
|
536
485
|
_index : usize
|
|
537
486
|
)
|
|
538
487
|
);
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
488
|
+
impl(
|
|
489
|
+
forall(K : Type, V : Type),
|
|
490
|
+
where(K <: (Eq(K), Hash)),
|
|
491
|
+
HashMapIter(K, V),
|
|
492
|
+
Iterator(
|
|
493
|
+
Item : Bucket(K, V),
|
|
494
|
+
next : (fn(self : *(Self)) -> Option(Bucket(K, V)))(
|
|
495
|
+
cond(
|
|
496
|
+
(self._map.capacity == usize(0)) =>.None,
|
|
497
|
+
true => {
|
|
498
|
+
ctrl_ptr := self._map.ctrl.unwrap();
|
|
499
|
+
data_ptr := self._map.data.unwrap();
|
|
500
|
+
result := Option(Bucket(K, V)).None;
|
|
501
|
+
while((self._index < self._map.capacity) && result.is_none(), self._index = (self._index + usize(1)), {
|
|
502
|
+
ctrl_byte := (ctrl_ptr &+ self._index).*;
|
|
503
|
+
cond(
|
|
504
|
+
((ctrl_byte != CTRL_EMPTY) && (ctrl_byte != CTRL_DELETED)) => {
|
|
505
|
+
result =.Some((data_ptr &+ self._index).*);
|
|
506
|
+
},
|
|
507
|
+
true => ()
|
|
508
|
+
);
|
|
509
|
+
});
|
|
510
|
+
result
|
|
511
|
+
}
|
|
512
|
+
)
|
|
560
513
|
)
|
|
561
514
|
)
|
|
562
|
-
)
|
|
563
|
-
|
|
564
|
-
|
|
515
|
+
);
|
|
516
|
+
impl(
|
|
517
|
+
forall(K : Type, V : Type),
|
|
518
|
+
where(K <: (Eq(K), Hash)),
|
|
519
|
+
HashMap(K, V),
|
|
565
520
|
into_iter : (fn(self : Self) -> HashMapIter(K, V))(
|
|
566
|
-
HashMapIter(K, V)(_map: self, _index: usize(0))
|
|
521
|
+
HashMapIter(K, V)(_map : self, _index : usize(0))
|
|
567
522
|
)
|
|
568
523
|
);
|
|
569
|
-
|
|
570
524
|
/**
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
525
|
+
* Pointer iterator for HashMap - yields pointers to Bucket(K, V)
|
|
526
|
+
* Pointers are valid as long as the map is not modified during iteration.
|
|
527
|
+
*/
|
|
574
528
|
HashMapIterPtr :: (fn(comptime(K) : Type, comptime(V) : Type) -> comptime(Type))(
|
|
575
529
|
struct(
|
|
576
|
-
_map
|
|
530
|
+
_map : HashMap(K, V),
|
|
577
531
|
_index : usize
|
|
578
532
|
)
|
|
579
533
|
);
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
534
|
+
impl(
|
|
535
|
+
forall(K : Type, V : Type),
|
|
536
|
+
where(K <: (Eq(K), Hash)),
|
|
537
|
+
HashMapIterPtr(K, V),
|
|
538
|
+
Iterator(
|
|
539
|
+
Item : *(Bucket(K, V)),
|
|
540
|
+
next : (fn(self : *(Self)) -> Option(*(Bucket(K, V))))(
|
|
541
|
+
cond(
|
|
542
|
+
(self._map.capacity == usize(0)) =>.None,
|
|
543
|
+
true => {
|
|
544
|
+
ctrl_ptr := self._map.ctrl.unwrap();
|
|
545
|
+
data_ptr := self._map.data.unwrap();
|
|
546
|
+
result := Option(*(Bucket(K, V))).None;
|
|
547
|
+
while((self._index < self._map.capacity) && result.is_none(), self._index = (self._index + usize(1)), {
|
|
548
|
+
ctrl_byte := (ctrl_ptr &+ self._index).*;
|
|
549
|
+
cond(
|
|
550
|
+
((ctrl_byte != CTRL_EMPTY) && (ctrl_byte != CTRL_DELETED)) => {
|
|
551
|
+
result =.Some(data_ptr &+ self._index);
|
|
552
|
+
},
|
|
553
|
+
true => ()
|
|
554
|
+
);
|
|
555
|
+
});
|
|
556
|
+
result
|
|
557
|
+
}
|
|
558
|
+
)
|
|
601
559
|
)
|
|
602
560
|
)
|
|
603
|
-
)
|
|
604
|
-
|
|
605
|
-
|
|
561
|
+
);
|
|
562
|
+
impl(
|
|
563
|
+
forall(K : Type, V : Type),
|
|
564
|
+
where(K <: (Eq(K), Hash)),
|
|
565
|
+
HashMap(K, V),
|
|
606
566
|
iter : (fn(self : *(Self)) -> HashMapIterPtr(K, V))(
|
|
607
|
-
HashMapIterPtr(K, V)(_map: self.*, _index: usize(0))
|
|
567
|
+
HashMapIterPtr(K, V)(_map : self.*, _index : usize(0))
|
|
608
568
|
)
|
|
609
569
|
);
|
|
610
|
-
|
|
611
570
|
/**
|
|
612
|
-
|
|
613
|
-
|
|
571
|
+
* Keys iterator - wraps HashMapIter and yields only the keys
|
|
572
|
+
*/
|
|
614
573
|
HashMapKeys :: (fn(comptime(K) : Type, comptime(V) : Type) -> comptime(Type))(
|
|
615
574
|
struct(
|
|
616
575
|
_inner : HashMapIter(K, V)
|
|
617
576
|
)
|
|
618
577
|
);
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
578
|
+
impl(
|
|
579
|
+
forall(K : Type, V : Type),
|
|
580
|
+
where(K <: (Eq(K), Hash)),
|
|
581
|
+
HashMapKeys(K, V),
|
|
582
|
+
Iterator(
|
|
583
|
+
Item : K,
|
|
584
|
+
next : (fn(self : *(Self)) -> Option(K))(
|
|
585
|
+
match(
|
|
586
|
+
self._inner.next(),
|
|
587
|
+
.None =>.None,
|
|
588
|
+
.Some(bucket) =>.Some(bucket.key)
|
|
589
|
+
)
|
|
626
590
|
)
|
|
627
591
|
)
|
|
628
|
-
)
|
|
629
|
-
|
|
630
|
-
|
|
592
|
+
);
|
|
593
|
+
impl(
|
|
594
|
+
forall(K : Type, V : Type),
|
|
595
|
+
where(K <: (Eq(K), Hash)),
|
|
596
|
+
HashMap(K, V),
|
|
631
597
|
keys : (fn(self : Self) -> HashMapKeys(K, V))(
|
|
632
|
-
HashMapKeys(K, V)(_inner: HashMapIter(K, V)(_map: self, _index: usize(0)))
|
|
598
|
+
HashMapKeys(K, V)(_inner : HashMapIter(K, V)(_map : self, _index : usize(0)))
|
|
633
599
|
)
|
|
634
600
|
);
|
|
635
|
-
|
|
636
601
|
/**
|
|
637
|
-
|
|
638
|
-
|
|
602
|
+
* Values iterator - wraps HashMapIter and yields only the values
|
|
603
|
+
*/
|
|
639
604
|
HashMapValues :: (fn(comptime(K) : Type, comptime(V) : Type) -> comptime(Type))(
|
|
640
605
|
struct(
|
|
641
606
|
_inner : HashMapIter(K, V)
|
|
642
607
|
)
|
|
643
608
|
);
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
609
|
+
impl(
|
|
610
|
+
forall(K : Type, V : Type),
|
|
611
|
+
where(K <: (Eq(K), Hash)),
|
|
612
|
+
HashMapValues(K, V),
|
|
613
|
+
Iterator(
|
|
614
|
+
Item : V,
|
|
615
|
+
next : (fn(self : *(Self)) -> Option(V))(
|
|
616
|
+
match(
|
|
617
|
+
self._inner.next(),
|
|
618
|
+
.None =>.None,
|
|
619
|
+
.Some(bucket) =>.Some(bucket.value)
|
|
620
|
+
)
|
|
651
621
|
)
|
|
652
622
|
)
|
|
653
|
-
)
|
|
654
|
-
|
|
655
|
-
|
|
623
|
+
);
|
|
624
|
+
impl(
|
|
625
|
+
forall(K : Type, V : Type),
|
|
626
|
+
where(K <: (Eq(K), Hash)),
|
|
627
|
+
HashMap(K, V),
|
|
656
628
|
values : (fn(self : Self) -> HashMapValues(K, V))(
|
|
657
|
-
HashMapValues(K, V)(_inner: HashMapIter(K, V)(_map: self, _index: usize(0)))
|
|
629
|
+
HashMapValues(K, V)(_inner : HashMapIter(K, V)(_map : self, _index : usize(0)))
|
|
630
|
+
)
|
|
631
|
+
);
|
|
632
|
+
impl(
|
|
633
|
+
forall(K : Type, V : Type),
|
|
634
|
+
HashMap(K, V),
|
|
635
|
+
Index(K)(
|
|
636
|
+
Output : V,
|
|
637
|
+
index : (fn(self : *(Self), idx : K, where(K <: (Eq(K), Hash))) -> *(Self.Output))({
|
|
638
|
+
hash := idx.hash();
|
|
639
|
+
bucket_opt := Self._find_bucket(self.*, idx, hash);
|
|
640
|
+
match(
|
|
641
|
+
bucket_opt,
|
|
642
|
+
.Some(i) => {
|
|
643
|
+
data_ptr := Self._data_ptr(self.*);
|
|
644
|
+
&((data_ptr &+ i).*.value)
|
|
645
|
+
},
|
|
646
|
+
.None => panic("HashMap: key not found")
|
|
647
|
+
)
|
|
648
|
+
})
|
|
658
649
|
)
|
|
659
650
|
);
|
|
660
|
-
|
|
661
|
-
impl(forall(K : Type, V : Type), HashMap(K, V), Index(K)(
|
|
662
|
-
Output : V,
|
|
663
|
-
index : (fn(self: *(Self), idx: K, where(K <: (Eq(K), Hash))) -> *(Self.Output))({
|
|
664
|
-
hash := idx.hash();
|
|
665
|
-
bucket_opt := Self._find_bucket(self.*, idx, hash);
|
|
666
|
-
match(bucket_opt,
|
|
667
|
-
.Some(i) => {
|
|
668
|
-
data_ptr := Self._data_ptr(self.*);
|
|
669
|
-
&((data_ptr &+ i).*.value)
|
|
670
|
-
},
|
|
671
|
-
.None => panic("HashMap: key not found")
|
|
672
|
-
)
|
|
673
|
-
})
|
|
674
|
-
));
|
|
675
|
-
|
|
676
651
|
/// Clone implementation for HashMap — deep-clones all key-value entries.
|
|
677
|
-
impl(
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
652
|
+
impl(
|
|
653
|
+
forall(K : Type, V : Type),
|
|
654
|
+
where(K <: (Clone, Eq(K), Hash), V <: Clone),
|
|
655
|
+
HashMap(K, V),
|
|
656
|
+
Clone(
|
|
657
|
+
clone : (fn(self : *(Self)) -> Self)({
|
|
658
|
+
result := Self.new();
|
|
659
|
+
it := self.iter();
|
|
660
|
+
running := true;
|
|
661
|
+
while(running, {
|
|
662
|
+
nxt := it.next();
|
|
663
|
+
match(
|
|
664
|
+
nxt,
|
|
665
|
+
.None => {
|
|
666
|
+
running = false;
|
|
667
|
+
},
|
|
668
|
+
.Some(bucket_ptr) => {
|
|
669
|
+
k := (&(bucket_ptr.*.key)).clone();
|
|
670
|
+
v := (&(bucket_ptr.*.value)).clone();
|
|
671
|
+
result.set(k, v);
|
|
672
|
+
}
|
|
673
|
+
);
|
|
674
|
+
});
|
|
675
|
+
result
|
|
676
|
+
})
|
|
677
|
+
)
|
|
678
|
+
);
|
|
679
|
+
export(
|
|
703
680
|
HashMap,
|
|
704
681
|
HashMapError,
|
|
705
682
|
Bucket,
|
|
@@ -707,12 +684,10 @@ export
|
|
|
707
684
|
HashMapIterPtr,
|
|
708
685
|
HashMapKeys,
|
|
709
686
|
HashMapValues
|
|
710
|
-
;
|
|
711
|
-
|
|
687
|
+
);
|
|
712
688
|
// =============================================================================
|
|
713
689
|
// hash_map! literal macro
|
|
714
690
|
// =============================================================================
|
|
715
|
-
|
|
716
691
|
/// Internal helper: build a list of `tmp.set(k, v).unwrap()` statements from
|
|
717
692
|
/// a sequence of `k => v` arrow expressions.
|
|
718
693
|
__hash_map_build_sets :: (fn(comptime(tmp) : Expr, comptime(entries) : ExprList) -> comptime(ExprList))(
|
|
@@ -723,17 +698,16 @@ __hash_map_build_sets :: (fn(comptime(tmp) : Expr, comptime(entries) : ExprList)
|
|
|
723
698
|
rest :: entries.cdr();
|
|
724
699
|
__ :: comptime_assert(first.is_fn_call(), "hash_map entries must use 'k => v'");
|
|
725
700
|
callee :: first.get_callee();
|
|
726
|
-
__1 :: comptime_assert(
|
|
701
|
+
__1 :: comptime_assert(callee.is_atom() && (callee == quote(=>)), "hash_map entries must use 'k => v'");
|
|
727
702
|
pair :: first.get_args();
|
|
728
|
-
__2 :: comptime_assert(
|
|
703
|
+
__2 :: comptime_assert(pair.len() == usize(2), "hash_map entries must use 'k => v'");
|
|
729
704
|
key :: pair.car();
|
|
730
705
|
val :: pair.cdr().car();
|
|
731
|
-
stmt :: quote(
|
|
706
|
+
stmt :: quote(unquote(tmp).set(unquote(key), unquote(val)).unwrap());
|
|
732
707
|
stmt.cons(recur(tmp, rest))
|
|
733
708
|
}
|
|
734
709
|
)
|
|
735
710
|
);
|
|
736
|
-
|
|
737
711
|
/// `hash_map` macro — construct a `HashMap(K, V)` literal.
|
|
738
712
|
///
|
|
739
713
|
/// `K` and `V` are inferred from the first entry's key and value via `typeof`,
|
|
@@ -743,15 +717,17 @@ __hash_map_build_sets :: (fn(comptime(tmp) : Expr, comptime(entries) : ExprList)
|
|
|
743
717
|
/// ```rust
|
|
744
718
|
/// m := hash_map(`a` => i32(1), `b` => i32(2));
|
|
745
719
|
/// ```
|
|
746
|
-
hash_map :: (fn(...(quote(entries))) -> unquote(Expr))
|
|
747
|
-
__ :: comptime_assert(
|
|
748
|
-
|
|
720
|
+
hash_map :: (fn(...(quote(entries))) -> unquote(Expr))({
|
|
721
|
+
__ :: comptime_assert(
|
|
722
|
+
entries.len() > usize(0),
|
|
723
|
+
"hash_map requires at least one 'k => v' entry to infer K and V"
|
|
724
|
+
);
|
|
749
725
|
first :: entries.car();
|
|
750
726
|
__1 :: comptime_assert(first.is_fn_call(), "hash_map entries must use 'k => v'");
|
|
751
727
|
callee :: first.get_callee();
|
|
752
|
-
__2 :: comptime_assert(
|
|
728
|
+
__2 :: comptime_assert(callee.is_atom() && (callee == quote(=>)), "hash_map entries must use 'k => v'");
|
|
753
729
|
pair :: first.get_args();
|
|
754
|
-
__3 :: comptime_assert(
|
|
730
|
+
__3 :: comptime_assert(pair.len() == usize(2), "hash_map entries must use 'k => v'");
|
|
755
731
|
first_key :: pair.car();
|
|
756
732
|
first_val :: pair.cdr().car();
|
|
757
733
|
rest :: entries.cdr();
|
|
@@ -759,13 +735,13 @@ hash_map :: (fn(...(quote(entries))) -> unquote(Expr)) {
|
|
|
759
735
|
k_tmp :: gensym("hash_map_k0");
|
|
760
736
|
v_tmp :: gensym("hash_map_v0");
|
|
761
737
|
rest_sets :: __hash_map_build_sets(tmp, rest);
|
|
762
|
-
quote
|
|
738
|
+
quote({
|
|
763
739
|
unquote(k_tmp) := unquote(first_key);
|
|
764
740
|
unquote(v_tmp) := unquote(first_val);
|
|
765
741
|
unquote(tmp) := HashMap(typeof(unquote(k_tmp)), typeof(unquote(v_tmp))).new();
|
|
766
742
|
unquote(tmp).set(unquote(k_tmp), unquote(v_tmp)).unwrap();
|
|
767
743
|
unquote_splicing(rest_sets);
|
|
768
744
|
unquote(tmp)
|
|
769
|
-
}
|
|
770
|
-
};
|
|
771
|
-
export
|
|
745
|
+
})
|
|
746
|
+
});
|
|
747
|
+
export(hash_map);
|