Skip to content

Add endianness as one of the pointer properties #649

@andrewrk

Description

@andrewrk

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    proposalThis issue suggests modifications. If it also has the "accepted" label then it is planned.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions