Skip to documentation
SLOP

tiny.preserves.constructors_mod

Reference tiny.preserves constructors_mod

Defined in tiny.preserves.

Each function here builds one value from Zig data in a single call.

API (13)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

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

Source

Called byCallsNo direct callstest sourcelib.preserves.src.constructorstest: top-level constructors round-tr...constructors_modboolean
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callstest sourcelib.preserves.src.constructorstest: top-level constructors round-tr...constructors_moddiscard
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callstest sourcelib.preserves.src.constructorstest: top-level constructors round-tr...constructors_modfloat
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callstest sourcelib.preserves.src.constructorstest: integer round-trips via toI64Lo...test sourcelib.preserves.src.constructorstest: top-level constructors round-tr...constructors_modinteger
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callstest sourcelib.preserves.src.constructorstest: top-level constructors round-tr...constructors_modnull val
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callstest sourcelib.preserves.src.constructorstest: top-level constructors round-tr...constructors_modstring
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callstest sourcelib.preserves.src.constructorstest: record/sequence/set/dictionary ...test sourcelib.preserves.src.constructorstest: top-level constructors round-tr...constructors_modsymbol
Static calls · unresolved targets: 0 · external targets: 1.

Source: lib/preserves/src/constructors.zig

zig
//! Each function here builds one value from Zig data in a single call. A caller needs values that//! compare, hash and encode the same way whatever order the caller listed a set's elements or a//! dictionary's entries in. A caller also needs to know which memory the new value points into, so//! it can free exactly what the call allocated.//!//! A set with a repeated element, or a dictionary with a repeated key, has no single meaning, so//! building one has to fail. Copying every string would cost an allocation per atom, and an atom//! built from a string literal can point into bytes that outlive the value.//!//! The string and symbol constructors borrow the caller's bytes, and the compound constructors copy//! only the slice of items they are given. `set` and `dictionary` reject a repeated element or key,//! then store a sorted copy, so the stored order is the package's value order (`compare`).//! `freeValue` frees the one slice or cell that a compound or pattern constructor allocated, and//! leaves the items and atoms it points to alone. `Constructors` takes the type of the embedded//! values as a parameter, and `constructors` and `any_constructors` are its two common instances.const std = @import("std");const Allocator = std.mem.Allocator;const value_mod = @import("value.zig");const domain_mod = @import("domain.zig");const embedded_mod = @import("embedded.zig");const symbols_mod = @import("symbols.zig");pub const Value = value_mod.Value;pub const NoEmbedded = domain_mod.NoEmbedded;pub const AnyEmbedded = embedded_mod.AnyEmbedded;/// Returns a namespace of constructors for `Value(D)`, the Preserves value whose embedded values/// hold a `D`. A caller whose values hold embedded values of one type calls it once at compile/// time, for every constructor over those values. The JSON decoder, the protocol record builders/// and the pattern conversions each call it to build their records. `D` has to provide `eql`,/// `order`, `deinit` and `clone`, or the call is a compile error.pub fn Constructors(comptime D: type) type {    domain_mod.assertIsDomain(D);    const V = Value(D);    return struct {        /// Returns the boolean value `v`. The call allocates nothing.        pub fn boolean(v: bool) V {            return V.initBoolean(v);        }        /// Returns an integer value holding `v`. The call widens `v` to 128 bits, so the value        /// compares and encodes like any other integer. The call allocates nothing.        pub fn integer(v: i64) V {            return V.initI128(@as(i128, v));        }        /// Returns the double value `v`. The call allocates nothing.        pub fn float(v: f64) V {            return V.initDouble(v);        }        /// Returns a string value that borrows the bytes `s`. The value points into `s`, so `s` has        /// to stay alive while the value is in use. The call checks nothing about `s`, including        /// whether it is valid UTF-8.        pub fn string(s: []const u8) V {            return .{ .string = s };        }        /// Returns a symbol value that borrows the bytes `name`. The value points into `name`, so        /// `name` has to stay alive while the value is in use.        pub fn symbol(name: []const u8) V {            return .{ .symbol = name };        }        /// Returns the discard pattern, which matches any value. The call allocates nothing.        pub fn discard() V {            return .{ .discard = {} };        }        /// Returns the symbol `null`, the value the package uses for JSON's null. The symbol's        /// bytes are a constant of the package, so the value is safe to keep. `isNull` recognizes        /// the value.        pub fn null_val() V {            return .{ .symbol = symbols_mod.SYM_NULL.name };        }        /// Returns a capture pattern, which matches what `inner` matches and records the matched        /// value. The call allocates one cell for `inner` with `alloc` and moves `inner` into it.        /// The only error is running out of memory.        pub fn capture(alloc: Allocator, inner: V) !V {            const p = try alloc.create(V);            p.* = inner;            return .{ .capture = p };        }        /// Returns a pattern that matches what `inner` matches and records the matched value under        /// `name`. The call allocates one cell for `inner` with `alloc` and borrows `name`. On        /// failure the call frees its cell and leaves `inner` with the caller.        pub fn bindVal(alloc: Allocator, name: []const u8, inner: V) !V {            const p = try alloc.create(V);            errdefer alloc.destroy(p);            p.* = inner;            return .{ .bind = .{ .name = name, .pattern = p } };        }        /// Returns a pattern that matches a sequence whose first items match `prefix` and whose        /// remaining items match `rest`. The call copies the `prefix` slice and allocates one cell        /// for `rest`, both with `alloc`. The copy is shallow: the items it holds still point to        /// whatever the caller's items pointed to. On failure the call frees what it allocated and        /// leaves `prefix` and `rest` with the caller.        pub fn restPattern(alloc: Allocator, prefix: []const V, rest: V) !V {            const prefix_copy = try alloc.dupe(V, prefix);            errdefer alloc.free(prefix_copy);            const rest_ptr = try alloc.create(V);            errdefer alloc.destroy(rest_ptr);            rest_ptr.* = rest;            return .{ .rest_pattern = .{ .prefix = prefix_copy, .rest = rest_ptr } };        }        /// Returns an embedded value that holds the pointer `ptr`. The call is a compile error        /// unless `D` is `AnyEmbedded`. The value carries no equality, cleanup or copy functions,        /// so two such values are equal only when they hold the same pointer. Freeing the value        /// leaves the pointed-to data alone.        pub fn embedded(ptr: *anyopaque) V {            if (D != AnyEmbedded) {                @compileError("Constructors(" ++ @typeName(D) ++ ").embedded(ptr) requires D == AnyEmbedded");            }            return V{ .embedded = AnyEmbedded{ .value = ptr } };        }        /// Returns a record with label `label` and fields `fields`. The call allocates one cell for        /// the label and a copy of the `fields` slice, both with `alloc`. The copy is shallow: the        /// new record holds the same field values the caller passed. `freeValue` frees exactly the        /// cell and the slice this call allocated. On failure the call frees its cell and leaves        /// `label` and `fields` with the caller.        pub fn record(alloc: Allocator, label: V, fields: []const V) !V {            const lp = try alloc.create(V);            errdefer alloc.destroy(lp);            lp.* = label;            const fs = try alloc.dupe(V, fields);            return .{ .record = .{ .label = lp, .fields = fs } };        }        /// Returns a sequence holding a copy of the `items` slice, allocated with `alloc`. The copy        /// is shallow: the new sequence holds the same item values the caller passed. `freeValue`        /// frees exactly the slice this call allocated.        pub fn sequence(alloc: Allocator, items: []const V) !V {            const s = try alloc.dupe(V, items);            return .{ .sequence = s };        }        /// Returns a set holding a sorted copy of `items`, allocated with `alloc`. The copy is        /// sorted by `compare`, the package's total order on values. The copy is shallow, and        /// `freeValue` frees exactly the slice this call allocated. The duplicate check compares        /// every pair of items, so its cost grows with the square of the count. The call returns        /// `error.DuplicateSetElement` when two items are equal. That error allocates nothing. The        /// duplicate check leaves `items` with the caller.        pub fn set(alloc: Allocator, items: []const V) !V {            if (!V.setElementsDistinct(items)) return error.DuplicateSetElement;            const sorted = try alloc.dupe(V, items);            const Cmp = struct {                fn lt(_: void, a: V, b: V) bool {                    return a.compare(b) == .lt;                }            };            std.mem.sort(V, sorted, {}, Cmp.lt);            return .{ .set = sorted };        }        /// Returns a dictionary holding a copy of `entries` sorted by key, allocated with `alloc`.        /// The keys are sorted by `compare`, the package's total order on values. The copy is        /// shallow, and `freeValue` frees exactly the slice this call allocated. The duplicate        /// check compares every pair of keys, so its cost grows with the square of the count. The        /// call returns `error.DuplicateDictionaryKey` when two keys are equal. That error        /// allocates nothing. The duplicate check leaves `entries` with the caller.        pub fn dictionary(alloc: Allocator, entries: []const V.DictionaryEntry) !V {            if (!V.dictionaryKeysDistinct(entries)) return error.DuplicateDictionaryKey;            const sorted = try alloc.dupe(V.DictionaryEntry, entries);            const Cmp = struct {                fn lt(_: void, a: V.DictionaryEntry, b: V.DictionaryEntry) bool {                    return a.key.compare(b.key) == .lt;                }            };            std.mem.sort(V.DictionaryEntry, sorted, {}, Cmp.lt);            return .{ .dictionary = sorted };        }    };}/// The constructors for `Value(NoEmbedded)`. A caller whose values are `Value(NoEmbedded)` calls/// these constructors, for a namespace fixed to that type. The protocol record builders and the/// pattern conversions for such values are built on it.pub const constructors = Constructors(NoEmbedded);/// The constructors for values whose embedded values hold any pointer (`AnyEmbedded`). The package/// root re-exports each of its functions under the same name, so a caller that writes/// `preserves.record` calls this instance. The JSON decoder builds its records and patterns with/// it.pub const any_constructors = Constructors(AnyEmbedded);/// Returns the boolean value `v` as a `Value(NoEmbedded)`, the same as `constructors.boolean`.pub fn boolean(v: bool) Value(NoEmbedded) {    return constructors.boolean(v);}/// Returns an integer value holding `v` as a `Value(NoEmbedded)`, the same as/// `constructors.integer`.pub fn integer(v: i64) Value(NoEmbedded) {    return constructors.integer(v);}/// Returns the double value `v` as a `Value(NoEmbedded)`, the same as `constructors.float`.pub fn float(v: f64) Value(NoEmbedded) {    return constructors.float(v);}/// Returns a `Value(NoEmbedded)` string that borrows `s`, the same as `constructors.string`. `s`/// has to stay alive while the value is in use.pub fn string(s: []const u8) Value(NoEmbedded) {    return constructors.string(s);}/// Returns a `Value(NoEmbedded)` symbol that borrows `name`, the same as `constructors.symbol`./// `name` has to stay alive while the value is in use.pub fn symbol(name: []const u8) Value(NoEmbedded) {    return constructors.symbol(name);}/// Returns the discard pattern as a `Value(NoEmbedded)`, the same as `constructors.discard`.pub fn discard() Value(NoEmbedded) {    return constructors.discard();}/// Returns the symbol `null` as a `Value(NoEmbedded)`, the same as `constructors.null_val`.pub fn null_val() Value(NoEmbedded) {    return constructors.null_val();}test "top-level constructors round-trip to classes" {    const integer_mod = @import("integer.zig");    const V = Value(NoEmbedded);    const b = boolean(true);    try std.testing.expectEqual(value_mod.AtomClass.boolean, b.atomClass().?);    const i = integer(42);    try std.testing.expectEqual(value_mod.AtomClass.signed_integer, i.atomClass().?);    try std.testing.expectEqual(@as(i64, 42), integer_mod.SignedInteger.toI64Lossy(i.signed_integer));    const f = float(3.14);    try std.testing.expectEqual(value_mod.AtomClass.double, f.atomClass().?);    const s = string("hi");    try std.testing.expectEqual(value_mod.AtomClass.string, s.atomClass().?);    const sy = symbol("tag");    try std.testing.expectEqual(value_mod.AtomClass.symbol, sy.atomClass().?);    const d = discard();    try std.testing.expectEqual(value_mod.PatternFormClass.discard, d.patternClass().?);    const n = null_val();    try std.testing.expectEqual(value_mod.AtomClass.symbol, n.atomClass().?);    try std.testing.expect(std.mem.eql(u8, n.symbol, "null"));    _ = V;}test "capture/bindVal/restPattern allocate and build pattern forms" {    const V = Value(NoEmbedded);    const allocator = std.testing.allocator;    var arena = std.heap.ArenaAllocator.init(allocator);    defer arena.deinit();    const a = arena.allocator();    const cap = try constructors.capture(a, V.initI128(7));    try std.testing.expectEqual(value_mod.PatternFormClass.capture, cap.patternClass().?);    try std.testing.expectEqual(@as(i128, 7), try cap.capture.*.signed_integer.toI128());    const bound = try constructors.bindVal(a, "x", V.initBoolean(true));    try std.testing.expectEqual(value_mod.PatternFormClass.bind, bound.patternClass().?);    try std.testing.expect(std.mem.eql(u8, bound.bind.name, "x"));    const prefix = [_]V{ V.initI128(1), V.initI128(2) };    const rp = try constructors.restPattern(a, &prefix, .{ .discard = {} });    try std.testing.expectEqual(value_mod.PatternFormClass.rest_pattern, rp.patternClass().?);    try std.testing.expectEqual(@as(usize, 2), rp.rest_pattern.prefix.len);    try std.testing.expectEqual(value_mod.PatternFormClass.discard, rp.rest_pattern.rest.*.patternClass().?);}test "record/sequence/set/dictionary constructors copy their inputs" {    const V = Value(NoEmbedded);    const allocator = std.testing.allocator;    var arena = std.heap.ArenaAllocator.init(allocator);    defer arena.deinit();    const a = arena.allocator();    const label = symbol("pair");    const fields = [_]V{ V.initI128(1), V.initI128(2) };    const rec = try constructors.record(a, label, &fields);    try std.testing.expectEqual(value_mod.CompoundClass.record, rec.compoundClass().?);    try std.testing.expectEqual(@as(usize, 2), rec.record.fields.len);    try std.testing.expect(std.mem.eql(u8, rec.record.label.*.symbol, "pair"));    const items = [_]V{ V.initBoolean(false), V.initBoolean(true) };    const seq = try constructors.sequence(a, &items);    try std.testing.expectEqual(value_mod.CompoundClass.sequence, seq.compoundClass().?);    try std.testing.expectEqual(@as(usize, 2), seq.sequence.len);    const set_items = [_]V{ V.initI128(3), V.initI128(4) };    const s = try constructors.set(a, &set_items);    try std.testing.expectEqual(value_mod.CompoundClass.set, s.compoundClass().?);    try std.testing.expectEqual(@as(usize, 2), s.set.len);}test "set rejects duplicate elements without consuming inputs" {    const V = Value(NoEmbedded);    const allocator = std.testing.allocator;    var first = try V.initString(allocator, "duplicate");    defer first.deinit(allocator);    var second = try V.initString(allocator, "duplicate");    defer second.deinit(allocator);    const items = [_]V{ first, V.initI128(3), second };    try std.testing.expectError(error.DuplicateSetElement, constructors.set(allocator, &items));}test "set sorts distinct elements" {    const V = Value(NoEmbedded);    const allocator = std.testing.allocator;    var arena = std.heap.ArenaAllocator.init(allocator);    defer arena.deinit();    const a = arena.allocator();    const items = [_]V{ V.initI128(4), V.initI128(3), V.initI128(1) };    const s = try constructors.set(a, &items);    try std.testing.expectEqual(@as(usize, 3), s.set.len);    try std.testing.expectEqual(@as(i128, 1), try s.set[0].signed_integer.toI128());    try std.testing.expectEqual(@as(i128, 3), try s.set[1].signed_integer.toI128());    try std.testing.expectEqual(@as(i128, 4), try s.set[2].signed_integer.toI128());}test "dictionary rejects duplicate keys without consuming inputs" {    const V = Value(NoEmbedded);    const allocator = std.testing.allocator;    var first = try V.initString(allocator, "duplicate");    defer first.deinit(allocator);    var second = try V.initString(allocator, "duplicate");    defer second.deinit(allocator);    const entries = [_]V.DictionaryEntry{        .{ .key = first, .value = V.initI128(1) },        .{ .key = second, .value = V.initI128(2) },    };    try std.testing.expectError(        error.DuplicateDictionaryKey,        constructors.dictionary(allocator, &entries),    );}test "dictionary sorts distinct keys" {    const V = Value(NoEmbedded);    const allocator = std.testing.allocator;    var arena = std.heap.ArenaAllocator.init(allocator);    defer arena.deinit();    const entries = [_]V.DictionaryEntry{        .{ .key = V.initI128(3), .value = V.initI128(30) },        .{ .key = V.initI128(1), .value = V.initI128(10) },        .{ .key = V.initI128(2), .value = V.initI128(20) },    };    const d = try constructors.dictionary(arena.allocator(), &entries);    try std.testing.expectEqual(value_mod.CompoundClass.dictionary, d.compoundClass().?);    try std.testing.expectEqual(@as(usize, 3), d.dictionary.len);    try std.testing.expectEqual(@as(i128, 1), try d.dictionary[0].key.signed_integer.toI128());    try std.testing.expectEqual(@as(i128, 10), try d.dictionary[0].value.signed_integer.toI128());    try std.testing.expectEqual(@as(i128, 2), try d.dictionary[1].key.signed_integer.toI128());    try std.testing.expectEqual(@as(i128, 3), try d.dictionary[2].key.signed_integer.toI128());}test "any_constructors.embedded wraps a raw pointer" {    var payload: u32 = 0xdeadbeef;    const e = any_constructors.embedded(&payload);    try std.testing.expectEqual(@as(?value_mod.AtomClass, null), e.atomClass());    try std.testing.expect(e == .embedded);    try std.testing.expectEqual(@intFromPtr(&payload), @intFromPtr(e.embedded.value));}test "integer round-trips via toI64Lossy" {    const integer_mod = @import("integer.zig");    const round = integer(-12345);    try std.testing.expectEqual(@as(i64, -12345), integer_mod.SignedInteger.toI64Lossy(round.signed_integer));}

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

zig
pub const constructors_mod = @import("constructors.zig");

Audit

Definitions8
Public names8
Members0
Version26.7.0
Revisiondaab053ee433