How can I do this (allocation to a buf, thats going to be owned by two separate buf)

I want to allocate a single pointer to be used by two buffers…but whats the type?

//in a struct
ptr_orig: ?[*]u8,
ptr1: ?[*]u8,
ptr2: ?[*]u8,

pub fn init(allocate: std.mem.Allocator) !*obj {
  ptr_orig = try allocate.alloc(Global.MEM_ALLOC * 2);
  ptr1 = ptr_orig[0..Global.MEM_ALLOC];
  ptr2 = (ptr_orig + Global.MEM_ALLOC)[0..Global.MEM_ALLOC];
}

basically, what i’m doing here is allocating 1 big chunk of []u8, and then
distributing a half on ptr1 and ptr2, is this permitted?

what I need is the correct type (a pointer to a []u8) or should i try to allocate
2 buffers instead? (seems like its more easier to deallocate a single one…)

Please insert spaces in between square [ ] brackets to have better formatting.
I don’t understand your question, what are you asking about ?

Better yet, surround any inline code with backticks: []u8

I guess something like this would work:

const std = @import("std");

pub fn main() !void {
    const mem_alloc = 1024;
    var gpa = std.heap.DebugAllocator(.{}){};
    const allocator = gpa.allocator();
    const buf = try allocator.alloc(u8, mem_alloc*2);
    defer allocator.free(buf);
    const ptr1 = buf[0..mem_alloc];
    const ptr2 = buf[mem_alloc..];
    ptr1[0] = 42;
    ptr2[0] = 53;
}

I think I would just allocate two buffers though and release both. Or use an arena if you have a lot of allocations and you want to free all of them at once.

I can’t edit anymore…what do you dont understand, I’m trying to allocate 1 buffer to two buffers, whats the type of the buffer and is it better to allocate 1 buffer or just allocate 2 buffers?

If you allocate one buffer, then you should free one buffer. Note that you also need to ‘remember’ it’s size, it’s different from C where the size is implicitly stored by the allocator interface (malloc and free).
I believe using slices instead of optional pointers to multiple elements would help you achieve this goal.
So I would rewrite it as:

//in a struct
allocated_buffer: []u8,
ptr1: []u8,
ptr2: []u8,

pub fn init(allocate: std.mem.Allocator) !*obj {
  allocated_buffer = try allocate.alloc(Global.MEM_ALLOC * 2);
  ptr1 = ptr_orig[0..Global.MEM_ALLOC];
  ptr2 = ptr_orig[Global.MEM_ALLOC..Global.MEM_ALLOC * 2];
}

If you want to be memory efficient, you could instead write two accessor functions:

//in a struct
allocated_buffer: []u8,

pub fn init(allocate: std.mem.Allocator) !*obj {
  allocated_buffer = try allocate.alloc(Global.MEM_ALLOC * 2);
}

pub fn ptr1(self: *Self) []u8{
    return self.allocated_buffer[0..Global.MEM_ALLOC];
}


pub fn ptr2(self: *Self) []u8{
    return self.allocated_buffer[Global.MEM_ALLOC .. Global.MEM_ALLOC * 2];
}

2 Likes

This one is good! Forgot about the u8 in allocate, but whats the type? Is my type correct?

I use an IDE (VSCode). I can see the types of ptr1 and ptr2 when I mouse hover them. Very handy so I highly recommend using an editor that can do this.

ok, i did not include the deinit…its

pub fn deinit(allocator: std.mem.Allocate, self: *obj) void {
  defer allocator.destroy(self.ptr_orig);
}

but it doesn’t have the size you are referring to…

ok…I’m using Vscode…

This sounds like a simple memory ownership labeling problem. ptr_orig is the memory owner, and ptr1 and ptr2 are references.

Zig doesn’t have a standard ownership labeling scheme. Personally, I wouldn’t put ptr1 and ptr2 in the same structure as ptr_orig with an memory ownership. Instead, I would rewrite ptr1 and ptr2 into a function to express retrieving its slice:

const Obj = struct {
    ptr_orig: []u8,

    pub fn init(allocate: std.mem.Allocator) !Obj {
        return .{ .ptr_orig = try allocate.alloc(Global.MEM_ALLOC * 2) };
    }
    pub fn deinit(self: Obj, allocate: std.mem.Allocator) void {
        allocate.free(self.ptr_orig);
    }
    pub fn ptr1(self: Obj) []u8 {
        return self.ptr_orig[0..Global.MEM_ALLOC];
    }
    pub fn ptr2(self: Obj) []u8 {
        return self.ptr_orig[Global.MEM_ALLOC..];
    }
    pub fn view(self: Obj) View {
        return .{ .ptr_orig = self.ptr_orig, .ptr1 = self.ptr1(), .ptr2 = self.ptr2() };
    }
    pub const View = struct {
        ptr_orig: []u8,
        ptr1: []u8,
        ptr2: []u8,
    }
    ...
}

I will design a View for this owned struct, representing the view of the struct without ownership. Although there is no language guarantee for this semantics, it is merely my own convention.

I am studying this View if this maybe feasible…

This is completely allowable behaviour, though I would recommend that instead of using pointer arithmetic on a many pointer, you allocate a slice and make sub-slices of it instead.
This is how it might look:

ptr: [*]u8,
slice_lower: []u8,
slice_upper: []u8,

/// Allocate a single buffer of len * 2, then split it into an upper and lower buffer.
pub fn init(allocator: std.mem.Allocator, len: usize) !*obj {
    const slice = try allocator.alloc(u8, len * 2);
    const slice_lower = slice[0..len];
    const slice_upper = slice[len..];
    const ptr = slice.ptr; // All slices allow you to access their many pointer
}

When you want to free it, you can simply do this:

/// Release all allocated memory.
pub fn deinit(allocator: std.mem.Allocator) void {
    allocator.free(ptr[0..slice_lower.len * 2]);
}

Since the slices are views of the memory owned by the pointer, freeing the pointer will also free the slices by proxy.

1 Like

Thanks for the edit…and to tholmes, okay