Help with ptr casting inconsistency

Hello! I’m quite stumped on something and was hoping that someone here can help diagnose what’s going on. I’m doing some ptr arithmatic stuff, and something isn’t working as I was expecting it to, here’s an example test to reproduce what I’m seeing:

const OFFSET = 13;
fn getU64Ptr(data: *[1024]u8) *u64 {
    var bytes align(@alignOf(u64)) = data[OFFSET .. OFFSET + @sizeOf(u64)];
    const u64_ptr: *u64 = @ptrCast(&bytes);

    return u64_ptr;
}
fn initU64Value(data: *[1024]u8) void {
    const ptr = getU64Ptr(data);
    std.debug.print("PTR 2 {d}\n", .{ptr});
    ptr.* = 0;
    std.debug.print("PTR 2 VAL {d}\n", .{ptr.*});
}

test "example" {
    var data: [1024]u8 = undefined;
    // set up some initial bytes
    for (0..1024) |i| {
        var intBytes: [@sizeOf(usize)]u8 = undefined;
        mem.writePackedIntNative(usize, intBytes[0..], 0, i);
        data[i] = intBytes[0];
    }
    const ptr = getU64Ptr(&data);
    std.debug.print("PTR 1 {*}\n", .{ptr});
    std.debug.print("PTR 1 VAL {d}\n", .{ptr.*});
    initU64Value(&data);
    std.debug.print("PTR 1 VAL {d}\n", .{ptr.*});
}

I really expected the last log to be 0, but it’s not. It seems that getU64Ptr doesn’t return the same pointer every time, which I really was not expecting. Can anyone explain why?

bytes is a slice, &bytes returns the address of the slice, you need bytes.ptr.
bytes must be a const since it is not modified.
With the @ptrCast you also need @alignCast, and this is not going to work for OFFSET=13.

fn getU64Ptr(data: *[1024]u8) *u64 {
    const bytes align(@alignOf(u64)) = data[OFFSET .. OFFSET + @sizeOf(u64)];
    const u64_ptr: *u64 = @ptrCast(@alignCast(bytes.ptr));
    return u64_ptr;
}
1 Like

Ah I see, makes sense thank you! I’m following along with some c code, the gist is that there’s a buffer with data adhering to a custom format like

| byte 0    | byte 1 | bytes n - n+8 |
| node_type | ...    | u64 id.       |

I’m trying to get a pointer to the u64 value so it can be read/updated. The c code I’m following gets a void ptr to that location, and it seems it automatically can be cast into a u64 ptr. Are you familiar with a way to do this in zig?

Do you have any alignment guarantees on that buffer? If you don’t, you’re not going to enjoy trying to cast that buffer.

Instead, I would go the opposite way:

  1. Make a value of type T
  2. Get a slice of bytes to memory of said value
  3. @memcpy the bytes from the buffer into the value
  4. use your value

Very safe bet, imo - no casting shenanigans to buffers that could be aligned to anything or everything.

Edited: Using @mnemnion 's flag value.

const std = @import("std");

pub fn main() !void {

    // buffer to your source bytes
    var buffer: [10]u8 = .{ 0 } ** 10;

    // consider default value that can be used for debugging
    var value: usize = 0xa5a5a5a5a5a5a5a5;

    // cast to a slice of target bytes
    const bytes = std.mem.asBytes(&value);

    // copy your source to target
    @memcpy(bytes, buffer[2..]);

    // check that you get your result
    std.debug.print("Should be zero: {}\n", .{ value });

}
3 Likes

Might be my paranoia speaking, but when doing weird casting stuff I like to ‘color’ all the bytes, usually like this:

// even more obvious flag
var value: usize = 0xa5a5a5a5a5a5a5a5;

Same principle though, just slightly higher odds of catching in a debugger if I clip some edge or other. I even know that decimal printing that value starts with 1193 as long as the high four is still there.

2 Likes

Ohh that should work for my needs! Awesome, thanks a bunch folks.

Just for my own edification, is doing a mem.asBytes and @memcpy similar in performance as writing to a pointer?

I’m new to this lower level programming, but seems like doing someT.* = newT must be doing something similar to @memcpy(newTAsBytes, someTAsBytes) under the hood no?

1 Like

Give a fish or teach to fish… your choice, my friend :slight_smile:

How to use Zig with Compiler Explorer: https://ziggit.dev/t/how-to-use-compiler-explorer-with-zig/

Compiler Explorer: godbolt.org

1 Like

Yeah - same, it’s 11:00am here… writing good code is an “after noon/coffee” thing haha… cheers…

2 Likes

Thanks for sharing! I’ll explore :slight_smile:

1 Like