I find myself allocating a big chunk of memory of u8s and then repartitioning it into equally sized slices of []u8. I need to build some kind of table and the slices here are the rows in that table. Now to my question:
Is there a good std function for this (I saw std.mem.window as the closes thing)
If not, how can i do this in a better way?
I just dont really like doing two allocations for this kind of problem.
Maybe I should just write my own function. Here is some example code:
You could allocate your data as, or cast it to, a slice of arrays (e.g. [][10]u8) although this requires you to know one dimension at comptime.
Another option is allocating it and casting the first segment into the table
var data = try gpa.allocWithOptions(
u8,
@sizeOf([]u8) * rows + cols * rows,
.fromByteUnits(@alignOf([]u8)),
null,
);
defer gpa.free(data);
const chunk = data[@sizeOf([]u8) * rows ..];
const table: [][]const u8 = @ptrCast(data[0 .. @sizeOf([]u8) * rows]);
var it = std.mem.window(u8, chunk, cols, cols);
for (table) |*row| row.* = it.next() orelse unreachable;
Given row and col are not comptime known when splitting it, you’re either going to have an iterator or an allocating function so I believe using std.mem.window is probably as good as you’re going to get if you don’t mind std.mem.WindowIterator.next returning ?[]const T.
edit note: corrected memory alignment as pointed out by matklad and vulpesx
@alignCast only asserts the runtime alignment is correct! The data is allocated at the natural alignment of u8 (1), whereas the alignment needed for table is 8 (or 4 on x32).
In otherworlds, it is entirely possible for data to be allocated at a runtime alignment that does not pass the @alignCast safety check, crashing your code.
Instead, use allocWithOptions and pass @alignOf([]u8) as the alignment. Then you can remove the @alignCast since they will be comptime known to be the same alignment.
The approach that came to my mind would be to do away with the separate table variable entirely and turn row access into a member function of a struct, something like:
I chose something in the middle of @DoingGitStuff and @squeek502 and I want to make it generic. More specifically I also want to be able to generate tables of u24, it’s a weird number, i know, but it’s necessary for what I’m doing.
This code compiles fine, but during runtime when I check the length of the Table(u24).table.len member, it is multiplied by 4, i.e. I encountered an issue where it should have been 4 but it was 16.
I am really new to alignment things, what am I doing wrong here?
Thanks for the hint, I know that u24 is a aligned by 4, but I still dont get how this line const table: [][]T = @ptrCast(data[0 .. @sizeOf([]T) * rows]); builds 16 rows instead of 4 ones :<
The issue is not alignment; your length calculation is in bytes, but the length the alloc* function takes is in units of T. simply always alloc u8 and cast.
But you do have an alignment problem! The alignment should be the max of the alignment of all the data, that is max of T and []T.
You haven’t run into this issue yet because u24 has a smaller alignment. But you should at least document your assumption, or remove it.
Another thing to be aware of: if the header data (your table) is smaller than T you will have to add padding to your allocation, and calculate an appropriately aligned start to the chunk slice.
Much easer to just assert that isn’t the case.
Lastly, it is inefficient to store multiple slices to the same backing slice. Ofc you dont have to care about that!
Thank you! You’re right, I think it’d be easier and more readable if I just switched back to having one chunk and methods that can index rows etc. So exactly like @squeek502 proposed.