lib/alloc/observe/src/buffer.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

 1 const std = @import("std");
 2 const adapter = @import("allocator.zig");
 3 const protocol = @import("protocol.zig");
 4 
 5 /// A buffer-first allocator type that manages allocations from an initial
 6 /// caller-provided buffer before falling back to a secondary allocator. In
 7 /// disabled builds, this type aliases `std.heap.BufferFirstAllocator`. In
 8 /// enabled builds, it wraps that allocator and exposes only `init` and
 9 /// `allocator`, forwarding allocation calls through an internal
10 /// `ObservedAllocator` tagged with the `buffer_first` producer kind. The
11 /// wrapper acts solely as an allocator adapter and adds no lifecycle records.
12 pub const First = if (protocol.enabled)
13     ObservedFirst
14 else
15     std.heap.BufferFirstAllocator;
16 
17 const ObservedFirst = struct {
18     inner: std.heap.BufferFirstAllocator,
19     observed: adapter.ObservedAllocator = undefined,
20     producer_id: u64,
21 
22     const Self = @This();
23 
24     /// Initializes the buffer-first allocator by passing the caller-supplied
25     /// initial buffer slice and fallback allocator to the underlying
26     /// implementation, assigning a unique producer identifier when observation
27     /// is enabled. The allocator borrows the buffer without copying or taking
28     /// ownership of it. Callers must ensure that both the initial memory buffer
29     /// and the fallback allocator remain valid and stable for the entire
30     /// operational lifetime of the instance.
31     pub fn init(buffer: []u8, fallback: std.mem.Allocator) Self {
32         return .{
33             .inner = std.heap.BufferFirstAllocator.init(buffer, fallback),
34             .producer_id = protocol.producerId(),
35         };
36     }
37 
38     /// Returns a borrowed allocator handle backed by the stable instance. In
39     /// enabled builds, this call initializes an embedded adapter pointing to
40     /// the underlying buffer-first allocator. The method offers no
41     /// thread-safety guarantees for concurrent calls on the same instance, so
42     /// callers must serialize access or complete handle creation before
43     /// sharing.
44     pub fn allocator(self: *Self) std.mem.Allocator {
45         self.observed = adapter.ObservedAllocator.init(
46             self.inner.allocator(),
47             self.producer_id,
48             .buffer_first,
49         );
50         return self.observed.allocator();
51     }
52 };