lib/alloc/phase/src/root.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 //! ## Package overview
  2 //!
  3 //! When we design a program that obtains storage before executing recurring
  4 //! work, we must answer three separate concerns: which allocator operations
  5 //! still occur during that work, how bounded input can reuse storage, and what
  6 //! capacity or ownership assumptions are declared and structurally checked. The
  7 //! `alloc_phase` package supplies independent tools for those questions:
  8 //! runtime phase allocators, reusable input regions, and compile-time capacity
  9 //! declarations. We can use each tool on its own. None of them provides a
 10 //! universal proof of zero process allocation or bounded latency across an
 11 //! entire program.
 12 //!
 13 //! Within the repository, `alloc_phase` operates as an internal implementation
 14 //! module. It has no public facade export under `tiny`.
 15 //!
 16 //! ## Runtime phase allocators
 17 //!
 18 //! To detect allocator calls that arrive after startup, we wrap a
 19 //! caller-supplied backing allocator in a runtime phase guard:
 20 //! `SealedPhaseAllocator` or `ObservingPhaseAllocator`. Both wrappers manage
 21 //! state through a separately allocated control block, which can itself fail
 22 //! allocation during initialization. The guard enforces operation permissions
 23 //! at the raw vtable boundary. Initialization permits raw allocation, resizing,
 24 //! remapping, and deallocation. Steady state forbids all four operations.
 25 //! Teardown permits deallocation only. Calling `seal` advances the guard from
 26 //! initialization to steady state, `beginTeardown` transitions from steady
 27 //! state to teardown, and `abortInitialization` transitions directly from a
 28 //! failed initialization to teardown. Callers must free all user allocations
 29 //! before calling `deinit`, which destroys the control block.
 30 //!
 31 //! The two runtime wrappers enforce distinct violation policies.
 32 //! `SealedPhaseAllocator` increments a violation counter and panics before
 33 //! calling the backing allocator, and the call is not forwarded. By contrast,
 34 //! `ObservingPhaseAllocator` increments the counter, samples up to 16 distinct
 35 //! nonzero return addresses into fixed slots, and forwards the operation to the
 36 //! backing allocator, returning its result or failure. Neither wrapper
 37 //! intercepts operations routed through other allocator handles or standard
 38 //! library calls that return before entering the vtable. While internal atomic
 39 //! scalars protect phase tags and violation counters, this bookkeeping does not
 40 //! drain concurrent in-flight operations or make an arbitrary backing allocator
 41 //! thread-safe. Callers must synchronize phase boundaries and cleanup
 42 //! externally. For implementation details and API signatures, see
 43 //! [allocator.zig](allocator.zig).
 44 //!
 45 //! ## Bounded reusable input storage
 46 //!
 47 //! For stream and file ingestion, the `input.OneRegion` container configures a
 48 //! single owned, reusable byte slice. To determine whether an incoming payload
 49 //! fits within a configured limit of `n` bytes without allocating a secondary
 50 //! buffer, the standard capacity derivation helper `deriveCapacity` computes
 51 //! `n + 1` bytes using checked addition. If a source yields `n + 1` bytes, the
 52 //! input exceeds the limit and is rejected. While a custom `Capacity.derive`
 53 //! function can describe a larger storage size, the standard helper sizes the
 54 //! requirement to `n + 1`. Calling `init` allocates this buffer from a
 55 //! caller-supplied allocator, `activate` enters steady state, and `deinit`
 56 //! frees the storage using the initialization allocator provided that no borrow
 57 //! remains active. When an input fits within the limit, acquisition grants an
 58 //! exclusive borrow of the slice, which marks the owner busy even when the
 59 //! admitted payload contains zero bytes. Calling `release` clears the busy
 60 //! state so subsequent operations can reuse the buffer. Lifecycle transitions
 61 //! rely on debug assertions instead of explicit panic enforcement, and this
 62 //! owner exposes neither `beginTeardown` nor `abortInitialization`.
 63 //!
 64 //! An input owner responds to oversized inputs according to its overload
 65 //! policy: a recoverable owner increments a rejection counter and preserves
 66 //! storage for subsequent attempts, while a terminal owner latches a terminal
 67 //! flag and blocks all future reads. Because read operations can consume source
 68 //! bytes or modify buffer contents before reporting an error, recovery
 69 //! indicates permission to reuse the buffer for a future request. Stream
 70 //! framing and transaction management sit with the caller. For files,
 71 //! `readFile` reads into the bounded buffer for whole-file admission: if an
 72 //! input exceeds the limit, the reader rejects it after reading a bounded
 73 //! prefix rather than consuming the entire file. When inputs exceed buffer
 74 //! capacity, `readFileWindow` serves bounded portions of larger files. Consumer
 75 //! tools illustrate these boundaries: Glom coordinates windowed reads and
 76 //! parses newline records outside the owner, Issue manages terminal bodies for
 77 //! commands, and Peek decodes image files into external structures using a
 78 //! recoverable buffer. In all cases, the owner manages only its own buffer,
 79 //! leaving decoder, parser, and stream internals to the caller. For the full
 80 //! interface, see [input/root.zig](input/root.zig).
 81 //!
 82 //! ## Capacity declarations and shape verification
 83 //!
 84 //! The `capacity` module describes owner shapes and validates declarations at
 85 //! compile time. It classifies owner shapes along two axes: storage source
 86 //! (allocator-backed versus caller-provisioned) and overload interface (exact
 87 //! versus rejecting). An exact classification indicates only that the validator
 88 //! does not require an Exhaustion declaration, which does not guarantee that
 89 //! workloads will always fit or that all methods will succeed. A rejecting
 90 //! classification requires Owner.Exhaustion to be a finite nonempty error set,
 91 //! and at least one non-lifecycle pointer-receiver owner method must return
 92 //! that set directly or return an error union containing it.
 93 //!
 94 //! A capacity declaration records covered and excluded storage, an expression
 95 //! graph describing sizing formulas, overload policies, risk classifications,
 96 //! obligation keys for external verification, and typed lifecycle bindings. The
 97 //! module also provides checked arithmetic helpers for computing bounds during
 98 //! allocation. Validating a declaration verifies the grammar and type structure
 99 //! of the expression graph: evaluating the formula, proving allocation closure,
