tiny.content.memory
Defined in tiny.content.
API (12)
Actions
Public operations.
Owner.activate: Opens the store for use once after init by moving the owner from initialization into the steady phase.Owner.count: Reports how many bindings the store holds, for a test or a diagnostic.Owner.deinit: Drops every binding and resets the store to empty, for a caller that has finished with the store and keeps the buffers for something else.Owner.init: Binds a derived capacity to the three buffers it calls for, once the caller has laid them out.Owner.reader: Returns a Reader to hand to code that may reopen bytes and do nothing else, bound to this owner and carrying the owner's capacity.Owner.sink: Returns a Sink to hand to code that may add bytes and do nothing else, bound to this owner and carrying the owner's capacity.Owner.store: Returns the owner's Reader and Sink as one Store to hand to code trusted with both authorities at once.
Types and contracts
Public types and contracts.
Entry: One binding: a reference and the offset at which its bytes start in the content region.InitErrorOwner: Provides a content store over three buffers the caller laid out, the metadata array, the content region, and the staging region, kept as borrowed slices so the caller keeps the memory.
Values and defaults
Public values and defaults.
Source
Source: lib/content/src/memory/store.zig:10
zig
/// One binding: a reference and the offset at which its bytes start in the/// content region. Entries make up the metadata array the caller sizes. The/// entry size has to equal the metadata entry size a capacity was derived with,/// and a compile-time check refuses the build otherwise.pub const Entry = struct { reference: content.Reference, offset: u64,};Source: lib/content/src/memory/store.zig:21
zig
pub const InitError = error{ CapacityHostWidthExceeded, ContentStorageTooShort, EntryStorageTooShort, StagingStorageTooShort, StorageOverlap,};Source: lib/content/src/memory/store.zig:34
zig
/// Provides a content store over three buffers the caller laid out, the/// metadata array, the content region, and the staging region, kept as borrowed/// slices so the caller keeps the memory. The owner tracks how many bindings it/// holds and how many content bytes it has used, and serializes every provider/// call behind a process-local spin lock.pub const Owner = struct { pub const work_limits: alloc_phase.capacity.WorkLimits = .{ .transition_steps_max = 1, .cleanup_steps_per_call_max = 0, .cleanup_calls_at_capacity_max = 0, }; pub const claim: alloc_phase.capacity.Declaration = .{ .source = .{ .id = "content.memory", .kind = .startup_static, .limit_source = .caller, .storage = .{ .covered = &.{ .{ .id = "identity_entries", .lifetime = .steady, .detail = "one exact reference and byte offset per admitted object", }, .{ .id = "committed_content", .lifetime = .steady, .detail = "caller-selected immutable content byte region", }, .{ .id = "publication_staging", .lifetime = .steady, .detail = "one maximum-size private publication staging region", }, }, .excluded = &.{ "caller input and read-output slices", "provider-independent Reader and Sink values", }, }, .capacity = .{ .inputs = &.{ alloc_phase.capacity.bindInput(content.Limits, "objects", "objects"), alloc_phase.capacity.bindInput(content.Limits, "object_bytes", "object_bytes"), alloc_phase.capacity.bindInput(content.Limits, "total_bytes", "total_bytes"), }, .type_selectors = &.{ alloc_phase.capacity.bindType(Entry, "entry"), }, .nodes = &.{ .{ .input = 0 }, .{ .input = 1 }, .{ .input = 2 }, .{ .scale = .{ .node = 0, .coefficient = .{ .size_of_concrete_type = 0 } } }, .{ .add = .{ .left = 3, .right = 2 } }, .{ .add = .{ .left = 4, .right = 1 } }, }, .assertions = &.{.{ .scope = .closure_total, .measure = .retained, .relation = .exact, .expression = 5, }}, }, .overload = .{ .kind = .reject_before_mutation, .detail = "capacity, digest, extent, and staging checks precede committed metadata mutation", }, .risks = .{ .transitive = .{ .status = .excluded, .detail = "all lookup, hashing, copy, and teardown work is bounded by checked Capacity fields", }, .foreign = .{ .status = .excluded, .detail = "caller-owned memory and a process-local mutex perform no filesystem or foreign-runtime work", }, }, .obligations = &.{ .{ .key = "content_memory_capacity", .role = .capacity_model }, .{ .key = "content_memory_overload", .role = .overload }, .{ .key = "content_memory_convergence", .role = .custom }, .{ .key = "content_memory_teardown", .role = .custom }, }, }, .bindings = .{ .owner = @This(), .seal = .{ .family = alloc_phase.capacity.selector(@This().activate), .premise = .{ .class = .checked_semantic_fact, .authority = .checker, }, }, .teardown = .{ .family = alloc_phase.capacity.selector(@This().deinit), .premise = .{ .class = .checked_semantic_fact, .authority = .checker, }, }, }, }; phase: alloc_phase.capacity.Phase, capacity: content.Capacity, entries: []Entry, content: []u8, staging: []u8, entry_count: usize = 0, used: usize = 0, mutex: std.atomic.Mutex = .unlocked, const reader_vtable: content.Reader.VTable = .{ .read = readProvider }; const sink_vtable: content.Sink.VTable = .{ .put = putProvider }; /// Binds a derived capacity to the three buffers it calls for, once the /// caller has laid them out. The call returns `CapacityHostWidthExceeded` /// when one of the capacity's counts is wider than a host `usize`, returns /// `EntryStorageTooShort`, `ContentStorageTooShort`, or /// `StagingStorageTooShort` when a buffer is smaller than the capacity /// calls for, and returns `StorageOverlap` when any two of the three /// buffers share bytes, because staging and committed content have to stay /// apart. Successful initialization cuts each buffer to the size the /// capacity names, keeps that slice, and leaves the owner in the /// initialization phase, where capabilities are still refused. pub fn init( capacity: content.Capacity, entries: []Entry, content_storage: []u8, staging: []u8, ) InitError!Owner { const object_count = std.math.cast(usize, capacity.limits.objects) orelse return error.CapacityHostWidthExceeded; const total_bytes = std.math.cast(usize, capacity.limits.total_bytes) orelse return error.CapacityHostWidthExceeded; const object_bytes = std.math.cast(usize, capacity.limits.object_bytes) orelse return error.CapacityHostWidthExceeded; if (entries.len < object_count) return error.EntryStorageTooShort; if (content_storage.len < total_bytes) return error.ContentStorageTooShort; if (staging.len < object_bytes) return error.StagingStorageTooShort; const selected_entries = entries[0..object_count]; const selected_content = content_storage[0..total_bytes]; const selected_staging = staging[0..object_bytes]; const entry_bytes = std.mem.sliceAsBytes(selected_entries); if (slicesOverlap(entry_bytes, selected_content) or slicesOverlap(entry_bytes, selected_staging) or slicesOverlap(selected_content, selected_staging)) { return error.StorageOverlap; } return .{ .phase = .initialization, .capacity = capacity, .entries = selected_entries, .content = selected_content, .staging = selected_staging, }; } /// Opens the store for use once after init by moving the owner from /// initialization into the steady phase. Every capability accessor asserts /// the steady phase, so no Reader or Sink escapes before this call. pub fn activate(self: *Owner) void { std.debug.assert(self.phase == .initialization); std.debug.assert(self.entry_count == 0); std.debug.assert(self.used == 0); self.phase = .steady; } /// Returns a Reader to hand to code that may reopen bytes and do nothing /// else, bound to this owner and carrying the owner's capacity. The /// returned value borrows the owner, so the owner outlives it. pub fn reader(self: *Owner) content.Reader { std.debug.assert(self.phase == .steady); return .{ .context = self, .capacity = self.capacity, .vtable = &reader_vtable, }; } /// Returns a Sink to hand to code that may add bytes and do nothing else, /// bound to this owner and carrying the owner's capacity. The returned /// value borrows the owner, so the owner outlives it. pub fn sink(self: *Owner) content.Sink { std.debug.assert(self.phase == .steady); return .{ .context = self, .capacity = self.capacity, .vtable = &sink_vtable, }; } /// Returns the owner's Reader and Sink as one Store to hand to code trusted /// with both authorities at once. Both halves carry the same capacity, so /// the composition always succeeds. pub fn store(self: *Owner) content.Store { return content.Store.init(self.reader(), self.sink()) catch unreachable; } /// Drops every binding and resets the store to empty, for a caller that has /// finished with the store and keeps the buffers for something else. This /// overwrites the live metadata entries and returns the binding count and /// the used byte count to zero. The call leaves all three buffers with /// their caller, who decides what happens to the bytes, and moves the owner /// into the teardown phase, after which the capability accessors refuse. pub fn deinit(self: *Owner) void { self.lock(); defer self.mutex.unlock(); std.debug.assert(self.phase == .steady); for (self.entries[0..self.entry_count]) |*entry| entry.* = undefined; self.entry_count = 0; self.used = 0; self.phase = .teardown; } /// Reports how many bindings the store holds, for a test or a diagnostic. /// The call takes the same lock the provider calls take, so the answer is a /// consistent one. pub fn count(self: *Owner) usize { self.lock(); defer self.mutex.unlock(); std.debug.assert(self.phase == .steady); return self.entry_count; } fn readProvider( context: *anyopaque, identifier: content.Identifier, output: []u8, ) error{ ContentMissing, ReadFailed }!u64 { const self: *Owner = @ptrCast(@alignCast(context)); self.lock(); defer self.mutex.unlock(); std.debug.assert(self.phase == .steady); const index = self.find(identifier) orelse return error.ContentMissing; const entry = self.entries[index]; const actual = entry.reference.extent.bytes; if (actual > output.len) return actual; const start = std.math.cast(usize, entry.offset) orelse return error.ReadFailed; const end = std.math.add(usize, start, actual) catch return error.ReadFailed; if (end > self.content.len or end > self.used) return error.ReadFailed; @memcpy(output[0..actual], self.content[start..end]); return actual; } fn putProvider( context: *anyopaque, reference: content.Reference, bytes: []const u8, ) error{ ContentMutated, PublicationConflict, PublicationFailed, StoreCapacityExceeded, }!content.Publication { const self: *Owner = @ptrCast(@alignCast(context)); self.lock(); defer self.mutex.unlock(); std.debug.assert(self.phase == .steady); if (!reference.identifier.matches(bytes)) return error.ContentMutated; if (self.find(reference.identifier)) |index| { const entry = self.entries[index]; if (entry.reference.extent.bytes != reference.extent.bytes) { return error.PublicationConflict; } const start = std.math.cast(usize, entry.offset) orelse return error.PublicationConflict; const end = std.math.add(usize, start, bytes.len) catch return error.PublicationConflict; if (end > self.used or end > self.content.len or !std.mem.eql(u8, self.content[start..end], bytes)) { return error.PublicationConflict; } if (!reference.identifier.matches(bytes)) return error.ContentMutated; return .exists_same; } if (self.entry_count >= self.entries.len) { return error.StoreCapacityExceeded; } const next_used = std.math.add(usize, self.used, bytes.len) catch return error.StoreCapacityExceeded; if (next_used > self.content.len or bytes.len > self.staging.len) { return error.StoreCapacityExceeded; } @memcpy(self.staging[0..bytes.len], bytes); const staged = self.staging[0..bytes.len]; if (!reference.identifier.matches(staged) or !reference.identifier.matches(bytes)) { return error.ContentMutated; } @memcpy(self.content[self.used..next_used], staged); self.entries[self.entry_count] = .{ .reference = reference, .offset = self.used, }; self.entry_count += 1; self.used = next_used; return .created; } fn find(self: *const Owner, identifier: content.Identifier) ?usize { var index: usize = 0; while (index < self.entry_count) : (index += 1) { if (self.entries[index].reference.identifier.eql(identifier)) { return index; } } return null; } fn lock(self: *Owner) void { while (!self.mutex.tryLock()) std.atomic.spinLoopHint(); }};Source: lib/content/src/memory/root.zig
zig
const store = @import("store.zig");pub const Entry = store.Entry;pub const InitError = store.InitError;pub const Owner = store.Owner;Source: lib/content/src/root.zig:44
zig
pub const memory = @import("memory/root.zig");Audit
| Definitions | 13 |
|---|---|
| Public names | 13 |
| Members | 15 |
| Version | 26.7.0 |
| Revision | daab053ee433 |