fn nodeAt(self: ChunkRing, index: Index) *Node {
// buffer is of type []u8
// node is of type struct {
// prev: Index,
// next: Index,
// size: u32
// }
return @ptrCast(@alignCast(&self.buffer[index.toInt().?])); // this line panics
}
Index.toInt().? * @sizeOf(Node), maybe? Hard to say with what youâve shown, but itâs pretty easy to under-align packing structs into a byte buffer, and thatâs what the bug is. You could return *align(1) Node but that will come with its own hassle.
In my current implementation every single byte can be the start of a Node. A node is always followed by any number of reserved bytes, their count being saved in the size field.
I think I still am not really understanding alignment Is it a property of memory itself and not the slice? Would it be better to make the byte buffer be alignOf(Node) and just loose like 3 bytes maximum per Node-Associated Memory?
I am sort of confused about the incorrect alignment error. Of course it is incorrect, thatâs why I am trying to cast it.
Edit: Wait isnt it a thing that alignment can only be decreased? Still I am confused about what alignment is fundamentally a property of
A struct has a ânatural alignmentâ. In your case the u32 guarantees that this alignment will be at least four bytes, I donât know what size Index is.
The natural alignment is always a power of two, and itâs how the compiler expects data to be laid out in memory. For an alignment of 4, the address is always divisible by four, and so on, and the compiler, left to its own devices, will always lay out the structure according to that alignment.
The type of a pointer to T has the natural alignment of T, unless otherwise specified with the align attribute.
So âevery single byte can be the start of a Nodeâ is not compatible with using *Node. However, it is compatible with using *align(1) Node, thatâs a pointer to a Node, but with the specification that any pointer (divisible by one, trivially) can point to such a node.
Oddly-aligned pointers are a bit annoying, but if thatâs how you need things to be, you can make it work. Modern chips allow it, and the performance penalty is little to none.
alignment is about divisibility of memory addresses yes. it is useful for compilers to be able to assume natural alignment, since they might need to emit different instructions for underaligned loads/stores.
Your options are: align the Nodes naturally, in which case you can use *Node, or pack them as you have been doing, in which case you have to use *align(1) Node, because (letâs say Index is also 32 bit) *Node is an *align(4) Node.
I would be inclined to align them naturally, because of the aforementioned annoyance. But it isnât all that bad, either is a justifiable choice, but youâll need to pick one.
Yea Iâll go with aligning things naturally. The entire datastructure is basically a queue with dynamically sized nodes stored in a buffer. The plan is to try and make a little tcp-over-udp thing and this is where I want to store the packets. I think like 3 bytes per packet of lost data is utterly justifiable lol
How does C do this btw? I had to write an allocator with nearly the same strat for uni but the compiler never complained about me just casting nodes that start literally anywhere and just gave me plain node pointers.
C doesnât have pointee alignment as part of its type system, let alone verifying alignment when casting.
While C does have alignment for types it gave little control over it for a long time, youâd have to rely on compiler specific extensions. Only recently has it given some control into the standard, but I donât think itâs nearly as much control as zig gives you (donât quote me on that).
@alignCast never changes the actual alignment of the pointer, it just allows you to tell the compiler to change the alignment of the type and if that pointer has the wrong alignment, then the compiler will complain to you (in safe modes), because you broke your promise to the compiler that it is fine to change it.
Using @alignCast to try to change alignment to an invalid alignment is a programming error and the @alignCast itself works like an (safe-mode) assertion, not a conversion.
If you want to change the alignment (in cases where that pointer wasnât compatible with your target alignment to begin with) then you need to move the data in memory to an address that is aligned before trying to cast it. (Or use non-naturally-aligned data like explained by @mnemnion)