Skip to documentation
SLOP

tiny.preserves.ownership

Reference tiny.preserves ownership

Defined in tiny.preserves.

Calls that copy a whole value tree and free one, each by the rule that matches how the tree was built.

API (5)

Actions

Public operations.

Types and contracts

Public types and contracts.

No direct callersNo direct callstiny.preservesownership
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallsNo direct callstiny.preservescloneValueDeeptest sourcelib.preserves.src.ownershiptest: cloneValueDeep owns copied atom...test sourcelib.preserves.src.ownershiptest: cloneValueDeep owns copied patt...ownershipcloneValueDeep
Static calls · unresolved targets: 11 · external targets: 12.
Called byCallsNo direct callstiny.preservesfreeValuetest sourcelib.preserves.src.ownershiptest: freeValue frees outer slice onlyownershipfreeValue
Static calls · unresolved targets: 0 · external targets: 4.
Called byCallsNo direct callstiny.preservesfreeValueDeeptest sourcelib.preserves.src.ownershiptest: cloneValueDeep owns copied patt...test sourcelib.preserves.src.ownershiptest: freeValueDeep frees compound tr...patterns_modConversionstest sourcelib.preserves.src.patternstest: preservesToPattern lowers recur...private sourcelib.preserves.src.text.formatwriteValueownershipfreeValueDeep
Static calls · unresolved targets: 0 · external targets: 5.

Source: lib/preserves/src/ownership.zig