100 //! establishing the truth of bounds, and checking external evidence remain
101 //! separate verification activities. For the specification grammar and
102 //! validation rules, see [capacity/root.zig](capacity/root.zig).
103 
104 const allocator = @import("allocator.zig");
105 
106 /// Compile-time capacity declarations and owner shape validation. Classifies
107 /// storage ownership and overload policies, validates declaration structure and
108 /// expression graph grammar, and registers static claims. See
109 /// [capacity/root.zig](capacity/root.zig).
110 pub const capacity = @import("capacity");
111 /// Bounded reusable input buffer storage (`OneRegion`). Configures a reusable
112 /// byte buffer, enforces exclusive borrow and release discipline, and provides
113 /// recoverable or terminal overload options. See
114 /// [input/root.zig](input/root.zig).
115 pub const input = @import("input/root.zig");
116 /// Audits phase compliance across an application lifecycle by recording
117 /// unauthorized raw operations in local counters and call site slots while
118 /// forwarding those requests to the backing allocator. When a caller attempts
119 /// an operation outside the permitted phase, the guard increments the
120 /// corresponding violation counter, attempts to record the return address in
121 /// one of 16 call site slots, and forwards the invocation to the backing
122 /// allocator. If the backing allocator fails the request, the violation counter
123 /// remains incremented because the attempt itself violated lifecycle rules.
124 ///
125 /// The guard stores its state in a separately allocated control block, allowing
126 /// wrapper copies and issued `std.mem.Allocator` handles to share the same
127 /// lifecycle phase, counters, and backing allocator. This control block
128 /// includes atomic return address slots, giving it a larger memory footprint
129 /// than `SealedPhaseAllocator`. The guard enforces the same lifecycle rules as
130 /// `SealedPhaseAllocator`, so invalid phase transitions, duplicate transitions,
131 /// handle requests in the wrong phase, and premature deinitialization trigger
132 /// an explicit panic in all build modes. This mechanism operates entirely
133 /// through its own allocator handles and functions independently from
134 /// `alloc_observe` event sessions.
135 pub const ObservingPhaseAllocator = allocator.ObservingPhaseAllocator;
136 /// Represents a by-value snapshot of four operation counters: allocations,
137 /// resizes, remaps, and frees. Each field is an unsigned 64-bit integer
138 /// recording the number of times caller code attempted that raw allocator
139 /// operation outside the permitted lifecycle phase. These values count
140 /// attempted operations rather than allocated byte quantities or successful
141 /// allocations. Because the underlying control block maintains these counts in
142 /// native atomic variables that are read individually, a snapshot retrieved
143 /// during concurrent operations does not represent a single coherent point in
144 /// time across all four fields.
145 pub const PhaseViolations = allocator.PhaseViolations;
146 /// Enforces strict lifecycle boundaries around a backing allocator by panicking
147 /// whenever caller code attempts a raw operation outside the permitted phase.
148 /// During initialization, the guard permits all four raw operations:
149 /// allocation, resizing, remapping, and freeing. Once sealed to the steady
150 /// phase, every raw allocator call is considered a violation: the guard
151 /// increments the corresponding violation counter and explicitly panics before
152 /// dispatching to the backing allocator, never returning `error.OutOfMemory` to
153 /// signal phase errors. During teardown, freeing remains permitted, but
154 /// allocation, remapping, and resizing are treated as violations and panic,
155 /// including requests that attempt to shrink existing allocations.
156 ///
157 /// The guard stores its state in a separately allocated control block, allowing
158 /// wrapper copies and issued `std.mem.Allocator` handles to share the same
159 /// lifecycle phase, counters, and backing allocator. Because `Allocator.ptr`
160 /// references this shared control block directly, moving or copying the wrapper
161 /// preserves issued handles. Lifecycle transitions occur through explicit calls
162 /// to `seal`, `abortInitialization`, and `beginTeardown`, and any invalid or
163 /// duplicate transition panics in all build modes. This guard intercepts only
164 /// raw calls routed through its own handles: it does not observe global
165 /// allocator usage, distinct allocator handles, operating system calls, or
166 /// backing allocator internal operations. It enforces no startup allocation
167 /// byte budget and provides no guarantee that runtime workloads fit
168 /// preallocated memory.
169 pub const SealedPhaseAllocator = allocator.SealedPhaseAllocator;
170 /// Represents an owned by-value snapshot of up to 16 distinct nonzero return
171 /// addresses captured during phase violations. The structure holds raw return
172 /// addresses and an active count without resolving symbols, capturing full
173 /// stack traces, or recording per-site occurrence counts. Because concurrent
174 /// threads race to register unrecorded sites into fixed slots, the retained set
175 /// reflects arrival order rather than a complete or ordered execution trace.
176 /// `SealedPhaseAllocator` maintains no site storage and always returns an empty
177 /// snapshot.
178 pub const ViolationSites = allocator.ViolationSites;
179 /// Defines the fixed capacity of 16 distinct nonzero return addresses recorded
180 /// by `ObservingPhaseAllocator`. Duplicate call sites do not consume additional
181 /// slots, and zero addresses are ignored. When new distinct sites arrive after
182 /// all 16 slots are full, the observing guard omits the new addresses while
183 /// continuing to increment its violation counters. `SealedPhaseAllocator` does
184 /// not record return addresses and allocates no site storage.
185 pub const violation_site_capacity = allocator.violation_site_capacity;