-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Description
A pointer is a memory address with metadata.
Here's the metadata a pointer currently has:
typeconstor mutablevolatileor no-side-effects for load/storealign(x)- guaranteed alignment of the address.
if unspecified it is the ABI alignment of the type.:a:b- indicates that the value isabits offset from the address.
I think we can removebbecause it should always be@bitSizeOf(T)
If:a:bis omitted,ais 0 andbis@bitSizeOf(T).
Here is metadata we plan on adding in accepted proposals:
nullor0to indicate that the pointer is null or 0 terminated (see proposal: type for null terminated pointer #265)
This proposal is to add yet another piece of metadata to pointers, which is endianness.
&.Endian.Little u32&.Endian.Big u32
A target has a native endianness. When pointer endianness is unspecified, it
means the native endianness. So on x86_64, &.Endian.Little u32 is the same
as &u32.
The value Endian here can be obtained from @import("builtin").Endian.
We may decide to automatically import builtin into the global namespace,
so it would become builtin.Endian.
Just like the type of a pointer, endianness can be a comptime value:
const E = if (some_comptime_value) Endian.Little else Endian.Big;
fn read(ptr: &.E u32) -> u32 {
return *ptr;
}- A load from a foreign endian pointer performs byte swapping.
- A store to a foreign endian pointer performs byte swapping.
These pointer concepts can be combined, and make sense together:
&.Endian.Big const volatile :2 u4
Here we have a memory address that
constwe should not write throughvolatilethere are side effects from reading from:2we must bit shift the loaded valueu4we must mask only 4 bits from the loaded value.Endian.Bigbit shift and mask assuming the loaded value is big endian
So how does this work with packed structs? (See #307)
const BitField = packed(Endian.Big) struct {
a: u32,
b: u32,
c: u4,
d: u4,
e: u4,
f: u4,
};Here, if you take the address of each field, you get respectively:
a-&.Endian.Big u32.b-&.Endian.Big u32.c-&.Endian.Big :0 u4.d-&.Endian.Big :4 u4.e-&.Endian.Big :0 u4.f-&.Endian.Big :4 u4.
What happened here is that the sub-byte fields have a parent integer, which
zig automatically determines based on byte boundaries.
const BitField = packed(Endian.Big) struct {
a: u32,
b: u32,
data: packed(Endian.Big, u16) struct {
c: u4,
d: u4,
e: u4,
f: u4,
},
};Now we have explicitly decided the parent integer.
data.c-&.Endian.Big :0 u4.data.d-&.Endian.Big :4 u4.data.e-&.Endian.Big :8 u4.data.f-&.Endian.Big :12 u4.