| ... | @@ -19,7 +19,11 @@ const testing = std.testing; | ... | @@ -19,7 +19,11 @@ const testing = std.testing; |
| 19 | /// For unions you can call `.items(.tags)` or `.items(.data)`. | 19 | /// For unions you can call `.items(.tags)` or `.items(.data)`. |
| 20 | pub fn MultiArrayList(comptime T: type) type { | 20 | pub fn MultiArrayList(comptime T: type) type { |
| 21 | return struct { | 21 | return struct { |
| 22 | bytes: [*]align(@alignOf(T)) u8 = undefined, | 22 | /// This pointer is always aligned to the boundary `sizes.big_align`; this is not specified |
| | 23 | /// in the type to avoid `MultiArrayList(T)` depending on the alignment of `T` because this |
| | 24 | /// can lead to dependency loops. See `allocatedBytes` which `@alignCast`s this pointer to |
| | 25 | /// the correct type. |
| | 26 | bytes: [*]u8 = undefined, |
| 23 | len: usize = 0, | 27 | len: usize = 0, |
| 24 | capacity: usize = 0, | 28 | capacity: usize = 0, |
| 25 | | 29 | |
| ... | @@ -133,10 +137,8 @@ pub fn MultiArrayList(comptime T: type) type { | ... | @@ -133,10 +137,8 @@ pub fn MultiArrayList(comptime T: type) type { |
| 133 | if (self.ptrs.len == 0 or self.capacity == 0) { | 137 | if (self.ptrs.len == 0 or self.capacity == 0) { |
| 134 | return .{}; | 138 | return .{}; |
| 135 | } | 139 | } |
| 136 | const unaligned_ptr = self.ptrs[sizes.fields[0]]; | | |
| 137 | const aligned_ptr: [*]align(@alignOf(Elem)) u8 = @alignCast(unaligned_ptr); | | |
| 138 | return .{ | 140 | return .{ |
| 139 | .bytes = aligned_ptr, | 141 | .bytes = self.ptrs[sizes.fields[0]], |
| 140 | .len = self.len, | 142 | .len = self.len, |
| 141 | .capacity = self.capacity, | 143 | .capacity = self.capacity, |
| 142 | }; | 144 | }; |
| ... | @@ -179,6 +181,7 @@ pub fn MultiArrayList(comptime T: type) type { | ... | @@ -179,6 +181,7 @@ pub fn MultiArrayList(comptime T: type) type { |
| 179 | const fields = meta.fields(Elem); | 181 | const fields = meta.fields(Elem); |
| 180 | /// `sizes.bytes` is an array of @sizeOf each T field. Sorted by alignment, descending. | 182 | /// `sizes.bytes` is an array of @sizeOf each T field. Sorted by alignment, descending. |
| 181 | /// `sizes.fields` is an array mapping from `sizes.bytes` array index to field index. | 183 | /// `sizes.fields` is an array mapping from `sizes.bytes` array index to field index. |
| | 184 | /// `sizes.big_align` is the overall alignment of the allocation, which equals the maximum field alignment. |
| 182 | const sizes = blk: { | 185 | const sizes = blk: { |
| 183 | const Data = struct { | 186 | const Data = struct { |
| 184 | size: usize, | 187 | size: usize, |
| ... | @@ -186,12 +189,14 @@ pub fn MultiArrayList(comptime T: type) type { | ... | @@ -186,12 +189,14 @@ pub fn MultiArrayList(comptime T: type) type { |
| 186 | alignment: usize, | 189 | alignment: usize, |
| 187 | }; | 190 | }; |
| 188 | var data: [fields.len]Data = undefined; | 191 | var data: [fields.len]Data = undefined; |
| | 192 | var big_align: usize = 1; |
| 189 | for (fields, 0..) |field_info, i| { | 193 | for (fields, 0..) |field_info, i| { |
| 190 | data[i] = .{ | 194 | data[i] = .{ |
| 191 | .size = @sizeOf(field_info.type), | 195 | .size = @sizeOf(field_info.type), |
| 192 | .size_index = i, | 196 | .size_index = i, |
| 193 | .alignment = if (@sizeOf(field_info.type) == 0) 1 else field_info.alignment, | 197 | .alignment = if (@sizeOf(field_info.type) == 0) 1 else field_info.alignment, |
| 194 | }; | 198 | }; |
| | 199 | big_align = @max(big_align, @alignOf(field_info.type)); |
| 195 | } | 200 | } |
| 196 | const Sort = struct { | 201 | const Sort = struct { |
| 197 | fn lessThan(context: void, lhs: Data, rhs: Data) bool { | 202 | fn lessThan(context: void, lhs: Data, rhs: Data) bool { |
| ... | @@ -210,6 +215,7 @@ pub fn MultiArrayList(comptime T: type) type { | ... | @@ -210,6 +215,7 @@ pub fn MultiArrayList(comptime T: type) type { |
| 210 | break :blk .{ | 215 | break :blk .{ |
| 211 | .bytes = sizes_bytes, | 216 | .bytes = sizes_bytes, |
| 212 | .fields = field_indexes, | 217 | .fields = field_indexes, |
| | 218 | .big_align = mem.Alignment.fromByteUnits(big_align), |
| 213 | }; | 219 | }; |
| 214 | }; | 220 | }; |
| 215 | | 221 | |
| ... | @@ -452,7 +458,7 @@ pub fn MultiArrayList(comptime T: type) type { | ... | @@ -452,7 +458,7 @@ pub fn MultiArrayList(comptime T: type) type { |
| 452 | assert(new_len <= self.capacity); | 458 | assert(new_len <= self.capacity); |
| 453 | assert(new_len <= self.len); | 459 | assert(new_len <= self.len); |
| 454 | | 460 | |
| 455 | const other_bytes = gpa.alignedAlloc(u8, .of(Elem), capacityInBytes(new_len)) catch { | 461 | const other_bytes = gpa.alignedAlloc(u8, sizes.big_align, capacityInBytes(new_len)) catch { |
| 456 | const self_slice = self.slice(); | 462 | const self_slice = self.slice(); |
| 457 | inline for (fields, 0..) |field_info, i| { | 463 | inline for (fields, 0..) |field_info, i| { |
| 458 | if (@sizeOf(field_info.type) != 0) { | 464 | if (@sizeOf(field_info.type) != 0) { |
| ... | @@ -533,7 +539,7 @@ pub fn MultiArrayList(comptime T: type) type { | ... | @@ -533,7 +539,7 @@ pub fn MultiArrayList(comptime T: type) type { |
| 533 | /// `new_capacity` must be greater or equal to `len`. | 539 | /// `new_capacity` must be greater or equal to `len`. |
| 534 | pub fn setCapacity(self: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void { | 540 | pub fn setCapacity(self: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void { |
| 535 | assert(new_capacity >= self.len); | 541 | assert(new_capacity >= self.len); |
| 536 | const new_bytes = try gpa.alignedAlloc(u8, .of(Elem), capacityInBytes(new_capacity)); | 542 | const new_bytes = try gpa.alignedAlloc(u8, sizes.big_align, capacityInBytes(new_capacity)); |
| 537 | if (self.len == 0) { | 543 | if (self.len == 0) { |
| 538 | gpa.free(self.allocatedBytes()); | 544 | gpa.free(self.allocatedBytes()); |
| 539 | self.bytes = new_bytes.ptr; | 545 | self.bytes = new_bytes.ptr; |
| ... | @@ -650,8 +656,8 @@ pub fn MultiArrayList(comptime T: type) type { | ... | @@ -650,8 +656,8 @@ pub fn MultiArrayList(comptime T: type) type { |
| 650 | return elem_bytes * capacity; | 656 | return elem_bytes * capacity; |
| 651 | } | 657 | } |
| 652 | | 658 | |
| 653 | fn allocatedBytes(self: Self) []align(@alignOf(Elem)) u8 { | 659 | fn allocatedBytes(self: Self) []align(sizes.big_align.toByteUnits()) u8 { |
| 654 | return self.bytes[0..capacityInBytes(self.capacity)]; | 660 | return @alignCast(self.bytes[0..capacityInBytes(self.capacity)]); |
| 655 | } | 661 | } |
| 656 | | 662 | |
| 657 | fn FieldType(comptime field: Field) type { | 663 | fn FieldType(comptime field: Field) type { |