| ... | ... | @@ -46,14 +46,6 @@ namespaces_free_list: std.ArrayListUnmanaged(NamespaceIndex) = .{}, |
| 46 | 46 | /// These are not serialized; it is computed upon deserialization. |
| 47 | 47 | maps: std.ArrayListUnmanaged(FieldMap) = .{}, |
| 48 | 48 | |
| 49 | | /// Used for finding the index inside `string_bytes`. |
| 50 | | string_table: std.HashMapUnmanaged( |
| 51 | | u32, |
| 52 | | void, |
| 53 | | std.hash_map.StringIndexContext, |
| 54 | | std.hash_map.default_max_load_percentage, |
| 55 | | ) = .{}, |
| 56 | | |
| 57 | 49 | /// An index into `tracked_insts` gives a reference to a single ZIR instruction which |
| 58 | 50 | /// persists across incremental updates. |
| 59 | 51 | tracked_insts: std.AutoArrayHashMapUnmanaged(TrackedInst, void) = .{}, |
| ... | ... | @@ -358,22 +350,31 @@ const Local = struct { |
| 358 | 350 | /// node: Garbage.Node, |
| 359 | 351 | /// header: List.Header, |
| 360 | 352 | /// data: [capacity]u32, |
| 361 | | /// tag: [capacity]Tag, |
| 353 | /// tag: [header.capacity]Tag, |
| 362 | 354 | items: List, |
| 363 | 355 | |
| 364 | 356 | /// node: Garbage.Node, |
| 365 | 357 | /// header: List.Header, |
| 366 | | /// extra: [capacity]u32, |
| 358 | /// extra: [header.capacity]u32, |
| 367 | 359 | extra: List, |
| 368 | 360 | |
| 361 | /// node: Garbage.Node, |
| 362 | /// header: List.Header, |
| 363 | /// bytes: [header.capacity]u8, |
| 364 | strings: List, |
| 365 | |
| 369 | 366 | garbage: Garbage, |
| 370 | 367 | |
| 371 | 368 | const List = struct { |
| 372 | 369 | entries: [*]u32, |
| 373 | 370 | |
| 374 | | const empty: List = .{ |
| 375 | | .entries = @constCast(&[_]u32{ 0, 0 })[Header.fields_len..].ptr, |
| 376 | | }; |
| 371 | const empty: List = .{ .entries = @constCast(&(extern struct { |
| 372 | header: Header, |
| 373 | entries: [0]u32, |
| 374 | }{ |
| 375 | .header = .{ .len = 0, .capacity = 0 }, |
| 376 | .entries = .{}, |
| 377 | }).entries) }; |
| 377 | 378 | |
| 378 | 379 | fn acquire(list: *const List) List { |
| 379 | 380 | return .{ .entries = @atomicLoad([*]u32, &list.entries, .acquire) }; |
| ... | ... | @@ -402,63 +403,75 @@ const Local = struct { |
| 402 | 403 | }; |
| 403 | 404 | |
| 404 | 405 | const Shard = struct { |
| 405 | | aligned: void align(std.atomic.cache_line) = {}, |
| 406 | | |
| 407 | | mutate_mutex: std.Thread.Mutex.Recursive, |
| 408 | | |
| 409 | | /// node: Local.Garbage.Node, |
| 410 | | /// header: Map.Header, |
| 411 | | /// entries: [capacity]Map.Entry, |
| 412 | | map: Map, |
| 406 | shared: struct { |
| 407 | map: Map(Index), |
| 408 | string_map: Map(OptionalNullTerminatedString), |
| 409 | } align(std.atomic.cache_line), |
| 410 | mutate: struct { |
| 411 | // TODO: measure cost of sharing unrelated mutate state |
| 412 | map: Mutate align(std.atomic.cache_line), |
| 413 | string_map: Mutate align(std.atomic.cache_line), |
| 414 | }, |
| 413 | 415 | |
| 414 | | const Map = struct { |
| 415 | | entries: [*]u32, |
| 416 | const Mutate = struct { |
| 417 | mutex: std.Thread.Mutex.Recursive, |
| 418 | len: u32, |
| 416 | 419 | |
| 417 | | const empty: Map = .{ |
| 418 | | .entries = @constCast(&[_]u32{ 0, 1, @intFromEnum(Index.none), 0 })[Header.fields_len..].ptr, |
| 420 | const empty: Mutate = .{ |
| 421 | .mutex = std.Thread.Mutex.Recursive.init, |
| 422 | .len = 0, |
| 419 | 423 | }; |
| 424 | }; |
| 420 | 425 | |
| 421 | | fn acquire(map: *const Map) Map { |
| 422 | | return .{ .entries = @atomicLoad([*]u32, &map.entries, .acquire) }; |
| 423 | | } |
| 424 | | fn release(map: *Map, new_map: Map) void { |
| 425 | | @atomicStore([*]u32, &map.entries, new_map.entries, .release); |
| 426 | | } |
| 427 | | |
| 428 | | const Header = extern struct { |
| 429 | | len: u32, |
| 430 | | capacity: u32, |
| 426 | fn Map(comptime Value: type) type { |
| 427 | comptime assert(@typeInfo(Value).Enum.tag_type == u32); |
| 428 | _ = @as(Value, .none); // expected .none key |
| 429 | return struct { |
| 430 | /// node: Local.Garbage.Node, |
| 431 | /// header: Header, |
| 432 | /// entries: [header.capacity]Entry, |
| 433 | entries: [*]Entry, |
| 434 | |
| 435 | const empty: @This() = .{ .entries = @constCast(&(extern struct { |
| 436 | header: Header, |
| 437 | entries: [1]Entry, |
| 438 | }{ |
| 439 | .header = .{ .capacity = 1 }, |
| 440 | .entries = .{.{ .value = .none, .hash = undefined }}, |
| 441 | }).entries) }; |
| 442 | |
| 443 | fn acquire(map: *const @This()) @This() { |
| 444 | return .{ .entries = @atomicLoad([*]Entry, &map.entries, .acquire) }; |
| 445 | } |
| 446 | fn release(map: *@This(), new_map: @This()) void { |
| 447 | @atomicStore([*]Entry, &map.entries, new_map.entries, .release); |
| 448 | } |
| 431 | 449 | |
| 432 | | const fields_len: u32 = @typeInfo(Header).Struct.fields.len; |
| 450 | const Header = extern struct { |
| 451 | capacity: u32, |
| 433 | 452 | |
| 434 | | fn mask(head: *const Header) u32 { |
| 435 | | assert(std.math.isPowerOfTwo(head.capacity)); |
| 436 | | assert(std.math.isPowerOfTwo(Entry.fields_len)); |
| 437 | | return (head.capacity - 1) * Entry.fields_len; |
| 453 | fn mask(head: *const Header) u32 { |
| 454 | assert(std.math.isPowerOfTwo(head.capacity)); |
| 455 | return head.capacity - 1; |
| 456 | } |
| 457 | }; |
| 458 | fn header(map: @This()) *Header { |
| 459 | return &(@as([*]Header, @ptrCast(map.entries)) - 1)[0]; |
| 438 | 460 | } |
| 439 | | }; |
| 440 | | fn header(map: Map) *Header { |
| 441 | | return @ptrCast(map.entries - Header.fields_len); |
| 442 | | } |
| 443 | 461 | |
| 444 | | const Entry = extern struct { |
| 445 | | index: Index, |
| 446 | | hash: u32, |
| 462 | const Entry = extern struct { |
| 463 | value: Value, |
| 464 | hash: u32, |
| 447 | 465 | |
| 448 | | const fields_len: u32 = @typeInfo(Entry).Struct.fields.len; |
| 449 | | |
| 450 | | fn acquire(entry: *const Entry) Index { |
| 451 | | return @atomicLoad(Index, &entry.index, .acquire); |
| 452 | | } |
| 453 | | fn release(entry: *Entry, index: Index) void { |
| 454 | | @atomicStore(Index, &entry.index, index, .release); |
| 455 | | } |
| 466 | fn acquire(entry: *const Entry) Value { |
| 467 | return @atomicLoad(Value, &entry.value, .acquire); |
| 468 | } |
| 469 | fn release(entry: *Entry, value: Value) void { |
| 470 | @atomicStore(Value, &entry.value, value, .release); |
| 471 | } |
| 472 | }; |
| 456 | 473 | }; |
| 457 | | fn at(map: Map, index: usize) *Entry { |
| 458 | | assert(index % Entry.fields_len == 0); |
| 459 | | return @ptrCast(&map.entries[index]); |
| 460 | | } |
| 461 | | }; |
| 474 | } |
| 462 | 475 | }; |
| 463 | 476 | |
| 464 | 477 | const FieldMap = std.ArrayHashMapUnmanaged(void, void, std.array_hash_map.AutoContext(void), false); |
| ... | ... | @@ -618,9 +631,13 @@ pub const NullTerminatedString = enum(u32) { |
| 618 | 631 | return @enumFromInt(@intFromEnum(self)); |
| 619 | 632 | } |
| 620 | 633 | |
| 634 | fn toOverlongSlice(string: NullTerminatedString, ip: *const InternPool) []const u8 { |
| 635 | return ip.string_bytes.items[@intFromEnum(string)..]; |
| 636 | } |
| 637 | |
| 621 | 638 | pub fn toSlice(string: NullTerminatedString, ip: *const InternPool) [:0]const u8 { |
| 622 | | const slice = ip.string_bytes.items[@intFromEnum(string)..]; |
| 623 | | return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0]; |
| 639 | const overlong_slice = string.toOverlongSlice(ip); |
| 640 | return overlong_slice[0..std.mem.indexOfScalar(u8, overlong_slice, 0).? :0]; |
| 624 | 641 | } |
| 625 | 642 | |
| 626 | 643 | pub fn length(string: NullTerminatedString, ip: *const InternPool) u32 { |
| ... | ... | @@ -628,7 +645,10 @@ pub const NullTerminatedString = enum(u32) { |
| 628 | 645 | } |
| 629 | 646 | |
| 630 | 647 | pub fn eqlSlice(string: NullTerminatedString, slice: []const u8, ip: *const InternPool) bool { |
| 631 | | return std.mem.eql(u8, string.toSlice(ip), slice); |
| 648 | const overlong_slice = string.toOverlongSlice(ip); |
| 649 | return overlong_slice.len > slice.len and |
| 650 | std.mem.eql(u8, overlong_slice[0..slice.len], slice) and |
| 651 | overlong_slice[slice.len] == 0; |
| 632 | 652 | } |
| 633 | 653 | |
| 634 | 654 | const Adapter = struct { |
| ... | ... | @@ -4639,14 +4659,21 @@ pub fn init(ip: *InternPool, gpa: Allocator, total_threads: usize) !void { |
| 4639 | 4659 | @memset(ip.local, .{ |
| 4640 | 4660 | .items = Local.List.empty, |
| 4641 | 4661 | .extra = Local.List.empty, |
| 4662 | .strings = Local.List.empty, |
| 4642 | 4663 | .garbage = .{}, |
| 4643 | 4664 | }); |
| 4644 | 4665 | |
| 4645 | 4666 | ip.shard_shift = @intCast(std.math.log2_int_ceil(usize, total_threads)); |
| 4646 | 4667 | ip.shards = try gpa.alloc(Shard, @as(usize, 1) << ip.shard_shift); |
| 4647 | 4668 | @memset(ip.shards, .{ |
| 4648 | | .mutate_mutex = std.Thread.Mutex.Recursive.init, |
| 4649 | | .map = Shard.Map.empty, |
| 4669 | .shared = .{ |
| 4670 | .map = Shard.Map(Index).empty, |
| 4671 | .string_map = Shard.Map(OptionalNullTerminatedString).empty, |
| 4672 | }, |
| 4673 | .mutate = .{ |
| 4674 | .map = Shard.Mutate.empty, |
| 4675 | .string_map = Shard.Mutate.empty, |
| 4676 | }, |
| 4650 | 4677 | }); |
| 4651 | 4678 | |
| 4652 | 4679 | // Reserve string index 0 for an empty string. |
| ... | ... | @@ -4697,8 +4724,6 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void { |
| 4697 | 4724 | for (ip.maps.items) |*map| map.deinit(gpa); |
| 4698 | 4725 | ip.maps.deinit(gpa); |
| 4699 | 4726 | |
| 4700 | | ip.string_table.deinit(gpa); |
| 4701 | | |
| 4702 | 4727 | ip.tracked_insts.deinit(gpa); |
| 4703 | 4728 | |
| 4704 | 4729 | ip.src_hash_deps.deinit(gpa); |
| ... | ... | @@ -5363,9 +5388,9 @@ const GetOrPutKey = union(enum) { |
| 5363 | 5388 | switch (gop.*) { |
| 5364 | 5389 | .existing => unreachable, |
| 5365 | 5390 | .new => |info| { |
| 5366 | | info.shard.map.at(info.map_index).release(index); |
| 5367 | | info.shard.map.header().len += 1; |
| 5368 | | info.shard.mutate_mutex.unlock(); |
| 5391 | info.shard.shared.map.entries[info.map_index].release(index); |
| 5392 | info.shard.mutate.map.len += 1; |
| 5393 | info.shard.mutate.map.mutex.unlock(); |
| 5369 | 5394 | }, |
| 5370 | 5395 | } |
| 5371 | 5396 | gop.* = .{ .existing = index }; |
| ... | ... | @@ -5380,7 +5405,7 @@ const GetOrPutKey = union(enum) { |
| 5380 | 5405 | fn deinit(gop: *GetOrPutKey) void { |
| 5381 | 5406 | switch (gop.*) { |
| 5382 | 5407 | .existing => {}, |
| 5383 | | .new => |info| info.shard.mutate_mutex.unlock(), |
| 5408 | .new => |info| info.shard.mutate.map.mutex.unlock(), |
| 5384 | 5409 | } |
| 5385 | 5410 | gop.* = undefined; |
| 5386 | 5411 | } |
| ... | ... | @@ -5394,70 +5419,69 @@ fn getOrPutKey( |
| 5394 | 5419 | const full_hash = key.hash64(ip); |
| 5395 | 5420 | const hash: u32 = @truncate(full_hash >> 32); |
| 5396 | 5421 | const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))]; |
| 5397 | | var map = shard.map.acquire(); |
| 5422 | var map = shard.shared.map.acquire(); |
| 5423 | const Map = @TypeOf(map); |
| 5398 | 5424 | var map_mask = map.header().mask(); |
| 5399 | 5425 | var map_index = hash; |
| 5400 | | while (true) : (map_index += Shard.Map.Entry.fields_len) { |
| 5426 | while (true) : (map_index += 1) { |
| 5401 | 5427 | map_index &= map_mask; |
| 5402 | | const entry = map.at(map_index); |
| 5428 | const entry = &map.entries[map_index]; |
| 5403 | 5429 | const index = entry.acquire(); |
| 5404 | 5430 | if (index == .none) break; |
| 5405 | | if (entry.hash == hash and ip.indexToKey(index).eql(key, ip)) |
| 5406 | | return .{ .existing = index }; |
| 5431 | if (entry.hash != hash) continue; |
| 5432 | if (ip.indexToKey(index).eql(key, ip)) return .{ .existing = index }; |
| 5407 | 5433 | } |
| 5408 | | shard.mutate_mutex.lock(); |
| 5409 | | errdefer shard.mutate_mutex.unlock(); |
| 5410 | | if (map.entries != shard.map.entries) { |
| 5411 | | map = shard.map; |
| 5434 | shard.mutate.map.mutex.lock(); |
| 5435 | errdefer shard.mutate.map.mutex.unlock(); |
| 5436 | if (map.entries != shard.shared.map.entries) { |
| 5437 | map = shard.shared.map; |
| 5412 | 5438 | map_mask = map.header().mask(); |
| 5413 | 5439 | map_index = hash; |
| 5414 | 5440 | } |
| 5415 | | while (true) : (map_index += Shard.Map.Entry.fields_len) { |
| 5441 | while (true) : (map_index += 1) { |
| 5416 | 5442 | map_index &= map_mask; |
| 5417 | | const entry = map.at(map_index); |
| 5418 | | const index = entry.index; |
| 5443 | const entry = &map.entries[map_index]; |
| 5444 | const index = entry.value; |
| 5419 | 5445 | if (index == .none) break; |
| 5420 | | if (entry.hash == hash and ip.indexToKey(index).eql(key, ip)) { |
| 5421 | | defer shard.mutate_mutex.unlock(); |
| 5446 | if (entry.hash != hash) continue; |
| 5447 | if (ip.indexToKey(index).eql(key, ip)) { |
| 5448 | defer shard.mutate.map.mutex.unlock(); |
| 5422 | 5449 | return .{ .existing = index }; |
| 5423 | 5450 | } |
| 5424 | 5451 | } |
| 5425 | 5452 | const map_header = map.header().*; |
| 5426 | | if (map_header.len >= map_header.capacity * 3 / 5) { |
| 5453 | if (shard.mutate.map.len >= map_header.capacity * 3 / 5) { |
| 5427 | 5454 | const new_map_capacity = map_header.capacity * 2; |
| 5428 | 5455 | const new_map_buf = try gpa.alignedAlloc( |
| 5429 | 5456 | u8, |
| 5430 | 5457 | Local.garbage_align, |
| 5431 | | @sizeOf(Local.Garbage.Node) + (Shard.Map.Header.fields_len + |
| 5432 | | new_map_capacity * Shard.Map.Entry.fields_len) * @sizeOf(u32), |
| 5458 | @sizeOf(Local.Garbage.Node) + @sizeOf(Map.Header) + |
| 5459 | new_map_capacity * @sizeOf(Map.Entry), |
| 5433 | 5460 | ); |
| 5434 | 5461 | const new_node: *Local.Garbage.Node = @ptrCast(new_map_buf.ptr); |
| 5435 | 5462 | new_node.* = .{ .data = .{ .buf_len = new_map_buf.len } }; |
| 5436 | 5463 | ip.local[@intFromEnum(tid)].garbage.prepend(new_node); |
| 5437 | 5464 | const new_map_entries = std.mem.bytesAsSlice( |
| 5438 | | u32, |
| 5439 | | new_map_buf[@sizeOf(Local.Garbage.Node)..], |
| 5440 | | )[Shard.Map.Header.fields_len..]; |
| 5441 | | const new_map: Shard.Map = .{ .entries = new_map_entries.ptr }; |
| 5442 | | new_map.header().* = .{ |
| 5443 | | .len = map_header.len, |
| 5444 | | .capacity = new_map_capacity, |
| 5445 | | }; |
| 5446 | | @memset(new_map_entries, @intFromEnum(Index.none)); |
| 5465 | Map.Entry, |
| 5466 | new_map_buf[@sizeOf(Local.Garbage.Node) + @sizeOf(Map.Header) ..], |
| 5467 | ); |
| 5468 | const new_map: Map = .{ .entries = new_map_entries.ptr }; |
| 5469 | new_map.header().* = .{ .capacity = new_map_capacity }; |
| 5470 | @memset(new_map_entries, .{ .value = .none, .hash = undefined }); |
| 5447 | 5471 | const new_map_mask = new_map.header().mask(); |
| 5448 | 5472 | map_index = 0; |
| 5449 | | while (map_index < map_header.capacity * 2) : (map_index += Shard.Map.Entry.fields_len) { |
| 5450 | | const entry = map.at(map_index); |
| 5451 | | const index = entry.index; |
| 5473 | while (map_index < map_header.capacity) : (map_index += 1) { |
| 5474 | const entry = &map.entries[map_index]; |
| 5475 | const index = entry.value; |
| 5452 | 5476 | if (index == .none) continue; |
| 5453 | 5477 | const item_hash = entry.hash; |
| 5454 | 5478 | var new_map_index = item_hash; |
| 5455 | | while (true) : (new_map_index += Shard.Map.Entry.fields_len) { |
| 5479 | while (true) : (new_map_index += 1) { |
| 5456 | 5480 | new_map_index &= new_map_mask; |
| 5457 | | const new_entry = new_map.at(new_map_index); |
| 5458 | | if (new_entry.index != .none) continue; |
| 5481 | const new_entry = &new_map.entries[new_map_index]; |
| 5482 | if (new_entry.value != .none) continue; |
| 5459 | 5483 | new_entry.* = .{ |
| 5460 | | .index = index, |
| 5484 | .value = index, |
| 5461 | 5485 | .hash = item_hash, |
| 5462 | 5486 | }; |
| 5463 | 5487 | break; |
| ... | ... | @@ -5465,13 +5489,13 @@ fn getOrPutKey( |
| 5465 | 5489 | } |
| 5466 | 5490 | map = new_map; |
| 5467 | 5491 | map_index = hash; |
| 5468 | | while (true) : (map_index += Shard.Map.Entry.fields_len) { |
| 5492 | while (true) : (map_index += 1) { |
| 5469 | 5493 | map_index &= new_map_mask; |
| 5470 | | if (map.at(map_index).index == .none) break; |
| 5494 | if (map.entries[map_index].value == .none) break; |
| 5471 | 5495 | } |
| 5472 | | shard.map.release(new_map); |
| 5496 | shard.shared.map.release(new_map); |
| 5473 | 5497 | } |
| 5474 | | map.at(map_index).hash = hash; |
| 5498 | map.entries[map_index].hash = hash; |
| 5475 | 5499 | return .{ .new = .{ .shard = shard, .map_index = map_index } }; |
| 5476 | 5500 | } |
| 5477 | 5501 | |
| ... | ... | @@ -7689,22 +7713,19 @@ pub fn getIfExists(ip: *const InternPool, key: Key) ?Index { |
| 7689 | 7713 | const full_hash = key.hash64(ip); |
| 7690 | 7714 | const hash: u32 = @truncate(full_hash >> 32); |
| 7691 | 7715 | const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))]; |
| 7692 | | const map = shard.map.acquire(); |
| 7716 | const map = shard.shared.map.acquire(); |
| 7693 | 7717 | const map_mask = map.header().mask(); |
| 7694 | 7718 | var map_index = hash; |
| 7695 | | while (true) : (map_index += Shard.Map.Entry.fields_len) { |
| 7719 | while (true) : (map_index += 1) { |
| 7696 | 7720 | map_index &= map_mask; |
| 7697 | | const entry = map.at(map_index); |
| 7721 | const entry = &map.entries[map_index]; |
| 7698 | 7722 | const index = entry.acquire(); |
| 7699 | 7723 | if (index == .none) return null; |
| 7700 | | if (entry.hash == hash and ip.indexToKey(index).eql(key, ip)) return index; |
| 7724 | if (entry.hash != hash) continue; |
| 7725 | if (ip.indexToKey(index).eql(key, ip)) return index; |
| 7701 | 7726 | } |
| 7702 | 7727 | } |
| 7703 | 7728 | |
| 7704 | | pub fn getAssumeExists(ip: *const InternPool, key: Key) Index { |
| 7705 | | return ip.getIfExists(key).?; |
| 7706 | | } |
| 7707 | | |
| 7708 | 7729 | fn addStringsToMap( |
| 7709 | 7730 | ip: *InternPool, |
| 7710 | 7731 | map_index: MapIndex, |
| ... | ... | @@ -8618,7 +8639,13 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void { |
| 8618 | 8639 | .type_inferred_error_set => 0, |
| 8619 | 8640 | .type_enum_explicit, .type_enum_nonexhaustive => b: { |
| 8620 | 8641 | const info = ip.extraData(EnumExplicit, data); |
| 8621 | | var ints = @typeInfo(EnumExplicit).Struct.fields.len + info.captures_len + info.fields_len; |
| 8642 | var ints = @typeInfo(EnumExplicit).Struct.fields.len; |
| 8643 | if (info.zir_index == .none) ints += 1; |
| 8644 | ints += if (info.captures_len != std.math.maxInt(u32)) |
| 8645 | info.captures_len |
| 8646 | else |
| 8647 | @typeInfo(PackedU64).Struct.fields.len; |
| 8648 | ints += info.fields_len; |
| 8622 | 8649 | if (info.values_map != .none) ints += info.fields_len; |
| 8623 | 8650 | break :b @sizeOf(u32) * ints; |
| 8624 | 8651 | }, |
| ... | ... | @@ -9084,7 +9111,6 @@ pub fn getOrPutTrailingString( |
| 9084 | 9111 | len: usize, |
| 9085 | 9112 | comptime embedded_nulls: EmbeddedNulls, |
| 9086 | 9113 | ) Allocator.Error!embedded_nulls.StringType() { |
| 9087 | | _ = tid; |
| 9088 | 9114 | const string_bytes = &ip.string_bytes; |
| 9089 | 9115 | const str_index: u32 = @intCast(string_bytes.items.len - len); |
| 9090 | 9116 | if (len > 0 and string_bytes.getLast() == 0) { |
| ... | ... | @@ -9101,25 +9127,123 @@ pub fn getOrPutTrailingString( |
| 9101 | 9127 | return @enumFromInt(str_index); |
| 9102 | 9128 | }, |
| 9103 | 9129 | } |
| 9104 | | const gop = try ip.string_table.getOrPutContextAdapted(gpa, key, std.hash_map.StringIndexAdapter{ |
| 9105 | | .bytes = string_bytes, |
| 9106 | | }, std.hash_map.StringIndexContext{ |
| 9107 | | .bytes = string_bytes, |
| 9108 | | }); |
| 9109 | | if (gop.found_existing) { |
| 9130 | const maybe_existing_index = try ip.getOrPutStringValue(gpa, tid, key, @enumFromInt(str_index)); |
| 9131 | if (maybe_existing_index.unwrap()) |existing_index| { |
| 9110 | 9132 | string_bytes.shrinkRetainingCapacity(str_index); |
| 9111 | | return @enumFromInt(gop.key_ptr.*); |
| 9133 | return @enumFromInt(@intFromEnum(existing_index)); |
| 9112 | 9134 | } else { |
| 9113 | | gop.key_ptr.* = str_index; |
| 9114 | 9135 | string_bytes.appendAssumeCapacity(0); |
| 9115 | 9136 | return @enumFromInt(str_index); |
| 9116 | 9137 | } |
| 9117 | 9138 | } |
| 9118 | 9139 | |
| 9119 | | pub fn getString(ip: *InternPool, s: []const u8) OptionalNullTerminatedString { |
| 9120 | | return if (ip.string_table.getKeyAdapted(s, std.hash_map.StringIndexAdapter{ |
| 9121 | | .bytes = &ip.string_bytes, |
| 9122 | | })) |index| @enumFromInt(index) else .none; |
| 9140 | fn getOrPutStringValue( |
| 9141 | ip: *InternPool, |
| 9142 | gpa: Allocator, |
| 9143 | tid: Zcu.PerThread.Id, |
| 9144 | key: []const u8, |
| 9145 | value: NullTerminatedString, |
| 9146 | ) Allocator.Error!OptionalNullTerminatedString { |
| 9147 | const full_hash = Hash.hash(0, key); |
| 9148 | const hash: u32 = @truncate(full_hash >> 32); |
| 9149 | const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))]; |
| 9150 | var map = shard.shared.string_map.acquire(); |
| 9151 | const Map = @TypeOf(map); |
| 9152 | var map_mask = map.header().mask(); |
| 9153 | var map_index = hash; |
| 9154 | while (true) : (map_index += 1) { |
| 9155 | map_index &= map_mask; |
| 9156 | const entry = &map.entries[map_index]; |
| 9157 | const index = entry.acquire().unwrap() orelse break; |
| 9158 | if (entry.hash != hash) continue; |
| 9159 | if (index.eqlSlice(key, ip)) return index.toOptional(); |
| 9160 | } |
| 9161 | shard.mutate.string_map.mutex.lock(); |
| 9162 | defer shard.mutate.string_map.mutex.unlock(); |
| 9163 | if (map.entries != shard.shared.string_map.entries) { |
| 9164 | shard.mutate.string_map.len += 1; |
| 9165 | map = shard.shared.string_map; |
| 9166 | map_mask = map.header().mask(); |
| 9167 | map_index = hash; |
| 9168 | } |
| 9169 | while (true) : (map_index += 1) { |
| 9170 | map_index &= map_mask; |
| 9171 | const entry = &map.entries[map_index]; |
| 9172 | const index = entry.acquire().unwrap() orelse break; |
| 9173 | if (entry.hash != hash) continue; |
| 9174 | if (index.eqlSlice(key, ip)) return index.toOptional(); |
| 9175 | } |
| 9176 | defer shard.mutate.string_map.len += 1; |
| 9177 | const map_header = map.header().*; |
| 9178 | if (shard.mutate.string_map.len < map_header.capacity * 3 / 5) { |
| 9179 | const entry = &map.entries[map_index]; |
| 9180 | entry.hash = hash; |
| 9181 | entry.release(value.toOptional()); |
| 9182 | return .none; |
| 9183 | } |
| 9184 | const new_map_capacity = map_header.capacity * 2; |
| 9185 | const new_map_buf = try gpa.alignedAlloc( |
| 9186 | u8, |
| 9187 | Local.garbage_align, |
| 9188 | @sizeOf(Local.Garbage.Node) + @sizeOf(Map.Header) + |
| 9189 | new_map_capacity * @sizeOf(Map.Entry), |
| 9190 | ); |
| 9191 | const new_node: *Local.Garbage.Node = @ptrCast(new_map_buf.ptr); |
| 9192 | new_node.* = .{ .data = .{ .buf_len = new_map_buf.len } }; |
| 9193 | ip.local[@intFromEnum(tid)].garbage.prepend(new_node); |
| 9194 | const new_map_entries = std.mem.bytesAsSlice( |
| 9195 | Map.Entry, |
| 9196 | new_map_buf[@sizeOf(Local.Garbage.Node) + @sizeOf(Map.Header) ..], |
| 9197 | ); |
| 9198 | const new_map: Map = .{ .entries = new_map_entries.ptr }; |
| 9199 | new_map.header().* = .{ .capacity = new_map_capacity }; |
| 9200 | @memset(new_map_entries, .{ .value = .none, .hash = undefined }); |
| 9201 | const new_map_mask = new_map.header().mask(); |
| 9202 | map_index = 0; |
| 9203 | while (map_index < map_header.capacity) : (map_index += 1) { |
| 9204 | const entry = &map.entries[map_index]; |
| 9205 | const index = entry.value.unwrap() orelse continue; |
| 9206 | const item_hash = entry.hash; |
| 9207 | var new_map_index = item_hash; |
| 9208 | while (true) : (new_map_index += 1) { |
| 9209 | new_map_index &= new_map_mask; |
| 9210 | const new_entry = &new_map.entries[new_map_index]; |
| 9211 | if (new_entry.value != .none) continue; |
| 9212 | new_entry.* = .{ |
| 9213 | .value = index.toOptional(), |
| 9214 | .hash = item_hash, |
| 9215 | }; |
| 9216 | break; |
| 9217 | } |
| 9218 | } |
| 9219 | map = new_map; |
| 9220 | map_index = hash; |
| 9221 | while (true) : (map_index += 1) { |
| 9222 | map_index &= new_map_mask; |
| 9223 | if (map.entries[map_index].value == .none) break; |
| 9224 | } |
| 9225 | map.entries[map_index] = .{ |
| 9226 | .value = value.toOptional(), |
| 9227 | .hash = hash, |
| 9228 | }; |
| 9229 | shard.shared.string_map.release(new_map); |
| 9230 | return .none; |
| 9231 | } |
| 9232 | |
| 9233 | pub fn getString(ip: *InternPool, key: []const u8) OptionalNullTerminatedString { |
| 9234 | const full_hash = Hash.hash(0, key); |
| 9235 | const hash: u32 = @truncate(full_hash >> 32); |
| 9236 | const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))]; |
| 9237 | const map = shard.shared.string_map.acquire(); |
| 9238 | const map_mask = map.header().mask(); |
| 9239 | var map_index = hash; |
| 9240 | while (true) : (map_index += 1) { |
| 9241 | map_index &= map_mask; |
| 9242 | const entry = map.at(map_index); |
| 9243 | const index = entry.acquire().unwrap() orelse return null; |
| 9244 | if (entry.hash != hash) continue; |
| 9245 | if (index.eqlSlice(key, ip)) return index; |
| 9246 | } |
| 9123 | 9247 | } |
| 9124 | 9248 | |
| 9125 | 9249 | pub fn typeOf(ip: *const InternPool, index: Index) Index { |