const io = @import("./io.zig"); const std = @import("std"); pub fn ElementPtr(comptime Slice: type) type { const pointer_info = @typeInfo(Slice).Pointer; return @Type(.{ .Pointer = .{ .size = .One, .is_const = pointer_info.is_const, .is_volatile = pointer_info.is_volatile, .alignment = pointer_info.alignment, .address_space = pointer_info.address_space, .child = pointer_info.child, .is_allowzero = false, .sentinel = null, }, }); } pub fn Parallel(comptime Type: type) type { const fields = @typeInfo(Type).Struct.fields; const alignment = @alignOf(Type); return struct { len: usize = 0, ptrs: [fields.len][*]align (alignment) io.Byte = undefined, pub fn Element(comptime field: Field) type { return fields[@intFromEnum(field)].type; } pub const Field = std.meta.FieldEnum(Type); const Self = @This(); const all_fields = std.enums.values(Field); pub fn ptr(self: Self, comptime field: Field) [*]align (alignment) Element(field) { return @as([*]align (alignment) Element(field), @ptrCast(self.ptrs[@intFromEnum(field)])); } pub fn slice(self: Self, comptime field: Field) []align (alignment) Element(field) { return self.ptr(field)[0 .. self.len]; } pub fn slice_all(self: Self, off: usize, len: usize) ?Self { if (len > self.len or off > len) { return null; } var sliced = Self{.len = len}; inline for (0 .. fields.len) |i| { sliced.ptrs[i] = @ptrFromInt(@intFromPtr(self.ptrs[i]) + (@sizeOf(Element(all_fields[i])) * off)); } return sliced; } pub fn get(self: Self, comptime field: Field, index: usize) ?Element(field) { if (index >= self.len) { return null; } return self.ptr(field)[index]; } pub fn get_ptr(self: Self, comptime field: Field, index: usize) ?*Element(field) { if (index >= self.len) { return null; } return &self.ptr(field)[index]; } pub fn set(self: Self, comptime field: Field, index: usize, value: Element(field)) bool { if (index >= self.len) { return false; } self.slice(field)[index] = value; return true; } pub fn set_all(self: Self, index: usize, value: Type) bool { if (index >= self.len) { return false; } inline for (0 .. fields.len) |i| { self.slice(all_fields[i])[index] = @field(value, fields[i].name); } return true; } }; } pub fn get(slice: anytype, index: usize) ?@typeInfo(@TypeOf(slice)).Pointer.child { if (index >= slice.len) { return null; } return slice[index]; } pub fn get_ptr(slice: anytype, index: usize) ?ElementPtr(@TypeOf(slice)) { if (index >= slice.len) { return null; } return &slice[index]; } pub fn parallel_alloc(comptime Element: type, allocator: std.mem.Allocator, n: usize) std.mem.Allocator.Error!Parallel(Element) { const alignment = @alignOf(Element); const Slices = Parallel(Element); var buffers = @as([std.enums.values(Slices.Field).len][]align (alignment) io.Byte, undefined); var buffers_allocated = @as(usize, 0); var allocated = Slices{.len = n}; errdefer { for (0 .. buffers_allocated) |i| { allocator.free(buffers[i]); } } const fields = @typeInfo(Element).Struct.fields; inline for (0 .. fields.len) |i| { buffers[i] = try allocator.alignedAlloc(io.Byte, alignment, @sizeOf(fields[i].type) * n); buffers_allocated += 1; allocated.ptrs[i] = buffers[i].ptr; } return allocated; } pub fn parallel_copy(comptime Element: type, target: Parallel(Element), origin: Parallel(Element)) void { inline for (comptime std.enums.values(Parallel(Element).Field)) |field| { @memcpy(target.slice(field), origin.slice(field)); } } pub fn parallel_free(comptime Element: type, allocator: std.mem.Allocator, buffers: Parallel(Element)) void { inline for (comptime std.enums.values(Parallel(Element).Field)) |field| { allocator.free(buffers.slice(field)); } }