Skip to documentation
SLOP

tiny.preserves.value

Reference tiny.preserves value

Defined in tiny.preserves.

One type holds every data value: atoms, compounds and embedded values of the host program.

API (12)

Actions

Public operations.

Types and contracts

Public types and contracts.

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

Source

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

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

Source: lib/preserves/src/value.zig

zig
//! One type holds every data value: atoms, compounds and embedded values of the host program. The//! atoms are booleans, doubles, integers of any size, strings, byte strings and symbols, and the//! compounds are records, sequences, sets and dictionaries.//!//! Two copies of one value have to be equal, order the same and hash alike, so a value can key a//! hash map and sort the same way everywhere. A program has to know which memory a value owns and//! which call frees it.//!//! A set or dictionary can sit in memory in any order: a program builds it by hand, and a text//! document lists it in the order its author wrote. Comparing two sets element by element needs//! both in sorted order, and sorting a copy would allocate memory on every comparison and every//! hash. A value's leaves can point at bytes it owns or bytes someone else keeps alive, and the//! value itself records neither.//!//! The type holds the values of the [Preserves](https://preserves.dev/) data language, which the//! package keeps along with its patterns and its text, binary and JSON forms. Preserves gives all//! values one total order in which sets and dictionaries compare through their elements in//! ascending order, whatever their storage order.//!//! The package builds that one type as a generic tagged union (`Value`). The union takes the type//! of its embedded values (*domain*) as a compile-time parameter, so each program picks what it//! embeds. A compile-time check (`assertIsDomain`) tests that type before the union is built. The//! union also holds four kinds of pattern (*pattern form*) beside the data: a discard, a capture of//! an inner pattern, a bind that names an inner pattern, and a rest pattern for the items after a//! sequence prefix. A value's kind (`ValueKind`) names its group (atom, compound, embedded value or//! pattern) and which member of the group it is. `compare` ranks the kinds in a fixed order,//! booleans first and patterns last, and within a kind compares contents. Doubles compare and hash//! by their bits, so every double, `NaN` included, has one place in the order.//!//! Equality, order and hash read each set and dictionary in ascending order under `compare`,//! whatever the order of its storage. That walk allocates nothing: a cursor finds each next element//! by scanning the whole set, so the cost grows with the square of the set's size. A Lean model//! proves that sorted copies of two sets are equal exactly when their elements are permutations of//! each other, and that every observation of a sorted copy gives one answer for all such//! permutations. The Lean results describe a model, and they check neither this Zig code nor its//! hash.//!//! Whether atom bytes are borrowed or copied depends on the constructor: `initString`,//! `initByteString` and `initSymbol` copy, and the root's `string` and `symbol` borrow. A value//! owns a compound's slice only when its creator hands the slice over and later frees the value//! with the same allocator. `deinit` frees everything a value points to, so it fits a value that//! owns every byte, such as a decoded value or a copy from `cloneValueDeep`.const std = @import("std");const Allocator = std.mem.Allocator;const atom_mod = @import("atom.zig");const integer_mod = @import("integer.zig");const domain_mod = @import("domain.zig");pub const AtomClass = atom_mod.AtomClass;pub const Atom = atom_mod.Atom;pub const CowBytes = atom_mod.CowBytes;pub const CowSignedInteger = atom_mod.CowSignedInteger;pub const Ownership = atom_mod.Ownership;pub const SignedInteger = integer_mod.SignedInteger;pub const NoEmbedded = domain_mod.NoEmbedded;/// The four compound kinds, in the order `compare` ranks them: record, sequence, set and/// dictionary. Code that switches on a compound's kind reads this from `compoundClass`./// `compoundClass` returns it for a compound and `null` for any other value.pub const CompoundClass = enum { record, sequence, set, dictionary };/// The four kinds of pattern, in the order `compare` ranks them: discard, capture, bind and rest/// pattern. Code that switches on a pattern's kind reads this from `patternClass`. `patternClass`/// returns it for a pattern and `null` for any other value. These kinds rank after every data kind.pub const PatternFormClass = enum { discard, capture, bind, rest_pattern };/// The four groups of kinds: atom, compound, embedded value and pattern. Code that switches on the/// group of a value's kind reads this from `ValueKind`. This enum tags `ValueKind`.pub const ValueKindTag = enum { atomic, compound, embedded, pattern };/// A value's kind: its group and, for an atom, a compound or a pattern, which one. Code that/// switches on a value's kind reads this from `kind`. `kind` returns it for any value.pub const ValueKind = union(ValueKindTag) {    /// Which of the six atom kinds the value is.    atomic: AtomClass,    /// Which of the four compound kinds the value is.    compound: CompoundClass,    /// Marks an embedded value, which has no subkind.    embedded,    /// Which of the four kinds of pattern the value is.    pattern: PatternFormClass,    /// Returns whether two kinds are in the same group and, within it, the same kind. `Value.eql`    /// calls it to check two values' kinds before their contents.    pub fn eql(a: ValueKind, b: ValueKind) bool {        return switch (a) {            .atomic => |ac| switch (b) {                .atomic => |bc| ac == bc,                else => false,            },            .compound => |ac| switch (b) {                .compound => |bc| ac == bc,                else => false,            },            .embedded => switch (b) {                .embedded => true,                else => false,            },            .pattern => |ac| switch (b) {                .pattern => |bc| ac == bc,                else => false,            },        };    }};/// Returns the tagged union of all values whose embedded values have type `D`. Every program that/// stores Preserves data calls it once for the type of its embedded values, as the text codec does/// with `AnyEmbedded`. The function stops compilation unless `D` meets `assertIsDomain`. Whether/// atom bytes are borrowed or copied depends on the constructor: `initString`, `initByteString` and/// `initSymbol` copy, and the root's `string` and `symbol` borrow. A value owns a compound's slice/// only when its creator hands the slice over and later calls `deinit` with the same allocator./// Every decoder copies the bytes it reads, so a decoded value owns every byte and `deinit` frees/// it.pub fn Value(comptime D: type) type {    domain_mod.assertIsDomain(D);    return union(enum) {        pub const Self = @This();        /// The type of the embedded values, `D`. Generic code reads it to recover the type of the        /// embedded values from a value type. `text.decode` and the binary `decode` take that type        /// as their first parameter, so `Value.Domain` fills it.        pub const Domain = D;        /// A slice of values, the storage of a sequence and of a record's fields. Code that builds        /// a record's fields or a sequence names the slice with this type.        pub const Sequence = []Self;        /// A slice of values, the storage of a set, holding each element once in any order. Code        /// that builds a set names the slice with this type. Equality, order and hash read it in        /// ascending order under `compare`.        pub const Set = []Self;        /// One key and value pair of a dictionary. Code that builds a dictionary names each entry        /// with this type, as the constructors and the JSON reader do.        pub const DictionaryEntry = struct {            /// The entry's key, which appears once in its dictionary.            key: Self,            /// The value the key maps to.            value: Self,        };        /// A slice of entries, the storage of a dictionary, in any order. Code that builds a        /// dictionary names the slice with this type. Equality, order and hash read the entries in        /// ascending order of key, then value, under `compare`.        pub const Dictionary = []DictionaryEntry;        /// The parts of a record: a label and its fields. Code that reads a record names its parts        /// with this type.        pub const Record = struct {            /// A pointer to the label value, kept in its own cell so a record can have any value as            /// its label. Records compare their labels first.            label: *Self,            /// The record's fields, in order. Records with equal labels compare their fields in            /// order, and a shorter list that matches ranks first.            fields: Sequence,        };        /// The parts of a bind: a name and the pattern it names. Code that reads a bind names its        /// parts with this type.        pub const Bind = struct {            /// The bind's name. Two binds are equal only when their names are equal byte for byte.            /// `deinit` frees the name, and `freeValue` and `freeValueDeep` leave it.            name: []const u8,            /// A pointer to the pattern the bind names, in its own cell.            pattern: *Self,        };        /// The parts of a rest pattern: patterns for the first items of a sequence and one pattern        /// for the items after them. Code that reads a rest pattern names its parts with this type.        pub const RestPattern = struct {            /// The patterns for the first items, in order.            prefix: []Self,            /// A pointer to the pattern for the items after the prefix, in its own cell.            rest: *Self,        };        /// A boolean. `false` orders before `true`.        boolean: bool,        /// A 64-bit double. Doubles are equal when their bits are equal, and they order by a total        /// order over their bits, so `0.0` and `-0.0` differ and a `NaN` has one place.        double: f64,        /// An integer of any size. `deinit` frees its heap digits when it has any.        signed_integer: SignedInteger,        /// The string's bytes. The text reader rejects invalid UTF-8 bytes. Strings compare byte by        /// byte.        string: []const u8,        /// The byte string's bytes, compared byte by byte.        byte_string: []const u8,        /// The symbol's name bytes, compared byte by byte.        symbol: []const u8,        /// A record: a label and its fields.        record: Record,        /// A sequence: values in order, compared item by item.        sequence: Sequence,        /// A set: each element once, stored in any order. Equality, order and hash read it in        /// ascending order under `compare`, whatever the order of its storage.        set: Set,        /// A dictionary: each key once, mapped to a value, stored in any order. Equality, order and        /// hash read it in ascending order of key, then value, whatever the order of its storage.        dictionary: Dictionary,        /// An embedded value of the type `D`, which supplies its equality, order, freeing and        /// copying.        embedded: D,        /// The discard pattern carries no data. All discards are equal.        discard: void,        /// A capture: a pointer to its inner pattern, in its own cell.        capture: *Self,        /// A bind: a name and the pattern it names.        bind: Bind,        /// A rest pattern: patterns for a sequence's first items and one pattern for the rest.        rest_pattern: RestPattern,        /// Returns the value's kind: its group and which member of the group. Code that switches on        /// a value's kind calls it.        pub fn kind(self: Self) ValueKind {            return switch (self) {                .boolean => .{ .atomic = .boolean },                .double => .{ .atomic = .double },                .signed_integer => .{ .atomic = .signed_integer },                .string => .{ .atomic = .string },                .byte_string => .{ .atomic = .byte_string },                .symbol => .{ .atomic = .symbol },                .record => .{ .compound = .record },                .sequence => .{ .compound = .sequence },                .set => .{ .compound = .set },                .dictionary => .{ .compound = .dictionary },                .embedded => .embedded,                .discard => .{ .pattern = .discard },                .capture => .{ .pattern = .capture },                .bind => .{ .pattern = .bind },                .rest_pattern => .{ .pattern = .rest_pattern },            };        }        /// Returns the atom kind of an atom, and `null` for any other value. Code that handles        /// atoms calls it to test for one and learn which.        pub fn atomClass(self: Self) ?AtomClass {            return switch (self.kind()) {                .atomic => |a| a,                else => null,            };        }        /// Returns the compound kind of a compound, and `null` for any other value. Code that        /// handles compounds calls it to test for one and learn which.        pub fn compoundClass(self: Self) ?CompoundClass {            return switch (self.kind()) {                .compound => |c| c,                else => null,            };        }        /// Returns the kind of pattern, and `null` for any other value. Code that handles patterns        /// calls it to test for one and learn which.        pub fn patternClass(self: Self) ?PatternFormClass {            return switch (self.kind()) {                .pattern => |p| p,                else => null,            };        }        /// Makes a boolean value from `v`, with no allocation. Readers and tests call it for a        /// boolean value.        pub fn initBoolean(v: bool) Self {            return .{ .boolean = v };        }        /// Makes a double value from `v`, with no allocation. The binary and text readers call it        /// for a double value.        pub fn initDouble(v: f64) Self {            return .{ .double = v };        }        /// Makes an integer value from `v`, with no allocation. Readers and constructors call it        /// for an integer value.        pub fn initI128(v: i128) Self {            return .{ .signed_integer = SignedInteger.fromI128(v) };        }        /// Makes an integer value from `v`, stored in 128 signed bits when it fits there, with no        /// allocation. Code that holds a number above the 128-bit signed range calls it, as the        /// tests of `text.encode` do.        pub fn initU128(v: u128) Self {            return .{ .signed_integer = SignedInteger.fromU128(v) };        }        /// Makes an integer value from `v`. The readers call it for an integer they already parsed.        /// The value takes `v`'s heap digits, so `deinit` on the value frees them.        pub fn initSignedInteger(v: SignedInteger) Self {            return .{ .signed_integer = v };        }        /// Makes a string value holding a copy of `bytes` from `allocator`. Code that builds a        /// string it must own calls it, as `cloneValueDeep` does. `deinit` with the same allocator        /// frees the copy.        pub fn initString(allocator: Allocator, bytes: []const u8) !Self {            return .{ .string = try allocator.dupe(u8, bytes) };        }        /// Makes a byte-string value holding a copy of `bytes` from `allocator`. Code that builds a        /// byte string it must own calls it. `deinit` with the same allocator frees the copy.        pub fn initByteString(allocator: Allocator, bytes: []const u8) !Self {            return .{ .byte_string = try allocator.dupe(u8, bytes) };        }        /// Makes a symbol value holding a copy of `bytes` from `allocator`. Code that builds a        /// symbol it must own calls it, as the record builders and `cloneValueDeep` do. `deinit`        /// with the same allocator frees the copy.        pub fn initSymbol(allocator: Allocator, bytes: []const u8) !Self {            return .{ .symbol = try allocator.dupe(u8, bytes) };        }        /// Makes a record from `label` and `fields`, allocating one cell for the label from        /// `allocator`. The binary and text readers and `cloneValueDeep` call it for a record whose        /// parts they own. The record takes `fields` as given, with no copy, so `deinit` with the        /// same allocator frees the slice, the label and every field. On `error.OutOfMemory`        /// nothing is allocated, and the caller still owns `label` and `fields`.        pub fn initRecord(allocator: Allocator, label: Self, fields: Sequence) !Self {            const label_ptr = try allocator.create(Self);            errdefer allocator.destroy(label_ptr);            label_ptr.* = label;            return .{ .record = .{ .label = label_ptr, .fields = fields } };        }        /// Makes a sequence that takes `items` as given, with no copy and no allocation.        /// `cloneValueDeep` and the embedded copy function call it for a sequence whose slice they        /// own.        pub fn initSequence(items: Sequence) Self {            return .{ .sequence = items };        }        /// Makes a set that takes `items` as given, with no copy, no sort and no allocation.        /// `cloneValueDeep` and the embedded copy function call it for a copy of a set already held        /// as a value, so no second check runs. The call does not check for equal elements, so a        /// caller who has not checked calls `setElementsDistinct` first or uses the `set`        /// constructor.        pub fn initSet(items: Set) Self {            return .{ .set = items };        }        /// Makes a dictionary that takes `entries` as given, with no copy, no sort and no        /// allocation. `cloneValueDeep` and the embedded copy function call it for a copy of a        /// dictionary already held as a value, so no second check runs. The call does not check for        /// equal keys, so a caller who has not checked calls `dictionaryKeysDistinct` first or uses        /// the `dictionary` constructor.        pub fn initDictionary(entries: Dictionary) Self {            return .{ .dictionary = entries };        }        /// Returns whether some item of `items` equals `needle` under `eql`. The binary and text        /// readers call it with the elements read so far, so each new element that repeats one is        /// rejected. The call scans every item and allocates nothing.        pub fn setContainsElement(items: []const Self, needle: Self) bool {            for (items) |item| {                if (item.eql(needle)) return true;            }            return false;        }        /// Returns whether some entry of `entries` has a key equal to `needle` under `eql`. The        /// binary and text readers call it with the keys read so far, so each new key that repeats        /// one is rejected. The call scans every entry and allocates nothing.        pub fn dictionaryContainsKey(            entries: []const DictionaryEntry,            needle: Self,        ) bool {            for (entries) |entry| {                if (entry.key.eql(needle)) return true;            }            return false;        }        /// Returns whether no two items of `items` are equal. The `set` constructor and the binary,        /// text and JSON writers call it to reject duplicate set elements. The call compares each        /// item with every item before it, so its cost grows with the square of the slice's length,        /// and it allocates nothing.        pub fn setElementsDistinct(items: []const Self) bool {            for (items, 0..) |item, index| {                if (setContainsElement(items[0..index], item)) return false;            }            return true;        }        /// Returns whether no two entries of `entries` have equal keys. The `dictionary`        /// constructor and the binary, text and JSON writers call it to reject duplicate keys. The        /// call compares each key with every key before it, so its cost grows with the square of        /// the slice's length, and it allocates nothing.        pub fn dictionaryKeysDistinct(entries: []const DictionaryEntry) bool {            for (entries, 0..) |entry, index| {                if (dictionaryContainsKey(entries[0..index], entry.key)) return false;            }            return true;        }        /// Makes a value holding the embedded value `d`, with no allocation. The text reader and        /// `cloneValueDeep` call it for an embedded value they built. `deinit` frees `d` through        /// the type's `deinit`.        pub fn initEmbedded(d: D) Self {            return .{ .embedded = d };        }        /// Returns a borrowed copy of the value's atom, and `null` for any other value. Code that        /// takes one atom out of a value calls it for a copy that records who owns the bytes. The        /// copy points at the value's own bytes, so the value has to outlive it.        pub fn asAtom(self: *const Self) ?Atom {            return switch (self.*) {                .boolean => |v| Atom.fromBool(v),                .double => |v| Atom.fromDouble(v),                .signed_integer => |v| Atom.fromSignedIntegerBorrowed(v),                .string => |v| Atom.fromStringBorrowed(v),                .byte_string => |v| Atom.fromByteStringBorrowed(v),                .symbol => |v| Atom.fromSymbolBorrowed(v),                .record, .sequence, .set, .dictionary, .embedded => null,                .discard, .capture, .bind, .rest_pattern => null,            };        }        /// Frees everything the value points to with `allocator`: atom bytes, integer digits,        /// compound storage, the cells and names of patterns, and embedded values through the        /// type's `deinit`. Code that decoded a value, or owns every byte of one, calls it when        /// done with it. Every byte has to come from `allocator`, so a value that borrows any bytes        /// goes to `freeValue` or `freeValueDeep`. Copies from `cloneValueDeep` own every byte. The        /// value is undefined afterward.        pub fn deinit(self: *Self, allocator: Allocator) void {            switch (self.*) {                .boolean, .double => {},                .signed_integer => |*si| si.deinit(allocator),                .string => |s| allocator.free(s),                .byte_string => |s| allocator.free(s),                .symbol => |s| allocator.free(s),                .record => |*r| {                    r.label.deinit(allocator);                    allocator.destroy(r.label);                    for (r.fields) |*f| f.deinit(allocator);                    allocator.free(r.fields);                },                .sequence => |s| {                    for (s) |*item| item.deinit(allocator);                    allocator.free(s);                },                .set => |s| {                    for (s) |*item| item.deinit(allocator);                    allocator.free(s);                },                .dictionary => |d| {                    for (d) |*entry| {                        entry.key.deinit(allocator);                        entry.value.deinit(allocator);                    }                    allocator.free(d);                },                .embedded => |*e| D.deinit(e, allocator),                .discard => {},                .capture => |p| {                    p.deinit(allocator);                    allocator.destroy(p);                },                .bind => |*bp| {                    bp.pattern.deinit(allocator);                    allocator.destroy(bp.pattern);                    allocator.free(bp.name);                },                .rest_pattern => |*rp| {                    for (rp.prefix) |*item| item.deinit(allocator);                    allocator.free(rp.prefix);                    rp.rest.deinit(allocator);                    allocator.destroy(rp.rest);                },            }            self.* = undefined;        }        /// Returns whether two values are equal: same kind and equal contents. Hash maps, sets and        /// every duplicate check call it for value equality. Sets and dictionaries are equal when        /// they hold the same elements or entries, each as many times, whatever their storage        /// order. Doubles compare by bits, binds by name and pattern, and embedded values through        /// the type's `eql`. The call allocates nothing, and its cost on two sets grows with the        /// square of their size.        pub fn eql(a: Self, b: Self) bool {            if (!a.kind().eql(b.kind())) return false;            return switch (a) {                .boolean => |v| v == b.boolean,                .double => |v| @as(u64, @bitCast(v)) == @as(u64, @bitCast(b.double)),                .signed_integer => |v| v.eql(b.signed_integer),                .string => |v| std.mem.eql(u8, v, b.string),                .byte_string => |v| std.mem.eql(u8, v, b.byte_string),                .symbol => |v| std.mem.eql(u8, v, b.symbol),                .record => |v| v.label.*.eql(b.record.label.*) and sequenceEql(v.fields, b.record.fields),                .sequence => |v| sequenceEql(v, b.sequence),                .set => |v| setEql(v, b.set),                .dictionary => |v| dictionaryEql(v, b.dictionary),                .embedded => |v| D.eql(v, b.embedded),                .discard => true,                .capture => |p| p.eql(b.capture.*),                .bind => |bp| std.mem.eql(u8, bp.name, b.bind.name) and bp.pattern.eql(b.bind.pattern.*),                .rest_pattern => |rp| sequenceEql(rp.prefix, b.rest_pattern.prefix) and rp.rest.eql(b.rest_pattern.rest.*),            };        }        fn sequenceEql(a: []const Self, b: []const Self) bool {            if (a.len != b.len) return false;            for (a, b) |av, bv| if (!av.eql(bv)) return false;            return true;        }        fn setEql(a: Set, b: Set) bool {            if (a.len != b.len) return false;            for (a, 0..) |candidate, i| {                if (countSetMatches(a[0..i], candidate) != 0) continue;                if (countSetMatches(a, candidate) != countSetMatches(b, candidate)) return false;            }            return true;        }        fn countSetMatches(items: []const Self, needle: Self) usize {            var count: usize = 0;            for (items) |item| {                if (item.eql(needle)) count += 1;            }            return count;        }        fn dictionaryEql(a: Dictionary, b: Dictionary) bool {            if (a.len != b.len) return false;            for (a, 0..) |candidate, i| {                if (countDictionaryEntryMatches(a[0..i], candidate) != 0) continue;                if (countDictionaryEntryMatches(a, candidate) != countDictionaryEntryMatches(b, candidate)) return false;            }            return true;        }        fn countDictionaryEntryMatches(entries: Dictionary, needle: DictionaryEntry) usize {            var count: usize = 0;            for (entries) |entry| {                if (entry.key.eql(needle.key) and entry.value.eql(needle.value)) count += 1;            }            return count;        }        /// Returns the order of `a` against `b`, a total order over all values. Sorting        /// constructors and writers call it for the package's one total order. Kinds rank booleans,        /// doubles, integers, strings, byte strings, symbols, records, sequences, sets,        /// dictionaries, embedded values, then the four kinds of pattern. Within a kind, records        /// compare label then fields, sequences item by item, and sets and dictionaries through        /// their elements in ascending order, with a shorter list that matches ranking first.        /// Embedded values order through the type's `order`. The call allocates nothing, and its        /// cost on two sets grows with the square of their size.        pub fn compare(a: Self, b: Self) std.math.Order {            const ra = typeRank(a);            const rb = typeRank(b);            if (ra != rb) return std.math.order(ra, rb);            return switch (a) {                .boolean => |va| std.math.order(@intFromBool(va), @intFromBool(b.boolean)),                .double => |va| floatTotalOrder(va, b.double),                .signed_integer => |va| va.order(b.signed_integer),                .string => |va| std.mem.order(u8, va, b.string),                .byte_string => |va| std.mem.order(u8, va, b.byte_string),                .symbol => |va| std.mem.order(u8, va, b.symbol),                .record => |va| recordCompare(va, b.record),                .sequence => |va| sliceCompare(va, b.sequence),                .set => |va| setCompare(va, b.set),                .dictionary => |va| dictionaryCompare(va, b.dictionary),                .embedded => |va| D.order(va, b.embedded),                .discard => .eq,                .capture => |va| va.compare(b.capture.*),                .bind => |va| bindCompare(va, b.bind),                .rest_pattern => |va| restPatternCompare(va, b.rest_pattern),            };        }        fn typeRank(v: Self) u8 {            return switch (v) {                .boolean => 0,                .double => 1,                .signed_integer => 2,                .string => 3,                .byte_string => 4,                .symbol => 5,                .record => 6,                .sequence => 7,                .set => 8,                .dictionary => 9,                .embedded => 10,                .discard => 11,                .capture => 12,                .bind => 13,                .rest_pattern => 14,            };        }        fn recordCompare(a: Record, b: Record) std.math.Order {            const label_ord = a.label.compare(b.label.*);            if (label_ord != .eq) return label_ord;            return sliceCompare(a.fields, b.fields);        }        const ValueCursor = struct {            items: []const Self,            after: ?Self = null,            current: ?Self = null,            remaining: usize = 0,            fn next(cursor: *@This()) Self {                if (cursor.remaining == 0) {                    const run = nextValueRun(cursor.items, cursor.after).?;                    cursor.current = run.value;                    cursor.after = run.value;                    cursor.remaining = run.count;                }                cursor.remaining -= 1;                return cursor.current.?;            }        };        const ValueRun = struct {            value: Self,            count: usize,        };        fn nextValueRun(items: []const Self, after: ?Self) ?ValueRun {            var candidate: ?Self = null;            for (items) |item| {                if (after) |previous| {                    if (item.compare(previous) != .gt) continue;                }                if (candidate == null or item.compare(candidate.?) == .lt) {                    candidate = item;                }            }            const value = candidate orelse return null;            var count: usize = 0;            for (items) |item| {                if (item.compare(value) == .eq) count += 1;            }            return .{ .value = value, .count = count };        }        fn setCompare(a: Set, b: Set) std.math.Order {            var a_cursor = ValueCursor{ .items = a };            var b_cursor = ValueCursor{ .items = b };            for (0..@min(a.len, b.len)) |_| {                const order = a_cursor.next().compare(b_cursor.next());                if (order != .eq) return order;            }            return std.math.order(a.len, b.len);        }        const DictionaryCursor = struct {            entries: Dictionary,            after: ?DictionaryEntry = null,            current: ?DictionaryEntry = null,            remaining: usize = 0,            fn next(cursor: *@This()) DictionaryEntry {                if (cursor.remaining == 0) {                    const run = nextDictionaryRun(cursor.entries, cursor.after).?;                    cursor.current = run.entry;                    cursor.after = run.entry;                    cursor.remaining = run.count;                }                cursor.remaining -= 1;                return cursor.current.?;            }        };        const DictionaryRun = struct {            entry: DictionaryEntry,            count: usize,        };        fn dictionaryEntryCompare(a: DictionaryEntry, b: DictionaryEntry) std.math.Order {            const key_order = a.key.compare(b.key);            if (key_order != .eq) return key_order;            return a.value.compare(b.value);        }        fn nextDictionaryRun(entries: Dictionary, after: ?DictionaryEntry) ?DictionaryRun {            var candidate: ?DictionaryEntry = null;            for (entries) |entry| {                if (after) |previous| {                    if (dictionaryEntryCompare(entry, previous) != .gt) continue;                }                if (candidate == null or dictionaryEntryCompare(entry, candidate.?) == .lt) {                    candidate = entry;                }            }            const selected = candidate orelse return null;            var count: usize = 0;            for (entries) |entry| {                if (dictionaryEntryCompare(entry, selected) == .eq) count += 1;            }            return .{ .entry = selected, .count = count };        }        fn dictionaryCompare(a: Dictionary, b: Dictionary) std.math.Order {            var a_cursor = DictionaryCursor{ .entries = a };            var b_cursor = DictionaryCursor{ .entries = b };            for (0..@min(a.len, b.len)) |_| {                const order = dictionaryEntryCompare(a_cursor.next(), b_cursor.next());                if (order != .eq) return order;            }            return std.math.order(a.len, b.len);        }        fn bindCompare(a: Bind, b: Bind) std.math.Order {            const no = std.mem.order(u8, a.name, b.name);            if (no != .eq) return no;            return a.pattern.compare(b.pattern.*);        }        fn restPatternCompare(a: RestPattern, b: RestPattern) std.math.Order {            const po = sliceCompare(a.prefix, b.prefix);            if (po != .eq) return po;            return a.rest.compare(b.rest.*);        }        fn sliceCompare(a: []const Self, b: []const Self) std.math.Order {            const len = @min(a.len, b.len);            for (a[0..len], b[0..len]) |ia, ib| {                const o = ia.compare(ib);                if (o != .eq) return o;            }            return std.math.order(a.len, b.len);        }        fn floatTotalOrder(a: f64, b: f64) std.math.Order {            const bits_a = @as(u64, @bitCast(a));            const bits_b = @as(u64, @bitCast(b));            const sign_a = bits_a >> 63;            const sign_b = bits_b >> 63;            if (sign_a != sign_b) return if (sign_a > sign_b) .lt else .gt;            if (sign_a == 1) return std.math.order(bits_b, bits_a);            return std.math.order(bits_a, bits_b);        }        /// Returns a 64-bit Wyhash, seed 0, of the value's kind and contents. Hash maps call it.        /// Sets and dictionaries feed their elements in ascending order under `compare`, so equal        /// values hash alike whatever their storage order. An embedded value adds its type's `hash`        /// when the type declares one, and nothing otherwise. The call allocates nothing, and its        /// cost on a set grows with the square of the set's size.        pub fn hash(self: Self) u64 {            var hasher = std.hash.Wyhash.init(0);            hashInto(&hasher, self);            return hasher.final();        }        fn hashInto(hasher: *std.hash.Wyhash, v: Self) void {            const tag: u8 = typeRank(v);            hasher.update(&.{tag});            switch (v) {                .boolean => |b| hasher.update(&.{@intFromBool(b)}),                .double => |f| {                    const bits: u64 = @bitCast(f);                    hasher.update(std.mem.asBytes(&bits));                },                .signed_integer => |si| hashSignedInteger(hasher, si),                .string => |s| hasher.update(s),                .byte_string => |b| hasher.update(b),                .symbol => |s| hasher.update(s),                .record => |r| {                    hashInto(hasher, r.label.*);                    for (r.fields) |field| hashInto(hasher, field);                },                .sequence => |s| for (s) |item| hashInto(hasher, item),                .set => |s| {                    var cursor = ValueCursor{ .items = s };                    for (0..s.len) |_| hashInto(hasher, cursor.next());                },                .dictionary => |d| {                    var cursor = DictionaryCursor{ .entries = d };                    for (0..d.len) |_| {                        const entry = cursor.next();                        hashInto(hasher, entry.key);                        hashInto(hasher, entry.value);                    }                },                .embedded => |e| {                    if (@hasDecl(D, "hash")) {                        const h: u64 = D.hash(e);                        hasher.update(std.mem.asBytes(&h));                    }                },                .discard => {},                .capture => |p| hashInto(hasher, p.*),                .bind => |bp| {                    hasher.update(bp.name);                    hashInto(hasher, bp.pattern.*);                },                .rest_pattern => |rp| {                    for (rp.prefix) |item| hashInto(hasher, item);                    hashInto(hasher, rp.rest.*);                },            }        }        fn hashSignedInteger(hasher: *std.hash.Wyhash, si: SignedInteger) void {            switch (si.repr) {                .i128 => |iv| {                    hasher.update(&.{0});                    hasher.update(std.mem.asBytes(&iv));                },                .u128 => |uv| {                    hasher.update(&.{1});                    hasher.update(std.mem.asBytes(&uv));                },                .big => |b| {                    hasher.update(&.{2});                    hasher.update(b);                },            }        }    };}test "Value(NoEmbedded) atoms round-trip through kind" {    const V = Value(NoEmbedded);    try std.testing.expectEqual(AtomClass.boolean, V.initBoolean(true).atomClass().?);    try std.testing.expectEqual(AtomClass.double, V.initDouble(1.5).atomClass().?);    try std.testing.expectEqual(AtomClass.signed_integer, V.initI128(42).atomClass().?);}test "Value(NoEmbedded) compound smoke" {    const V = Value(NoEmbedded);    const allocator = std.testing.allocator;    var arena = std.heap.ArenaAllocator.init(allocator);    defer arena.deinit();    const arena_alloc = arena.allocator();    const sym = try V.initSymbol(arena_alloc, "tag");    const fields = try arena_alloc.alloc(V, 2);    fields[0] = V.initI128(1);    fields[1] = V.initBoolean(false);    var rec = try V.initRecord(arena_alloc, sym, fields);    try std.testing.expectEqual(CompoundClass.record, rec.compoundClass().?);    try std.testing.expect(rec.eql(rec));    _ = &rec;}test "Value.asAtom produces a borrowed view" {    const V = Value(NoEmbedded);    const literal = "hi";    const v = V{ .string = literal };    const atom = v.asAtom().?;    try std.testing.expectEqual(AtomClass.string, atom.class());    try std.testing.expectEqual(Ownership.borrowed, atom.string.ownership);    try std.testing.expect(std.mem.eql(u8, atom.string.bytes, literal));}test "Value.eql distinguishes duplicate-bearing sets" {    const V = Value(NoEmbedded);    const allocator = std.testing.allocator;    var arena = std.heap.ArenaAllocator.init(allocator);    defer arena.deinit();    const a = arena.allocator();    const duplicate_items = try a.alloc(V, 2);    duplicate_items[0] = V.initI128(1);    duplicate_items[1] = V.initI128(1);    const canonical_items = try a.alloc(V, 2);    canonical_items[0] = V.initI128(1);    canonical_items[1] = V.initI128(2);    try std.testing.expect(!V.initSet(duplicate_items).eql(V.initSet(canonical_items)));}test "Value.eql distinguishes duplicate-bearing dictionaries" {    const V = Value(NoEmbedded);    const allocator = std.testing.allocator;    var arena = std.heap.ArenaAllocator.init(allocator);    defer arena.deinit();    const a = arena.allocator();    const duplicate_entries = try a.alloc(V.DictionaryEntry, 2);    duplicate_entries[0] = .{        .key = try V.initSymbol(a, "dup"),        .value = V.initI128(1),    };    duplicate_entries[1] = .{        .key = try V.initSymbol(a, "dup"),        .value = V.initI128(1),    };    const canonical_entries = try a.alloc(V.DictionaryEntry, 2);    canonical_entries[0] = .{        .key = try V.initSymbol(a, "dup"),        .value = V.initI128(1),    };    canonical_entries[1] = .{        .key = try V.initSymbol(a, "other"),        .value = V.initI128(1),    };    try std.testing.expect(!V.initDictionary(duplicate_entries).eql(V.initDictionary(canonical_entries)));}test "Value pattern-form variants surface their class" {    const V = Value(NoEmbedded);    const allocator = std.testing.allocator;    var arena = std.heap.ArenaAllocator.init(allocator);    defer arena.deinit();    const a = arena.allocator();    const discard_v: V = .{ .discard = {} };    try std.testing.expectEqual(PatternFormClass.discard, discard_v.patternClass().?);    try std.testing.expect(discard_v.atomClass() == null);    try std.testing.expect(discard_v.compoundClass() == null);    try std.testing.expect(discard_v.asAtom() == null);    const inner = try a.create(V);    inner.* = V.initI128(7);    const capture_v: V = .{ .capture = inner };    try std.testing.expectEqual(PatternFormClass.capture, capture_v.patternClass().?);    const bind_inner = try a.create(V);    bind_inner.* = V.initBoolean(true);    const bind_v: V = .{ .bind = .{ .name = "x", .pattern = bind_inner } };    try std.testing.expectEqual(PatternFormClass.bind, bind_v.patternClass().?);    const prefix = try a.alloc(V, 1);    prefix[0] = V.initI128(1);    const rest = try a.create(V);    rest.* = .{ .discard = {} };    const rest_v: V = .{ .rest_pattern = .{ .prefix = prefix, .rest = rest } };    try std.testing.expectEqual(PatternFormClass.rest_pattern, rest_v.patternClass().?);    try std.testing.expect(discard_v.eql(discard_v));    try std.testing.expect(!discard_v.eql(capture_v));}test "Value.compare total-orders across kind ranks" {    const V = Value(NoEmbedded);    const allocator = std.testing.allocator;    var arena = std.heap.ArenaAllocator.init(allocator);    defer arena.deinit();    const a = arena.allocator();    try std.testing.expectEqual(std.math.Order.lt, V.compare(V.initBoolean(false), V.initBoolean(true)));    try std.testing.expectEqual(std.math.Order.eq, V.compare(V.initI128(5), V.initI128(5)));    try std.testing.expectEqual(std.math.Order.lt, V.compare(V.initDouble(1.0), V.initI128(0)));    const ss = try V.initString(a, "abc");    const sb = try V.initString(a, "abd");    try std.testing.expectEqual(std.math.Order.lt, V.compare(ss, sb));    const discard_v: V = .{ .discard = {} };    try std.testing.expectEqual(std.math.Order.gt, V.compare(discard_v, V.initI128(0)));    try std.testing.expectEqual(std.math.Order.eq, V.compare(discard_v, discard_v));}test "Value.hash respects equality for canonical values" {    const V = Value(NoEmbedded);    const allocator = std.testing.allocator;    var arena = std.heap.ArenaAllocator.init(allocator);    defer arena.deinit();    const a = arena.allocator();    const x = V.initI128(42);    const y = V.initI128(42);    try std.testing.expectEqual(x.hash(), y.hash());    const s1 = try V.initString(a, "hello");    const s2 = try V.initString(a, "hello");    try std.testing.expectEqual(s1.hash(), s2.hash());    try std.testing.expect(x.hash() != V.initI128(7).hash());}

Audit

Definitions1
Public names1
Members0
Version26.7.0
Revisiondaab053ee433