| ... | ... | @@ -116,232 +116,6 @@ pub const StringIndexAdapter = struct { |
| 116 | 116 | |
| 117 | 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 | 119 | /// General purpose hash table. |
| 346 | 120 | /// No order is guaranteed and any modification invalidates live iterators. |
| 347 | 121 | /// It provides fast operations (lookup, insertion, deletion) with quite high |
| ... | ... | @@ -368,10 +142,6 @@ pub fn HashMap( |
| 368 | 142 | allocator: Allocator, |
| 369 | 143 | ctx: Context, |
| 370 | 144 | |
| 371 | | comptime { |
| 372 | | verifyContext(Context, K, K, u64, false); |
| 373 | | } |
| 374 | | |
| 375 | 145 | /// The type of the unmanaged hash map underlying this wrapper |
| 376 | 146 | pub const Unmanaged = HashMapUnmanaged(K, V, Context, max_load_percentage); |
| 377 | 147 | /// An entry, containing pointers to a key and value stored in the map |
| ... | ... | @@ -734,10 +504,6 @@ pub fn HashMapUnmanaged( |
| 734 | 504 | return struct { |
| 735 | 505 | const Self = @This(); |
| 736 | 506 | |
| 737 | | comptime { |
| 738 | | verifyContext(Context, K, K, u64, false); |
| 739 | | } |
| 740 | | |
| 741 | 507 | // This is actually a midway pointer to the single buffer containing |
| 742 | 508 | // a `Header` field, the `Metadata`s and `Entry`s. |
| 743 | 509 | // At `-@sizeOf(Header)` is the Header field. |
| ... | ... | @@ -1097,7 +863,7 @@ pub fn HashMapUnmanaged( |
| 1097 | 863 | pub fn putAssumeCapacityNoClobberContext(self: *Self, key: K, value: V, ctx: Context) void { |
| 1098 | 864 | assert(!self.containsContext(key, ctx)); |
| 1099 | 865 | |
| 1100 | | const hash = ctx.hash(key); |
| 866 | const hash: Hash = ctx.hash(key); |
| 1101 | 867 | const mask = self.capacity() - 1; |
| 1102 | 868 | var idx: usize = @truncate(hash & mask); |
| 1103 | 869 | |
| ... | ... | @@ -1188,8 +954,6 @@ pub fn HashMapUnmanaged( |
| 1188 | 954 | |
| 1189 | 955 | /// Find the index containing the data for the given key. |
| 1190 | 956 | fn getIndex(self: Self, key: anytype, ctx: anytype) ?usize { |
| 1191 | | comptime verifyContext(@TypeOf(ctx), @TypeOf(key), K, Hash, false); |
| 1192 | | |
| 1193 | 957 | if (self.size == 0) { |
| 1194 | 958 | // We use cold instead of unlikely to force a jump to this case, |
| 1195 | 959 | // no matter the weight of the opposing side. |
| ... | ... | @@ -1199,12 +963,8 @@ pub fn HashMapUnmanaged( |
| 1199 | 963 | |
| 1200 | 964 | // If you get a compile error on this line, it means that your generic hash |
| 1201 | 965 | // function is invalid for these parameters. |
| 1202 | | const hash = ctx.hash(key); |
| 1203 | | // verifyContext can't verify the return type of generic hash functions, |
| 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 | | } |
| 966 | const hash: Hash = ctx.hash(key); |
| 967 | |
| 1208 | 968 | const mask = self.capacity() - 1; |
| 1209 | 969 | const fingerprint = Metadata.takeFingerprint(hash); |
| 1210 | 970 | // Don't loop indefinitely when there are no empty slots. |
| ... | ... | @@ -1215,15 +975,8 @@ pub fn HashMapUnmanaged( |
| 1215 | 975 | while (!metadata[0].isFree() and limit != 0) { |
| 1216 | 976 | if (metadata[0].isUsed() and metadata[0].fingerprint == fingerprint) { |
| 1217 | 977 | const test_key = &self.keys()[idx]; |
| 1218 | | // If you get a compile error on this line, it means that your generic eql |
| 1219 | | // function is invalid for these parameters. |
| 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) { |
| 978 | |
| 979 | if (ctx.eql(key, test_key.*)) { |
| 1227 | 980 | return idx; |
| 1228 | 981 | } |
| 1229 | 982 | } |
| ... | ... | @@ -1378,16 +1131,11 @@ pub fn HashMapUnmanaged( |
| 1378 | 1131 | return result; |
| 1379 | 1132 | } |
| 1380 | 1133 | pub fn getOrPutAssumeCapacityAdapted(self: *Self, key: anytype, ctx: anytype) GetOrPutResult { |
| 1381 | | comptime verifyContext(@TypeOf(ctx), @TypeOf(key), K, Hash, false); |
| 1382 | 1134 | |
| 1383 | 1135 | // If you get a compile error on this line, it means that your generic hash |
| 1384 | 1136 | // function is invalid for these parameters. |
| 1385 | | const hash = ctx.hash(key); |
| 1386 | | // verifyContext can't verify the return type of generic hash functions, |
| 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 | | } |
| 1137 | const hash: Hash = ctx.hash(key); |
| 1138 | |
| 1391 | 1139 | const mask = self.capacity() - 1; |
| 1392 | 1140 | const fingerprint = Metadata.takeFingerprint(hash); |
| 1393 | 1141 | var limit = self.capacity(); |
| ... | ... | @@ -1400,13 +1148,8 @@ pub fn HashMapUnmanaged( |
| 1400 | 1148 | const test_key = &self.keys()[idx]; |
| 1401 | 1149 | // If you get a compile error on this line, it means that your generic eql |
| 1402 | 1150 | // function is invalid for these parameters. |
| 1403 | | const eql = ctx.eql(key, test_key.*); |
| 1404 | | // verifyContext can't verify the return type of generic eql functions, |
| 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) { |
| 1151 | |
| 1152 | if (ctx.eql(key, test_key.*)) { |
| 1410 | 1153 | return GetOrPutResult{ |
| 1411 | 1154 | .key_ptr = test_key, |
| 1412 | 1155 | .value_ptr = &self.values()[idx], |