zig
//! Calls that copy a whole value tree and free one, each by the rule that matches how the tree was//! built. A tree can own every byte, own its structure while borrowing its text, or own only its//! outermost storage, and each case needs its own free.//!//! Nothing in a value records which of its bytes it owns, so the caller has to pick the free that//! matches how the tree was built. A Lean model of parser results proves that when a borrowed//! symbol and an owned symbol look the same, no single cleanup frees exactly the owned bytes of//! both. The same model proves that cleanup is exact when every resource in a result is owned.//!//! The package keeps the values of the [Preserves](https://preserves.dev/) data language and//! changes one thing for this file: the caller owns and frees all storage.//!//! `cloneValueDeep` copies a tree into storage that owns every byte, so the copy takes the one full//! free, `Value.deinit`, whatever the original borrowed. `freeValueDeep` frees the structure of a//! tree and leaves its text, for trees that own their structure and borrow their strings and//! symbols. `freeValue` frees one level of storage, the level the root constructors allocate.//!//! - *domain*: the type of the embedded values//! - *pattern form*: one of four value kinds that describe a matchconst std = @import("std");const Allocator = std.mem.Allocator;const value_mod = @import("value.zig");const domain_mod = @import("domain.zig");pub const Value = value_mod.Value;pub const NoEmbedded = domain_mod.NoEmbedded;/// Copies `val` all the way down into storage from `alloc`: atom bytes, integer digits, bind names,/// compound storage and embedded values. Code that keeps a value past the life of its source calls/// it for a copy that owns every byte, as the text reader and the property tests do. A set or/// dictionary copy keeps the original's storage order. Each embedded value is copied by its type's/// `clone`. For `AnyEmbedded` that calls the payload's copy function, shares a payload that has/// neither a copy nor a free function, and panics on one that has a free function and no copy/// function. `Value.deinit` with `alloc` frees the result. On `error.OutOfMemory` the call frees/// every partial copy and leaves `val` unchanged.pub fn cloneValueDeep(comptime D: type, alloc: Allocator, val: Value(D)) Allocator.Error!Value(D) {    domain_mod.assertIsDomain(D);    const V = Value(D);    return switch (val) {        .boolean => |v| V.initBoolean(v),        .double => |v| V.initDouble(v),        .signed_integer => |si| V.initSignedInteger(try si.clone(alloc)),        .string => |s| try V.initString(alloc, s),        .byte_string => |s| try V.initByteString(alloc, s),        .symbol => |s| try V.initSymbol(alloc, s),        .record => |record| blk: {            var label = try cloneValueDeep(D, alloc, record.label.*);            errdefer label.deinit(alloc);            const fields = try alloc.alloc(V, record.fields.len);            var filled: usize = 0;            errdefer {                while (filled > 0) {                    filled -= 1;                    fields[filled].deinit(alloc);                }                alloc.free(fields);            }            while (filled < record.fields.len) : (filled += 1) {                fields[filled] = try cloneValueDeep(D, alloc, record.fields[filled]);            }            break :blk try V.initRecord(alloc, label, fields);        },        .sequence => |items| blk: {            const cloned = try alloc.alloc(V, items.len);            var filled: usize = 0;            errdefer {                while (filled > 0) {                    filled -= 1;                    cloned[filled].deinit(alloc);                }                alloc.free(cloned);            }            while (filled < items.len) : (filled += 1) {                cloned[filled] = try cloneValueDeep(D, alloc, items[filled]);            }            break :blk V.initSequence(cloned);        },        .set => |items| blk: {            const cloned = try alloc.alloc(V, items.len);            var filled: usize = 0;            errdefer {                while (filled > 0) {                    filled -= 1;                    cloned[filled].deinit(alloc);                }                alloc.free(cloned);            }            while (filled < items.len) : (filled += 1) {                cloned[filled] = try cloneValueDeep(D, alloc, items[filled]);            }            break :blk V.initSet(cloned);        },        .dictionary => |entries| blk: {            const cloned = try alloc.alloc(V.DictionaryEntry, entries.len);            var filled: usize = 0;            errdefer {                while (filled > 0) {                    filled -= 1;                    cloned[filled].key.deinit(alloc);                    cloned[filled].value.deinit(alloc);                }                alloc.free(cloned);            }            while (filled < entries.len) : (filled += 1) {                var key = try cloneValueDeep(D, alloc, entries[filled].key);                errdefer key.deinit(alloc);                cloned[filled].value = try cloneValueDeep(D, alloc, entries[filled].value);                cloned[filled].key = key;            }            break :blk V.initDictionary(cloned);        },        .embedded => |embedded| V.initEmbedded(try embedded.clone(alloc)),        .discard => .{ .discard = {} },        .capture => |inner| blk: {            const ptr = try alloc.create(V);            errdefer alloc.destroy(ptr);            ptr.* = try cloneValueDeep(D, alloc, inner.*);            break :blk .{ .capture = ptr };        },        .bind => |binding| blk: {            const ptr = try alloc.create(V);            errdefer alloc.destroy(ptr);            ptr.* = try cloneValueDeep(D, alloc, binding.pattern.*);            errdefer ptr.deinit(alloc);            break :blk .{                .bind = .{                    .name = try alloc.dupe(u8, binding.name),                    .pattern = ptr,                },            };        },        .rest_pattern => |rest| blk: {            const prefix = try alloc.alloc(V, rest.prefix.len);            var filled: usize = 0;            errdefer {                while (filled > 0) {                    filled -= 1;                    prefix[filled].deinit(alloc);                }                alloc.free(prefix);            }            while (filled < rest.prefix.len) : (filled += 1) {                prefix[filled] = try cloneValueDeep(D, alloc, rest.prefix[filled]);            }            const rest_ptr = try alloc.create(V);            errdefer alloc.destroy(rest_ptr);            rest_ptr.* = try cloneValueDeep(D, alloc, rest.rest.*);            break :blk .{ .rest_pattern = .{ .prefix = prefix, .rest = rest_ptr } };        },    };}/// Frees the outer storage of one compound value and nothing below it. Code that built a value with/// the root constructors calls it at the end, as the README's example does. The outer storage is a/// record's label cell and field slice, the slice of a sequence, set or dictionary, the inner cell/// of a capture or bind, and a rest pattern's prefix slice and rest cell. The call matches the root/// constructors, which allocate that outer storage and borrow everything below it. The call frees/// no child value, no atom bytes and no embedded payload, and it does nothing to an atom or an/// embedded value.pub fn freeValue(comptime D: type, alloc: Allocator, val: Value(D)) void {    domain_mod.assertIsDomain(D);    switch (val) {        .record => |r| {            alloc.destroy(r.label);            alloc.free(r.fields);        },        .sequence => |s| alloc.free(s),        .set => |s| alloc.free(s),        .dictionary => |d| alloc.free(d),        .capture => |inner| alloc.destroy(inner),        .bind => |b| alloc.destroy(b.pattern),        .rest_pattern => |rp| {            alloc.free(rp.prefix);            alloc.destroy(rp.rest);        },        else => {},    }}/// Frees a value's compound storage all the way down: records, sequences, sets, dictionaries,/// captures, binds and rest patterns. The pattern conversions and `toText` call it for trees whose/// structure they own and whose text they borrow. The call also frees the digits of every integer/// too large for 128 bits. The call leaves the bytes of strings, byte strings and symbols, the/// names of binds, and embedded payloads. `Value.deinit` frees all of those as well, so it fits/// only a tree whose every byte comes from `alloc`.pub fn freeValueDeep(comptime D: type, alloc: Allocator, val: Value(D)) void {    domain_mod.assertIsDomain(D);    switch (val) {        .signed_integer => |si| {            var copy = si;            copy.deinit(alloc);        },        .record => |r| {            freeValueDeep(D, alloc, r.label.*);            for (r.fields) |f| freeValueDeep(D, alloc, f);            alloc.destroy(r.label);            alloc.free(r.fields);        },        .sequence => |s| {            for (s) |item| freeValueDeep(D, alloc, item);            alloc.free(s);        },        .set => |s| {            for (s) |item| freeValueDeep(D, alloc, item);            alloc.free(s);        },        .dictionary => |d| {            for (d) |entry| {                freeValueDeep(D, alloc, entry.key);                freeValueDeep(D, alloc, entry.value);            }            alloc.free(d);        },        .capture => |inner| {            freeValueDeep(D, alloc, inner.*);            alloc.destroy(inner);        },        .bind => |b| {            freeValueDeep(D, alloc, b.pattern.*);            alloc.destroy(b.pattern);        },        .rest_pattern => |rp| {            for (rp.prefix) |item| freeValueDeep(D, alloc, item);            alloc.free(rp.prefix);            freeValueDeep(D, alloc, rp.rest.*);            alloc.destroy(rp.rest);        },        else => {},    }}test "freeValue frees outer slice only" {    const V = Value(NoEmbedded);    const allocator = std.testing.allocator;    const fields = try allocator.alloc(V, 2);    fields[0] = V.initI128(1);    fields[1] = V.initI128(2);    const label_ptr = try allocator.create(V);    label_ptr.* = V.initBoolean(true);    const r: V = .{ .record = .{ .label = label_ptr, .fields = fields } };    freeValue(NoEmbedded, allocator, r);}test "cloneValueDeep owns copied atom and compound storage" {    const V = Value(NoEmbedded);    const allocator = std.testing.allocator;    const dict_entries = try allocator.alloc(V.DictionaryEntry, 1);    errdefer allocator.free(dict_entries);    dict_entries[0] = .{ .key = V{ .symbol = "key" }, .value = V{ .byte_string = "bytes" } };    const fields = try allocator.alloc(V, 2);    errdefer allocator.free(fields);    fields[0] = V{ .string = "name" };    fields[1] = V.initDictionary(dict_entries);    const original = try V.initRecord(allocator, V{ .symbol = "shape" }, fields);    defer {        allocator.destroy(original.record.label);        allocator.free(original.record.fields);        allocator.free(dict_entries);    }    var cloned = try cloneValueDeep(NoEmbedded, allocator, original);    defer cloned.deinit(allocator);    try std.testing.expect(original.eql(cloned));}test "cloneValueDeep owns copied pattern form storage" {    const V = Value(NoEmbedded);    const allocator = std.testing.allocator;    const name = try allocator.dupe(u8, "slot");    defer allocator.free(name);    const capture_ptr = try allocator.create(V);    capture_ptr.* = .{ .discard = {} };    const bind_ptr = try allocator.create(V);    bind_ptr.* = .{ .capture = capture_ptr };    const prefix = try allocator.alloc(V, 2);    prefix[0] = V.initI128(1);    prefix[1] = .{ .bind = .{ .name = name, .pattern = bind_ptr } };    const rest_ptr = try allocator.create(V);    rest_ptr.* = V{ .symbol = "tail" };    const original: V = .{ .rest_pattern = .{ .prefix = prefix, .rest = rest_ptr } };    var cloned = try cloneValueDeep(NoEmbedded, allocator, original);    defer cloned.deinit(allocator);    try std.testing.expect(original.eql(cloned));    freeValueDeep(NoEmbedded, allocator, original);    try std.testing.expectEqualStrings("slot", cloned.rest_pattern.prefix[1].bind.name);}test "freeValueDeep frees compound tree but skips string/symbol slices" {    const V = Value(NoEmbedded);    const allocator = std.testing.allocator;    const inner_items = try allocator.alloc(V, 2);    inner_items[0] = V{ .string = "borrowed-a" };    inner_items[1] = V{ .symbol = "borrowed-b" };    const inner_seq = V{ .sequence = inner_items };    const outer_items = try allocator.alloc(V, 1);    outer_items[0] = inner_seq;    const label_ptr = try allocator.create(V);    label_ptr.* = V{ .symbol = "Label" };    const r: V = .{ .record = .{ .label = label_ptr, .fields = outer_items } };    freeValueDeep(NoEmbedded, allocator, r);}

Source: lib/preserves/src/root.zig:114

zig
pub const ownership = @import("ownership.zig");

Audit

Definitions4
Public names4
Members0
Version26.7.0
Revisiondaab053ee433