diff --git a/src/module/Archetype.zig b/src/module/Archetype.zig index 157fabe0..773963f1 100644 --- a/src/module/Archetype.zig +++ b/src/module/Archetype.zig @@ -73,18 +73,6 @@ pub fn appendUndefined(storage: *Archetype, gpa: Allocator) !u32 { return row_index; } -// TODO: comptime: missing a runtime variant of this function -pub fn append(storage: *Archetype, gpa: Allocator, row: anytype) !u32 { - debugAssertRowType(storage, row); - - try storage.ensureUnusedCapacity(gpa, 1); - assert(storage.len < storage.capacity); - storage.len += 1; - - storage.setRow(storage.len - 1, row); - return storage.len - 1; -} - pub fn undoAppend(storage: *Archetype) void { storage.len -= 1; } @@ -107,6 +95,8 @@ pub fn ensureTotalCapacity(storage: *Archetype, gpa: Allocator, new_capacity: us return storage.setCapacity(gpa, better_capacity); } +const max_align_padding = 64; + /// Sets the capacity to exactly `new_capacity` rows total /// /// Asserts `new_capacity >= storage.len`, if you want to shrink capacity then change the len @@ -117,8 +107,9 @@ pub fn setCapacity(storage: *Archetype, gpa: Allocator, new_capacity: usize) !vo // TODO: ensure columns are sorted by type_id for (storage.columns) |*column| { const old_values = column.values; - const new_values = try gpa.alloc(u8, new_capacity * column.size); + const new_values = try gpa.alloc(u8, (new_capacity * column.size) + max_align_padding); if (storage.capacity > 0) { + // Note: this copies alignment padding (which is fine, since it is a constant amount.) @memcpy(new_values[0..old_values.len], old_values); gpa.free(old_values); } @@ -127,22 +118,6 @@ pub fn setCapacity(storage: *Archetype, gpa: Allocator, new_capacity: usize) !vo storage.capacity = @as(u32, @intCast(new_capacity)); } -// TODO: comptime: missing a runtime variant of this function -/// Sets the entire row's values in the table. -pub fn setRow(storage: *Archetype, row_index: u32, row: anytype) void { - debugAssertRowType(storage, row); - - const fields = std.meta.fields(@TypeOf(row)); - inline for (fields, 0..) |field, index| { - const ColumnType = field.type; - if (@sizeOf(ColumnType) == 0) continue; - - const column = storage.columns[index]; - const column_values = @as([*]ColumnType, @ptrCast(@alignCast(column.values.ptr))); - column_values[row_index] = @field(row, field.name); - } -} - /// Sets the value of the named components (columns) for the given row in the table. pub fn set(storage: *Archetype, row_index: u32, name: StringTable.Index, component: anytype) void { const ColumnType = @TypeOf(component); @@ -197,10 +172,11 @@ pub fn remove(storage: *Archetype, row_index: u32) void { assert(row_index < storage.len); if (storage.len > 1 and row_index != storage.len - 1) { for (storage.columns) |column| { + const aligned_values = storage.aligned(&column, column.values); const dstStart = column.size * row_index; - const dst = column.values[dstStart .. dstStart + column.size]; + const dst = aligned_values[dstStart .. dstStart + column.size]; const srcStart = column.size * (storage.len - 1); - const src = column.values[srcStart .. srcStart + column.size]; + const src = aligned_values[srcStart .. srcStart + column.size]; @memcpy(dst, src); } } @@ -220,6 +196,17 @@ pub fn hasComponent(storage: *Archetype, name: StringTable.Index) bool { return storage.columnByName(name) != null; } +/// Given a column.values slice which is unaligned, adds the neccessary padding +/// to achieve alignment for the column's data type, and returns the padded slice. +inline fn aligned(storage: *Archetype, column: *const Column, values: []u8) []u8 { + const aligned_addr = std.mem.alignForward(usize, @intFromPtr(values.ptr), column.alignment); + if (is_debug) { + const padding_bytes = aligned_addr - @as(usize, @intFromPtr(values.ptr)); + if (padding_bytes > max_align_padding) @panic("mach: max_align_padding is too low, this is a bug"); + } + return @as([*]u8, @ptrFromInt(aligned_addr))[0 .. storage.capacity * column.size]; +} + pub fn getColumnValues(storage: *Archetype, name: StringTable.Index, comptime ColumnType: type) ?[]ColumnType { const values = storage.getColumnValuesRaw(name) orelse return null; if (is_debug) debugAssertColumnType(storage, storage.columnByName(name).?, ColumnType); @@ -230,7 +217,7 @@ pub fn getColumnValues(storage: *Archetype, name: StringTable.Index, comptime Co pub fn getColumnValuesRaw(storage: *Archetype, name: StringTable.Index) ?[]u8 { const column = storage.columnByName(name) orelse return null; - return column.values; + return storage.aligned(column, column.values); } pub inline fn columnByName(storage: *Archetype, name: StringTable.Index) ?*Column { diff --git a/src/module/entities.zig b/src/module/entities.zig index 27ea1966..1c70aefe 100644 --- a/src/module/entities.zig +++ b/src/module/entities.zig @@ -254,7 +254,8 @@ pub fn Database(comptime modules: anytype) type { assert(archetype_entry.found_existing); var void_archetype = archetype_entry.ptr; - const new_row = try void_archetype.append(entities.allocator, .{ .id = new_id }); + const new_row = try void_archetype.appendUndefined(entities.allocator); + void_archetype.set(new_row, entities.id_name, new_id); const void_pointer = Pointer{ .archetype_index = 0, // void archetype is guaranteed to be first index .row_index = new_row,