Given the following code which takes a struct of type T IE {thing1: u32, thing2: u8} generates an SOA (struct of arrays) that are supposed to by aligned to to suggestVectorLength(T); But when actually run, it appears that the alignment values provided to StructField.Attributes.@”align” are erased.
pub fn BoundedMultiArray(comptime T: type, capacity: comptime_int) type {
const typeinfo = @typeInfo(T);
const structInfo = switch (typeinfo) {
.@"struct" => |s| s,
else => @compileError("T must be of type struct"),
};
comptime var names: [structInfo.fields.len + 1][:0]const u8 = undefined;
comptime var types: [structInfo.fields.len + 1]type = undefined;
comptime var attributes: [structInfo.fields.len + 1]StructField.Attributes = undefined;
inline for (structInfo.fields, 0..) |value, i| {
names[i] = value.name;
types[i] = [capacity]value.type;
attributes[i].@"align" = @alignOf(@Vector(suggestVectorLength(value.type) orelse 1, value.type));
attributes[i].@"comptime" = false;
attributes[i].default_value_ptr = null;
}
names[names.len - 1] = "occupied";
types[types.len - 1] = [capacity]bool;
attributes[attributes.len - 1].@"align" = @alignOf(@Vector(suggestVectorLength(bool) orelse 1, bool));
attributes[attributes.len - 1].@"comptime" = false;
attributes[attributes.len - 1].default_value_ptr = null;
const y = @Struct(.auto, null, &names, &types, &attributes);
return struct {
data: y,
pos: usize,
pub fn init() @This() {
return .{ .data = undefined, .pos = 0 };
}
pub fn testing(this: @This()) !void {
inline for (names) |value| {
_ = this;
//const x = @field(this.data, value);
//std.debug.print("{any}", .{x});
std.debug.print("{s}\n", .{value});
}
}
pub inline fn testSearching(this: @This(), comptime field: []const u8, comptime target: anytype) void {
@setEvalBranchQuota(1_000_000_000);
const fieldVal = @field(this.data, field);
const FieldValType = @TypeOf(fieldVal);
const FieldValTypeInfo = @typeInfo(FieldValType);
const FieldValArrCast = switch (FieldValTypeInfo) {
.array => |s| s,
else => unreachable,
};
if (@TypeOf(target) != FieldValArrCast.child) {
@compileError("Invalid target type");
}
const vLenth = suggestVectorLength(FieldValArrCast.child) orelse 1;
const targetVec: @Vector(vLenth, FieldValArrCast.child) = @splat(target);
const zeroVec: @Vector(vLenth, u8) = @splat(255);
const indexVec = genIndexVec(vLenth);
var finalResult: u64 = std.math.maxInt(usize);
l: for (0..@divTrunc(capacity, suggestVectorLength(FieldValArrCast.child) orelse 1)) |index| {
const current: @Vector(vLenth, FieldValArrCast.child) = fieldVal[(index * vLenth)..][0..vLenth].*;
const result = @reduce(.Min, @select(u8, targetVec == current, indexVec, zeroVec));
//std.debug.print("{any}", .{index * vLenth});
//std.debug.print("{any}\n", .{targetVec == current});
//std.debug.print("{any}\n", .{@select(u8, targetVec == current, indexVec, zeroVec)});
std.debug.print("{any}\n", .{result});
if (result != 255) {
finalResult = result + (index * vLenth);
break :l;
}
}
std.debug.print("Final Result: {any}", .{finalResult});
}
pub const InsertErrors = error{CapacityReached};
pub fn insert(this: *@This(), value: T) !void {
this.seek() catch |e| {
switch (e) {
error.CapacityReached => {
return InsertErrors.CapacityReached;
},
}
};
inline for (structInfo.fields) |field| {
@field(this.data, field.name)[this.pos] = @field(value, field.name);
}
@field(this.data, "occupied")[this.pos] = true;
this.pos += 1;
}
pub const SeekErrors = error{CapacityReached};
pub fn seek(this: *@This()) SeekErrors!void {
var count: usize = 0;
const vlength = suggestVectorLength(bool) orelse 1;
const indexVec = genIndexVec(vlength);
const nullVec: @Vector(vlength, u8) = @splat(255);
const occupied: [capacity]bool = @field(this.data, "occupied");
const target: @Vector(vlength, bool) = @splat(false);
for (count..@divTrunc(capacity, vlength)) |index| {
const current: @Vector(vlength, bool) = occupied[(index * vlength)..][0..vlength].*;
const result = @reduce(.Min, @select(u8, target == current, indexVec, nullVec));
if (result != 255) {
this.pos = result + (index * vlength);
return;
}
count += vlength;
}
return error.CapacityReached;
}
fn genIndexVec(vlen: comptime_int) @Vector(vlen, u8) {
var res: @Vector(vlen, u8) = undefined;
inline for (0..vlen) |val| {
res[val] = val;
}
return res;
}
pub fn getFieldAlignment(this: @This()) void {
const dataTypeInfo = @typeInfo(@TypeOf(this.data));
inline for (dataTypeInfo.@"struct".fields) |field| {
std.debug.print("{s}: alignment attribute = {any}, actual = {any}\n", .{ field.name, field.alignment, @alignOf(field.type) });
}
}
};
}
const std = @import("std");
const suggestVectorLength = std.simd.suggestVectorLength;
const StructField = std.builtin.Type.StructField;
test "can construct" {
const y = struct { thing1: u8, thing2: u16 };
var x: BoundedMultiArray(y, 1024) = .init();
_ = try x.testing();
const z: u8 = 45;
try x.insert(.{ .thing1 = 44, .thing2 = 55 });
try x.insert(.{ .thing1 = 45, .thing2 = 55 });
x.getFieldAlignment();
_ = x.testSearching("thing1", z);
}
when running
zig test ./src/utility/BoundedMultiArray.zig
thing1
thing2
occupied
thing1: alignment attribute = 32, actual = 1
thing2: alignment attribute = 32, actual = 2
occupied: alignment attribute = 4, actual = 1
1
Final Result: 1All 1 tests passed.
I’ve taken out any references to other modules so that one could copy and paste this code
0.16.0-dev.2510+bcb5218a2
Am I doing something wrong, or is this a bug?