A pointer is a memory address with metadata.
Here's the metadata a pointer currently has:
type
const or mutable
volatile or no-side-effects for load/store
align(x) - guaranteed alignment of the address.
if unspecified it is the ABI alignment of the type.
:a:b - indicates that the value is a bits offset from the address.
I think we can remove b because it should always be @bitSizeOf(T)
If :a:b is omitted, a is 0 and b is @bitSizeOf(T).
Here is metadata we plan on adding in accepted proposals:
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
const we should not write through
volatile there are side effects from reading from
:2 we must bit shift the loaded value
u4 we must mask only 4 bits from the loaded value
.Endian.Big bit 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.
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 remove
bbecause 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 u32A target has a native endianness. When pointer endianness is unspecified, it
means the native endianness. So on x86_64,
&.Endian.Little u32is the sameas
&u32.The value
Endianhere can be obtained from@import("builtin").Endian.We may decide to automatically import
builtininto the global namespace,so it would become
builtin.Endian.Just like the type of a pointer, endianness can be a comptime value:
These pointer concepts can be combined, and make sense together:
&.Endian.Big const volatile :2 u4Here 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 endianSo how does this work with packed structs? (See #307)
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.
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.