@@ -3,50 +3,34 @@ const assert = std.debug.assert;
3
3
4
4
/// 'comptime' optimized mapping between string keys and associated `V` values.
5
5
pub fn StaticStringMap (comptime V : type ) type {
6
- return StaticArrayMapWithEql ( V , u8 , defaultEql );
6
+ return StaticStringMapWithEql ( V , defaultEql );
7
7
}
8
8
9
9
/// Same as StaticStringMap, except keys are compared case-insensitively.
10
10
pub fn StaticStringMapIgnoreCase (comptime V : type ) type {
11
- return StaticArrayMapWithEql ( V , u8 , ignoreCaseEql );
11
+ return StaticStringMapWithEql ( V , ignoreCaseEql );
12
12
}
13
13
14
- /// Same as StaticStringMap, but allows you to provide the `eql` function yourself.
15
- pub fn StaticStringMapWithEql (
16
- comptime V : type ,
17
- comptime eql : fn (comptime anytype , anytype ) bool ,
18
- ) type {
19
- return StaticArrayMapWithEql (V , u8 , eql );
20
- }
21
-
22
- /// 'comptime' optimized mapping between `[]const T` keys and associated `V` values.
23
- pub fn StaticArrayMap (comptime T : type , comptime V : type ) type {
24
- return StaticArrayMapWithEql (V , T , defaultEql );
25
- }
26
-
27
- pub fn defaultEql (comptime expected : anytype , actual : anytype ) bool {
28
- const Array = @TypeOf (expected , actual );
29
- const T = @typeInfo (Array ).array .child ;
30
- const child_bits = @sizeOf (T ) * std .mem .byte_size_in_bits ;
14
+ fn defaultEql (comptime expected : anytype , actual : anytype ) bool {
15
+ comptime assert (expected .len == actual .len );
16
+ const child_bits = expected .len * std .mem .byte_size_in_bits ;
31
17
32
- // Directly comparing sections of memory as integer types
33
- // does not work for padded array fields, nor does it work for arrays
34
- // which exceed 65535 bits (there are no integer types that large).
35
- const unique = std .meta .hasUniqueRepresentation (Array );
18
+ // Integers are limited to 65535 bits; default to a for loop
19
+ // if the strings to compare are too long
36
20
const too_many_bits = child_bits > 65535 ;
37
21
38
22
// TODO: riscv64 backend can't airBitCast [7]u8 to u56
39
23
const limited_backend = @import ("builtin" ).zig_backend == .stage2_riscv64 ;
40
24
41
- if (! unique or too_many_bits or limited_backend ) {
25
+ if (too_many_bits or limited_backend ) {
42
26
var match : bool = true ;
43
27
for (expected , actual ) | a , b | {
44
28
match = match and a == b ;
45
29
}
46
30
return match ;
47
31
}
48
32
49
- const Compare = std .meta .Int (.unsigned , @bitSizeOf ( Array ) );
33
+ const Compare = std .meta .Int (.unsigned , child_bits );
50
34
const a : Compare = @bitCast (expected );
51
35
const b : Compare = @bitCast (actual );
52
36
return a == b ;
@@ -67,6 +51,7 @@ fn ignoreCaseEql(comptime expected: anytype, actual: anytype) bool {
67
51
return defaultEql (lower_expected , lower_actual );
68
52
}
69
53
54
+ // For backends that cannot handle integer vectors
70
55
fn toLowerSimple (comptime len : usize , input : anytype ) [len ]u8 {
71
56
var output : [len ]u8 = undefined ;
72
57
for (input , & output ) | in_byte , * out_byte | {
@@ -93,25 +78,23 @@ fn toLowerSimd(input: anytype) [@typeInfo(@TypeOf(input)).array.len]u8 {
93
78
94
79
/// Static string map constructed at compile time for additional optimizations.
95
80
/// First branches on the key length, then compares the possible matching keys.
96
- pub fn StaticArrayMapWithEql (
81
+ fn StaticStringMapWithEql (
97
82
/// The type of the value
98
83
comptime V : type ,
99
- /// The type of the element in the array, eg. []const T - would be u8 for a string
100
- comptime T : type ,
101
84
/// The equal function that is used to compare the keys
102
85
comptime eql : fn (comptime anytype , anytype ) bool ,
103
86
) type {
104
87
return struct {
105
88
const Self = @This ();
106
89
107
90
pub const Kv = struct {
108
- key : []const T ,
91
+ key : []const u8 ,
109
92
value : V ,
110
93
};
111
94
112
95
kvs : []const Kv ,
113
96
114
- /// Initializes the map at comptime
97
+ /// Initializes the map at comptime with a list of key / value pairs
115
98
pub inline fn initComptime (comptime kvs_list : anytype ) Self {
116
99
const list = comptime blk : {
117
100
var list : [kvs_list .len ]Kv = undefined ;
@@ -126,23 +109,23 @@ pub fn StaticArrayMapWithEql(
126
109
return comptime .{ .kvs = & list };
127
110
}
128
111
129
- /// Checks if the map has a value for the key.
130
- pub fn has (comptime self : Self , key : []const T ) bool {
112
+ /// Checks if the map contains the key.
113
+ pub fn has (comptime self : Self , key : []const u8 ) bool {
131
114
return self .get (key ) != null ;
132
115
}
133
116
134
- /// Returns the value for the key if any, else null .
135
- pub fn get (comptime self : Self , key : []const T ) ? V {
117
+ /// Returns the value for the key if any.
118
+ pub fn get (comptime self : Self , key : []const u8 ) ? V {
136
119
switch (self .kvs .len ) {
137
120
0 = > return null ,
138
121
else = > return self .filterLength (key ),
139
122
}
140
123
}
141
124
142
- /// The list of all the keys in the map.
143
- pub fn keys (comptime self : Self ) []const []const T {
125
+ /// Returns the list of all the keys in the map.
126
+ pub fn keys (comptime self : Self ) []const []const u8 {
144
127
const list = comptime blk : {
145
- var list : [self .kvs .len ][]const T = undefined ;
128
+ var list : [self .kvs .len ][]const u8 = undefined ;
146
129
for (& list , self .kvs ) | * key , kv | {
147
130
key .* = kv .key ;
148
131
}
@@ -151,7 +134,7 @@ pub fn StaticArrayMapWithEql(
151
134
return & list ;
152
135
}
153
136
154
- /// The list of all the values in the map.
137
+ /// Returns the list of all the values in the map.
155
138
pub fn values (comptime self : Self ) []const V {
156
139
const list = comptime blk : {
157
140
var list : [self .kvs .len ]V = undefined ;
@@ -165,7 +148,7 @@ pub fn StaticArrayMapWithEql(
165
148
166
149
/// Filters the input key by length, then compares it to the possible matches.
167
150
/// Because we know the length at comptime, we can compare the strings faster.
168
- fn filterLength (comptime self : Self , key : []const T ) ? V {
151
+ fn filterLength (comptime self : Self , key : []const u8 ) ? V {
169
152
// separateLength is hungry - provide 2000 branches per key/value pair
170
153
@setEvalBranchQuota (2000 * self .kvs .len );
171
154
const kvs_by_lengths = comptime self .separateLength ();
@@ -179,14 +162,7 @@ pub fn StaticArrayMapWithEql(
179
162
@setEvalBranchQuota (10 * len * idx );
180
163
comptime for (kvs_by_len .kvs [0.. idx ]) | prev_kv | {
181
164
if (eql (kv .key [0.. len ].* , prev_kv .key [0.. len ].* )) {
182
- if (T == u8 and std .unicode .utf8ValidateSlice (kv .key )) {
183
- @compileError ("duplicate key \" " ++ kv .key ++ "\" " );
184
- } else {
185
- @compileError (std .fmt .comptimePrint (
186
- "duplicate key: {any}" ,
187
- .{kv .key },
188
- ));
189
- }
165
+ @compileError ("redundant key \" " ++ kv .key ++ "\" " );
190
166
}
191
167
};
192
168
@@ -429,73 +405,12 @@ test "sorting kvs doesn't exceed eval branch quota" {
429
405
try testing .expectEqual (1 , TypeToByteSizeLUT .get ("t1" ));
430
406
}
431
407
432
- test "static array map" {
433
- const map = StaticArrayMap (u16 , u4 ).initComptime (.{
434
- .{ &[_ ]u16 { 0 , 1 , 2 , 3 }, 0 },
435
- .{ &[_ ]u16 { 4 , 5 , 6 , 7 , 8 }, 1 },
436
- .{ &[_ ]u16 { 9 , 10 , 1 << 13 , 4 }, 2 },
437
- .{ &[_ ]u16 {0 }, 3 },
438
- .{ &[_ ]u16 {}, 4 },
439
- });
440
-
441
- try testing .expectEqual (0 , map .get (&[_ ]u16 { 0 , 1 , 2 , 3 }).? );
442
- try testing .expectEqual (2 , map .get (&[_ ]u16 { 9 , 10 , 1 << 13 , 4 }).? );
443
- try testing .expectEqual (4 , map .get (&[_ ]u16 {}).? );
444
-
445
- try testing .expectEqual (null , map .get (&[_ ]u16 { 7 , 7 , 7 }));
446
- try testing .expectEqual (null , map .get (&[_ ]u16 { 0 , 1 }));
447
-
448
- try testing .expectEqual (true , map .has (&[_ ]u16 { 4 , 5 , 6 , 7 , 8 }));
449
- try testing .expectEqual (true , map .has (&[_ ]u16 {0 }));
450
-
451
- try testing .expectEqual (false , map .has (&[_ ]u16 {5 }));
452
- try testing .expectEqual (false , map .has (&[_ ]u16 { 0 , 0 }));
453
- }
454
-
455
- test "array elements that are padded" {
456
- const map = StaticArrayMap (u7 , u4 ).initComptime (&.{
457
- .{ &.{ 0 , 1 , 2 , 3 , 4 , 5 }, 0 },
458
- .{ &.{ 0 , 1 , 127 , 3 , 4 , 5 }, 1 },
459
- .{ &.{ 0 , 1 , 2 , 126 , 4 , 5 }, 2 },
460
- });
461
-
462
- try testing .expectEqual (null , map .get (&.{0 }));
463
- try testing .expectEqual (0 , map .get (&.{ 0 , 1 , 2 , 3 , 4 , 5 }));
464
- try testing .expectEqual (1 , map .get (&.{ 0 , 1 , 127 , 3 , 4 , 5 }));
465
- try testing .expectEqual (2 , map .get (&.{ 0 , 1 , 2 , 126 , 4 , 5 }));
466
- }
467
-
468
- fn lastElementEql (comptime expected : anytype , actual : anytype ) bool {
469
- if (expected .len == 0 ) {
470
- return false ;
471
- } else {
472
- const last_idx = expected .len - 1 ;
473
- return expected [last_idx ] == actual [last_idx ];
474
- }
475
- }
476
-
477
- test "custom equal function" {
478
- const map = StaticStringMapWithEql (u2 , lastElementEql ).initComptime (.{
479
- .{ "last byte is a t" , 0 },
480
- .{ "last byte is a b" , 1 },
481
- .{ "last byte is a s" , 2 },
482
- .{ "last byte is a c" , 3 },
483
- });
484
-
485
- // limitation: eql functions only are called on same-length inputs
486
- try testing .expectEqual (false , map .has ("t" ));
487
-
488
- try testing .expectEqual (1 , map .get ("my magic byte: b" ));
489
- try testing .expectEqual (0 , map .get ("my magic byte: t" ));
490
- try testing .expectEqual (2 , map .get ("my magic byte: s" ));
491
- }
492
-
493
408
test "single string StaticStringMap" {
494
- const map = StaticStringMap (void ).initComptime (.{.{"o kama pona " }});
409
+ const map = StaticStringMap (void ).initComptime (.{.{"Hello, World! " }});
495
410
496
- try testing .expectEqual (true , map .has ("o kama pona " ));
497
- try testing .expectEqual (false , map .has ("o kama ike " ));
498
- try testing .expectEqual (false , map .has ("o kama pona ala " ));
411
+ try testing .expectEqual (true , map .has ("Hello, World! " ));
412
+ try testing .expectEqual (false , map .has ("Same len str! " ));
413
+ try testing .expectEqual (false , map .has ("Hello, World! (not the same) " ));
499
414
}
500
415
501
416
test "empty StaticStringMap" {
0 commit comments