| ... | @@ -116,232 +116,6 @@ pub const StringIndexAdapter = struct { | ... | @@ -116,232 +116,6 @@ pub const StringIndexAdapter = struct { |
| 116 | | 116 | |
| 117 | pub const default_max_load_percentage = 80; | 117 | pub const default_max_load_percentage = 80; |
| 118 | | 118 | |
| 119 | /// This function issues a compile error with a helpful message if there | | |
| 120 | /// is a problem with the provided context type. A context must have the following | | |
| 121 | /// member functions: | | |
| 122 | /// - hash(self, PseudoKey) Hash | | |
| 123 | /// - eql(self, PseudoKey, Key) bool | | |
| 124 | /// | | |
| 125 | /// If you are passing a context to a *Adapted function, PseudoKey is the type | | |
| 126 | /// of the key parameter. Otherwise, when creating a HashMap or HashMapUnmanaged | | |
| 127 | /// type, PseudoKey = Key = K. | | |
| 128 | pub fn verifyContext( | | |
| 129 | comptime RawContext: type, | | |
| 130 | comptime PseudoKey: type, | | |
| 131 | comptime Key: type, | | |
| 132 | comptime Hash: type, | | |
| 133 | comptime is_array: bool, | | |
| 134 | ) void { | | |
| 135 | comptime { | | |
| 136 | var allow_const_ptr = false; | | |
| 137 | var allow_mutable_ptr = false; | | |
| 138 | // Context is the actual namespace type. RawContext may be a pointer to Context. | | |
| 139 | var Context = RawContext; | | |
| 140 | // Make sure the context is a namespace type which may have member functions | | |
| 141 | switch (@typeInfo(Context)) { | | |
| 142 | .@"struct", .@"union", .@"enum" => {}, | | |
| 143 | // Special-case .@"opaque" for a better error message | | |
| 144 | .@"opaque" => @compileError("Hash context must be a type with hash and eql member functions. Cannot use " ++ @typeName(Context) ++ " because it is opaque. Use a pointer instead."), | | |
| 145 | .pointer => |ptr| { | | |
| 146 | if (ptr.size != .One) { | | |
| 147 | @compileError("Hash context must be a type with hash and eql member functions. Cannot use " ++ @typeName(Context) ++ " because it is not a single pointer."); | | |
| 148 | } | | |
| 149 | Context = ptr.child; | | |
| 150 | allow_const_ptr = true; | | |
| 151 | allow_mutable_ptr = !ptr.is_const; | | |
| 152 | switch (@typeInfo(Context)) { | | |
| 153 | .@"struct", .@"union", .@"enum", .@"opaque" => {}, | | |
| 154 | else => @compileError("Hash context must be a type with hash and eql member functions. Cannot use " ++ @typeName(Context)), | | |
| 155 | } | | |
| 156 | }, | | |
| 157 | else => @compileError("Hash context must be a type with hash and eql member functions. Cannot use " ++ @typeName(Context)), | | |
| 158 | } | | |
| 159 | | | |
| 160 | // Keep track of multiple errors so we can report them all. | | |
| 161 | var errors: []const u8 = ""; | | |
| 162 | | | |
| 163 | // Put common errors here, they will only be evaluated | | |
| 164 | // if the error is actually triggered. | | |
| 165 | const lazy = struct { | | |
| 166 | const prefix = "\n "; | | |
| 167 | const deep_prefix = prefix ++ " "; | | |
| 168 | const hash_signature = "fn (self, " ++ @typeName(PseudoKey) ++ ") " ++ @typeName(Hash); | | |
| 169 | const index_param = if (is_array) ", b_index: usize" else ""; | | |
| 170 | const eql_signature = "fn (self, " ++ @typeName(PseudoKey) ++ ", " ++ | | |
| 171 | @typeName(Key) ++ index_param ++ ") bool"; | | |
| 172 | const err_invalid_hash_signature = prefix ++ @typeName(Context) ++ ".hash must be " ++ hash_signature ++ | | |
| 173 | deep_prefix ++ "but is actually " ++ @typeName(@TypeOf(Context.hash)); | | |
| 174 | const err_invalid_eql_signature = prefix ++ @typeName(Context) ++ ".eql must be " ++ eql_signature ++ | | |
| 175 | deep_prefix ++ "but is actually " ++ @typeName(@TypeOf(Context.eql)); | | |
| 176 | }; | | |
| 177 | | | |
| 178 | // Verify Context.hash(self, PseudoKey) => Hash | | |
| 179 | if (@hasDecl(Context, "hash")) { | | |
| 180 | const hash = Context.hash; | | |
| 181 | const info = @typeInfo(@TypeOf(hash)); | | |
| 182 | if (info == .@"fn") { | | |
| 183 | const func = info.@"fn"; | | |
| 184 | if (func.params.len != 2) { | | |
| 185 | errors = errors ++ lazy.err_invalid_hash_signature; | | |
| 186 | } else { | | |
| 187 | var emitted_signature = false; | | |
| 188 | if (func.params[0].type) |Self| { | | |
| 189 | if (Self == Context) { | | |
| 190 | // pass, this is always fine. | | |
| 191 | } else if (Self == *const Context) { | | |
| 192 | if (!allow_const_ptr) { | | |
| 193 | if (!emitted_signature) { | | |
| 194 | errors = errors ++ lazy.err_invalid_hash_signature; | | |
| 195 | emitted_signature = true; | | |
| 196 | } | | |
| 197 | errors = errors ++ lazy.deep_prefix ++ "First parameter must be " ++ @typeName(Context) ++ ", but is " ++ @typeName(Self); | | |
| 198 | errors = errors ++ lazy.deep_prefix ++ "Note: Cannot be a pointer because it is passed by value."; | | |
| 199 | } | | |
| 200 | } else if (Self == *Context) { | | |
| 201 | if (!allow_mutable_ptr) { | | |
| 202 | if (!emitted_signature) { | | |
| 203 | errors = errors ++ lazy.err_invalid_hash_signature; | | |
| 204 | emitted_signature = true; | | |
| 205 | } | | |
| 206 | if (!allow_const_ptr) { | | |
| 207 | errors = errors ++ lazy.deep_prefix ++ "First parameter must be " ++ @typeName(Context) ++ ", but is " ++ @typeName(Self); | | |
| 208 | errors = errors ++ lazy.deep_prefix ++ "Note: Cannot be a pointer because it is passed by value."; | | |
| 209 | } else { | | |
| 210 | errors = errors ++ lazy.deep_prefix ++ "First parameter must be " ++ @typeName(Context) ++ " or " ++ @typeName(*const Context) ++ ", but is " ++ @typeName(Self); | | |
| 211 | errors = errors ++ lazy.deep_prefix ++ "Note: Cannot be non-const because it is passed by const pointer."; | | |
| 212 | } | | |
| 213 | } | | |
| 214 | } else { | | |
| 215 | if (!emitted_signature) { | | |
| 216 | errors = errors ++ lazy.err_invalid_hash_signature; | | |
| 217 | emitted_signature = true; | | |
| 218 | } | | |
| 219 | errors = errors ++ lazy.deep_prefix ++ "First parameter must be " ++ @typeName(Context); | | |
| 220 | if (allow_const_ptr) { | | |
| 221 | errors = errors ++ " or " ++ @typeName(*const Context); | | |
| 222 | if (allow_mutable_ptr) { | | |
| 223 | errors = errors ++ " or " ++ @typeName(*Context); | | |
| 224 | } | | |
| 225 | } | | |
| 226 | errors = errors ++ ", but is " ++ @typeName(Self); | | |
| 227 | } | | |
| 228 | } | | |
| 229 | if (func.params[1].type != null and func.params[1].type.? != PseudoKey) { | | |
| 230 | if (!emitted_signature) { | | |
| 231 | errors = errors ++ lazy.err_invalid_hash_signature; | | |
| 232 | emitted_signature = true; | | |
| 233 | } | | |
| 234 | errors = errors ++ lazy.deep_prefix ++ "Second parameter must be " ++ @typeName(PseudoKey) ++ ", but is " ++ @typeName(func.params[1].type.?); | | |
| 235 | } | | |
| 236 | if (func.return_type != null and func.return_type.? != Hash) { | | |
| 237 | if (!emitted_signature) { | | |
| 238 | errors = errors ++ lazy.err_invalid_hash_signature; | | |
| 239 | emitted_signature = true; | | |
| 240 | } | | |
| 241 | errors = errors ++ lazy.deep_prefix ++ "Return type must be " ++ @typeName(Hash) ++ ", but was " ++ @typeName(func.return_type.?); | | |
| 242 | } | | |
| 243 | // If any of these are generic (null), we cannot verify them. | | |
| 244 | // The call sites check the return type, but cannot check the | | |
| 245 | // parameters. This may cause compile errors with generic hash/eql functions. | | |
| 246 | } | | |
| 247 | } else { | | |
| 248 | errors = errors ++ lazy.err_invalid_hash_signature; | | |
| 249 | } | | |
| 250 | } else { | | |
| 251 | errors = errors ++ lazy.prefix ++ @typeName(Context) ++ " must declare a pub hash function with signature " ++ lazy.hash_signature; | | |
| 252 | } | | |
| 253 | | | |
| 254 | // Verify Context.eql(self, PseudoKey, Key) => bool | | |
| 255 | if (@hasDecl(Context, "eql")) { | | |
| 256 | const eql = Context.eql; | | |
| 257 | const info = @typeInfo(@TypeOf(eql)); | | |
| 258 | if (info == .@"fn") { | | |
| 259 | const func = info.@"fn"; | | |
| 260 | const args_len = if (is_array) 4 else 3; | | |
| 261 | if (func.params.len != args_len) { | | |
| 262 | errors = errors ++ lazy.err_invalid_eql_signature; | | |
| 263 | } else { | | |
| 264 | var emitted_signature = false; | | |
| 265 | if (func.params[0].type) |Self| { | | |
| 266 | if (Self == Context) { | | |
| 267 | // pass, this is always fine. | | |
| 268 | } else if (Self == *const Context) { | | |
| 269 | if (!allow_const_ptr) { | | |
| 270 | if (!emitted_signature) { | | |
| 271 | errors = errors ++ lazy.err_invalid_eql_signature; | | |
| 272 | emitted_signature = true; | | |
| 273 | } | | |
| 274 | errors = errors ++ lazy.deep_prefix ++ "First parameter must be " ++ @typeName(Context) ++ ", but is " ++ @typeName(Self); | | |
| 275 | errors = errors ++ lazy.deep_prefix ++ "Note: Cannot be a pointer because it is passed by value."; | | |
| 276 | } | | |
| 277 | } else if (Self == *Context) { | | |
| 278 | if (!allow_mutable_ptr) { | | |
| 279 | if (!emitted_signature) { | | |
| 280 | errors = errors ++ lazy.err_invalid_eql_signature; | | |
| 281 | emitted_signature = true; | | |
| 282 | } | | |
| 283 | if (!allow_const_ptr) { | | |
| 284 | errors = errors ++ lazy.deep_prefix ++ "First parameter must be " ++ @typeName(Context) ++ ", but is " ++ @typeName(Self); | | |
| 285 | errors = errors ++ lazy.deep_prefix ++ "Note: Cannot be a pointer because it is passed by value."; | | |
| 286 | } else { | | |
| 287 | errors = errors ++ lazy.deep_prefix ++ "First parameter must be " ++ @typeName(Context) ++ " or " ++ @typeName(*const Context) ++ ", but is " ++ @typeName(Self); | | |
| 288 | errors = errors ++ lazy.deep_prefix ++ "Note: Cannot be non-const because it is passed by const pointer."; | | |
| 289 | } | | |
| 290 | } | | |
| 291 | } else { | | |
| 292 | if (!emitted_signature) { | | |
| 293 | errors = errors ++ lazy.err_invalid_eql_signature; | | |
| 294 | emitted_signature = true; | | |
| 295 | } | | |
| 296 | errors = errors ++ lazy.deep_prefix ++ "First parameter must be " ++ @typeName(Context); | | |
| 297 | if (allow_const_ptr) { | | |
| 298 | errors = errors ++ " or " ++ @typeName(*const Context); | | |
| 299 | if (allow_mutable_ptr) { | | |
| 300 | errors = errors ++ " or " ++ @typeName(*Context); | | |
| 301 | } | | |
| 302 | } | | |
| 303 | errors = errors ++ ", but is " ++ @typeName(Self); | | |
| 304 | } | | |
| 305 | } | | |
| 306 | if (func.params[1].type.? != PseudoKey) { | | |
| 307 | if (!emitted_signature) { | | |
| 308 | errors = errors ++ lazy.err_invalid_eql_signature; | | |
| 309 | emitted_signature = true; | | |
| 310 | } | | |
| 311 | errors = errors ++ lazy.deep_prefix ++ "Second parameter must be " ++ @typeName(PseudoKey) ++ ", but is " ++ @typeName(func.params[1].type.?); | | |
| 312 | } | | |
| 313 | if (func.params[2].type.? != Key) { | | |
| 314 | if (!emitted_signature) { | | |
| 315 | errors = errors ++ lazy.err_invalid_eql_signature; | | |
| 316 | emitted_signature = true; | | |
| 317 | } | | |
| 318 | errors = errors ++ lazy.deep_prefix ++ "Third parameter must be " ++ @typeName(Key) ++ ", but is " ++ @typeName(func.params[2].type.?); | | |
| 319 | } | | |
| 320 | if (func.return_type.? != bool) { | | |
| 321 | if (!emitted_signature) { | | |
| 322 | errors = errors ++ lazy.err_invalid_eql_signature; | | |
| 323 | emitted_signature = true; | | |
| 324 | } | | |
| 325 | errors = errors ++ lazy.deep_prefix ++ "Return type must be bool, but was " ++ @typeName(func.return_type.?); | | |
| 326 | } | | |
| 327 | // If any of these are generic (null), we cannot verify them. | | |
| 328 | // The call sites check the return type, but cannot check the | | |
| 329 | // parameters. This may cause compile errors with generic hash/eql functions. | | |
| 330 | } | | |
| 331 | } else { | | |
| 332 | errors = errors ++ lazy.err_invalid_eql_signature; | | |
| 333 | } | | |
| 334 | } else { | | |
| 335 | errors = errors ++ lazy.prefix ++ @typeName(Context) ++ " must declare a pub eql function with signature " ++ lazy.eql_signature; | | |
| 336 | } | | |
| 337 | | | |
| 338 | if (errors.len != 0) { | | |
| 339 | // errors begins with a newline (from lazy.prefix) | | |
| 340 | @compileError("Problems found with hash context type " ++ @typeName(Context) ++ ":" ++ errors); | | |
| 341 | } | | |
| 342 | } | | |
| 343 | } | | |
| 344 | | | |
| 345 | /// General purpose hash table. | 119 | /// General purpose hash table. |
| 346 | /// No order is guaranteed and any modification invalidates live iterators. | 120 | /// No order is guaranteed and any modification invalidates live iterators. |
| 347 | /// It provides fast operations (lookup, insertion, deletion) with quite high | 121 | /// It provides fast operations (lookup, insertion, deletion) with quite high |
| ... | @@ -368,10 +142,6 @@ pub fn HashMap( | ... | @@ -368,10 +142,6 @@ pub fn HashMap( |
| 368 | allocator: Allocator, | 142 | allocator: Allocator, |
| 369 | ctx: Context, | 143 | ctx: Context, |
| 370 | | 144 | |
| 371 | comptime { | | |
| 372 | verifyContext(Context, K, K, u64, false); | | |
| 373 | } | | |
| 374 | | | |
| 375 | /// The type of the unmanaged hash map underlying this wrapper | 145 | /// The type of the unmanaged hash map underlying this wrapper |
| 376 | pub const Unmanaged = HashMapUnmanaged(K, V, Context, max_load_percentage); | 146 | pub const Unmanaged = HashMapUnmanaged(K, V, Context, max_load_percentage); |
| 377 | /// An entry, containing pointers to a key and value stored in the map | 147 | /// An entry, containing pointers to a key and value stored in the map |
| ... | @@ -734,10 +504,6 @@ pub fn HashMapUnmanaged( | ... | @@ -734,10 +504,6 @@ pub fn HashMapUnmanaged( |
| 734 | return struct { | 504 | return struct { |
| 735 | const Self = @This(); | 505 | const Self = @This(); |
| 736 | | 506 | |
| 737 | comptime { | | |
| 738 | verifyContext(Context, K, K, u64, false); | | |
| 739 | } | | |
| 740 | | | |
| 741 | // This is actually a midway pointer to the single buffer containing | 507 | // This is actually a midway pointer to the single buffer containing |
| 742 | // a `Header` field, the `Metadata`s and `Entry`s. | 508 | // a `Header` field, the `Metadata`s and `Entry`s. |
| 743 | // At `-@sizeOf(Header)` is the Header field. | 509 | // At `-@sizeOf(Header)` is the Header field. |
| ... | @@ -1097,7 +863,7 @@ pub fn HashMapUnmanaged( | ... | @@ -1097,7 +863,7 @@ pub fn HashMapUnmanaged( |
| 1097 | pub fn putAssumeCapacityNoClobberContext(self: *Self, key: K, value: V, ctx: Context) void { | 863 | pub fn putAssumeCapacityNoClobberContext(self: *Self, key: K, value: V, ctx: Context) void { |
| 1098 | assert(!self.containsContext(key, ctx)); | 864 | assert(!self.containsContext(key, ctx)); |
| 1099 | | 865 | |
| 1100 | const hash = ctx.hash(key); | 866 | const hash: Hash = ctx.hash(key); |
| 1101 | const mask = self.capacity() - 1; | 867 | const mask = self.capacity() - 1; |
| 1102 | var idx: usize = @truncate(hash & mask); | 868 | var idx: usize = @truncate(hash & mask); |
| 1103 | | 869 | |
| ... | @@ -1188,8 +954,6 @@ pub fn HashMapUnmanaged( | ... | @@ -1188,8 +954,6 @@ pub fn HashMapUnmanaged( |
| 1188 | | 954 | |
| 1189 | /// Find the index containing the data for the given key. | 955 | /// Find the index containing the data for the given key. |
| 1190 | fn getIndex(self: Self, key: anytype, ctx: anytype) ?usize { | 956 | fn getIndex(self: Self, key: anytype, ctx: anytype) ?usize { |
| 1191 | comptime verifyContext(@TypeOf(ctx), @TypeOf(key), K, Hash, false); | | |
| 1192 | | | |
| 1193 | if (self.size == 0) { | 957 | if (self.size == 0) { |
| 1194 | // We use cold instead of unlikely to force a jump to this case, | 958 | // We use cold instead of unlikely to force a jump to this case, |
| 1195 | // no matter the weight of the opposing side. | 959 | // no matter the weight of the opposing side. |
| ... | @@ -1199,12 +963,8 @@ pub fn HashMapUnmanaged( | ... | @@ -1199,12 +963,8 @@ pub fn HashMapUnmanaged( |
| 1199 | | 963 | |
| 1200 | // If you get a compile error on this line, it means that your generic hash | 964 | // If you get a compile error on this line, it means that your generic hash |
| 1201 | // function is invalid for these parameters. | 965 | // function is invalid for these parameters. |
| 1202 | const hash = ctx.hash(key); | 966 | const hash: Hash = ctx.hash(key); |
| 1203 | // verifyContext can't verify the return type of generic hash functions, | 967 | |
| 1204 | // so we need to double-check it here. | | |
| 1205 | if (@TypeOf(hash) != Hash) { | | |
| 1206 | @compileError("Context " ++ @typeName(@TypeOf(ctx)) ++ " has a generic hash function that returns the wrong type! " ++ @typeName(Hash) ++ " was expected, but found " ++ @typeName(@TypeOf(hash))); | | |
| 1207 | } | | |
| 1208 | const mask = self.capacity() - 1; | 968 | const mask = self.capacity() - 1; |
| 1209 | const fingerprint = Metadata.takeFingerprint(hash); | 969 | const fingerprint = Metadata.takeFingerprint(hash); |
| 1210 | // Don't loop indefinitely when there are no empty slots. | 970 | // Don't loop indefinitely when there are no empty slots. |
| ... | @@ -1215,15 +975,8 @@ pub fn HashMapUnmanaged( | ... | @@ -1215,15 +975,8 @@ pub fn HashMapUnmanaged( |
| 1215 | while (!metadata[0].isFree() and limit != 0) { | 975 | while (!metadata[0].isFree() and limit != 0) { |
| 1216 | if (metadata[0].isUsed() and metadata[0].fingerprint == fingerprint) { | 976 | if (metadata[0].isUsed() and metadata[0].fingerprint == fingerprint) { |
| 1217 | const test_key = &self.keys()[idx]; | 977 | const test_key = &self.keys()[idx]; |
| 1218 | // If you get a compile error on this line, it means that your generic eql | 978 | |
| 1219 | // function is invalid for these parameters. | 979 | if (ctx.eql(key, test_key.*)) { |
| 1220 | const eql = ctx.eql(key, test_key.*); | | |
| 1221 | // verifyContext can't verify the return type of generic eql functions, | | |
| 1222 | // so we need to double-check it here. | | |
| 1223 | if (@TypeOf(eql) != bool) { | | |
| 1224 | @compileError("Context " ++ @typeName(@TypeOf(ctx)) ++ " has a generic eql function that returns the wrong type! bool was expected, but found " ++ @typeName(@TypeOf(eql))); | | |
| 1225 | } | | |
| 1226 | if (eql) { | | |
| 1227 | return idx; | 980 | return idx; |
| 1228 | } | 981 | } |
| 1229 | } | 982 | } |
| ... | @@ -1378,16 +1131,11 @@ pub fn HashMapUnmanaged( | ... | @@ -1378,16 +1131,11 @@ pub fn HashMapUnmanaged( |
| 1378 | return result; | 1131 | return result; |
| 1379 | } | 1132 | } |
| 1380 | pub fn getOrPutAssumeCapacityAdapted(self: *Self, key: anytype, ctx: anytype) GetOrPutResult { | 1133 | pub fn getOrPutAssumeCapacityAdapted(self: *Self, key: anytype, ctx: anytype) GetOrPutResult { |
| 1381 | comptime verifyContext(@TypeOf(ctx), @TypeOf(key), K, Hash, false); | | |
| 1382 | | 1134 | |
| 1383 | // If you get a compile error on this line, it means that your generic hash | 1135 | // If you get a compile error on this line, it means that your generic hash |
| 1384 | // function is invalid for these parameters. | 1136 | // function is invalid for these parameters. |
| 1385 | const hash = ctx.hash(key); | 1137 | const hash: Hash = ctx.hash(key); |
| 1386 | // verifyContext can't verify the return type of generic hash functions, | 1138 | |
| 1387 | // so we need to double-check it here. | | |
| 1388 | if (@TypeOf(hash) != Hash) { | | |
| 1389 | @compileError("Context " ++ @typeName(@TypeOf(ctx)) ++ " has a generic hash function that returns the wrong type! " ++ @typeName(Hash) ++ " was expected, but found " ++ @typeName(@TypeOf(hash))); | | |
| 1390 | } | | |
| 1391 | const mask = self.capacity() - 1; | 1139 | const mask = self.capacity() - 1; |
| 1392 | const fingerprint = Metadata.takeFingerprint(hash); | 1140 | const fingerprint = Metadata.takeFingerprint(hash); |
| 1393 | var limit = self.capacity(); | 1141 | var limit = self.capacity(); |
| ... | @@ -1400,13 +1148,8 @@ pub fn HashMapUnmanaged( | ... | @@ -1400,13 +1148,8 @@ pub fn HashMapUnmanaged( |
| 1400 | const test_key = &self.keys()[idx]; | 1148 | const test_key = &self.keys()[idx]; |
| 1401 | // If you get a compile error on this line, it means that your generic eql | 1149 | // If you get a compile error on this line, it means that your generic eql |
| 1402 | // function is invalid for these parameters. | 1150 | // function is invalid for these parameters. |
| 1403 | const eql = ctx.eql(key, test_key.*); | 1151 | |
| 1404 | // verifyContext can't verify the return type of generic eql functions, | 1152 | if (ctx.eql(key, test_key.*)) { |
| 1405 | // so we need to double-check it here. | | |
| 1406 | if (@TypeOf(eql) != bool) { | | |
| 1407 | @compileError("Context " ++ @typeName(@TypeOf(ctx)) ++ " has a generic eql function that returns the wrong type! bool was expected, but found " ++ @typeName(@TypeOf(eql))); | | |
| 1408 | } | | |
| 1409 | if (eql) { | | |
| 1410 | return GetOrPutResult{ | 1153 | return GetOrPutResult{ |
| 1411 | .key_ptr = test_key, | 1154 | .key_ptr = test_key, |
| 1412 | .value_ptr = &self.values()[idx], | 1155 | .value_ptr = &self.values()[idx], |