Skip to documentation
SLOP

tiny.preserves.atom

Reference tiny.preserves atom

Defined in tiny.preserves.

A copy of one atom, taken out of a value, records whether its holder owns the bytes behind it.

API (6)

Types and contracts

Public types and contracts.

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

Source

Source: lib/preserves/src/atom.zig

zig
//! A copy of one atom, taken out of a value, records whether its holder owns the bytes behind it.//! An atom is a boolean, a double, an integer, a string, a byte string or a symbol.//!//! A program that keeps atoms apart from the tree they came from has to know, for each one, whether//! freeing its bytes is the program's job.//!//! Atom bytes come from two places: memory that some other owner keeps alive, and memory the holder//! allocated itself. A value tree stores its text as bare slices and records neither case, so one//! free call over the tree is wrong for one of the two. 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 six atom kinds are the atoms of the [Preserves](https://preserves.dev/) data language, which//! the package keeps along with its compounds and embedded values.//!//! Each copy carries a two-state tag (`Ownership`). The tag says whether the holder owns the copy's//! bytes or borrows them. A byte slice with that tag (`CowBytes`) holds the text of a string, byte//! string or symbol. An integer with that tag (`CowSignedInteger`) holds a number whose digits may//! live on the heap. The union of all six kinds (`Atom`) holds booleans and doubles directly, since//! they own no storage. Freeing a copy frees only what it owns, and promoting a borrowed copy//! copies its bytes into storage its holder owns. Two copies of one atom compare equal whatever//! their ownership tags. `Value.asAtom` returns a borrowed copy of one atom of a value, and `null`//! for any other kind. The codecs and the free calls of the package never read these types, and a//! value never stores them.const std = @import("std");const Allocator = std.mem.Allocator;const integer = @import("integer.zig");pub const SignedInteger = integer.SignedInteger;/// The six kinds of atom, in the order `Value` ranks them: boolean, double, integer, string, byte/// string and symbol. Code that switches on an atom's kind reads this, from `Value.atomClass` or/// `Atom.class`. `Value.atomClass` returns it for an atom and `null` for any other value. It tags/// the union `Atom`.pub const AtomClass = enum(u8) {    boolean,    double,    signed_integer,    string,    byte_string,    symbol,};/// Whether the holder of some bytes owns them, and frees them with its own `deinit`, or borrows/// them from an owner that keeps them alive. Code that holds atom bytes apart from their value/// reads this to decide whether `deinit` frees them.pub const Ownership = enum { borrowed, owned };/// A byte slice with a tag that says whether its holder owns the bytes. Code that keeps a string,/// byte string or symbol apart from its value holds it in this type, so its ownership travels with/// it. A new one borrows its bytes unless its builder says otherwise.pub const CowBytes = struct {    /// The bytes of the string, byte string or symbol.    bytes: []const u8,    /// Whether the holder owns `bytes`. It defaults to borrowed, so a struct written by hand never    /// frees bytes it did not allocate.    ownership: Ownership = .borrowed,    /// Wraps `bytes` as borrowed. Code that wraps bytes it keeps alive elsewhere calls it. `deinit`    /// leaves the bytes alone, so they have to outlive the result.    pub fn borrow(bytes: []const u8) CowBytes {        return .{ .bytes = bytes, .ownership = .borrowed };    }    /// Wraps `bytes` as owned. Code that wraps bytes it allocated calls it. `deinit` frees them    /// with the allocator it is given, so they have to come from that allocator.    pub fn own(bytes: []const u8) CowBytes {        return .{ .bytes = bytes, .ownership = .owned };    }    /// Frees the bytes with `allocator` when they are owned, and leaves them when they are    /// borrowed. Code that holds the bytes calls it when it is done with them. The struct is    /// undefined afterward.    pub fn deinit(self: *CowBytes, allocator: Allocator) void {        if (self.ownership == .owned) allocator.free(self.bytes);        self.* = undefined;    }    /// Returns a copy that owns its bytes. Code that must keep the bytes after their owner goes    /// calls it. Borrowed bytes are copied into storage from `allocator`. Owned bytes come back as    /// the same struct with no copy, so the result and the original share them and only one of the    /// two may be freed. The call can fail only with `error.OutOfMemory`.    pub fn intoOwned(self: CowBytes, allocator: Allocator) !CowBytes {        return switch (self.ownership) {            .owned => self,            .borrowed => CowBytes.own(try allocator.dupe(u8, self.bytes)),        };    }};/// An integer with a tag that says whether its holder owns the integer's heap digits. Code that/// keeps an integer apart from its value holds it in this type, so the ownership of its digits/// travels with it. A new one borrows unless its builder says otherwise.pub const CowSignedInteger = struct {    /// The integer.    value: SignedInteger,    /// Whether the holder owns the digits of `value`. It defaults to borrowed, so a struct written    /// by hand never frees digits it did not allocate.    ownership: Ownership = .borrowed,    /// Wraps `value` as borrowed. Code that wraps an integer whose digits live elsewhere calls it.    /// `deinit` leaves its digits alone.    pub fn borrow(value: SignedInteger) CowSignedInteger {        return .{ .value = value, .ownership = .borrowed };    }    /// Wraps `value` as owned. Code that wraps an integer it built calls it. `deinit` frees its    /// digits with the allocator it is given, so they have to come from that allocator.    pub fn own(value: SignedInteger) CowSignedInteger {        return .{ .value = value, .ownership = .owned };    }    /// Frees the integer's heap digits with `allocator` when they are owned, and leaves them when    /// they are borrowed. Code that holds the integer calls it when it is done with it. Only an    /// integer too large for 128 bits has heap digits. The struct is undefined afterward.    pub fn deinit(self: *CowSignedInteger, allocator: Allocator) void {        if (self.ownership == .owned) self.value.deinit(allocator);        self.* = undefined;    }    /// Returns a copy that owns its digits. Code that must keep the integer after its owner goes    /// calls it. A borrowed integer is copied into storage from `allocator`. An owned integer comes    /// back as the same struct with no copy, so the result and the original share its digits and    /// only one of the two may be freed.    pub fn intoOwned(self: CowSignedInteger, allocator: Allocator) !CowSignedInteger {        return switch (self.ownership) {            .owned => self,            .borrowed => CowSignedInteger.own(try self.value.clone(allocator)),        };    }};/// A copy of one atom, tagged by its kind. Code that takes one atom out of a value holds it in this/// type, as `Value.asAtom` returns it. Booleans and doubles are stored directly. Strings, byte/// strings and symbols are stored as bytes with an ownership tag, and integers as an integer with/// one. `Value.asAtom` builds one that borrows from a value.pub const Atom = union(AtomClass) {    /// The boolean.    boolean: bool,    /// The double.    double: f64,    /// The integer, with whether its holder owns its digits.    signed_integer: CowSignedInteger,    /// The string's bytes, with whether its holder owns them.    string: CowBytes,    /// The byte string's bytes, with whether its holder owns them.    byte_string: CowBytes,    /// The symbol's name bytes, with whether its holder owns them.    symbol: CowBytes,    /// Returns the atom's kind. Code that switches on an atom's kind calls it.    pub fn class(self: Atom) AtomClass {        return @as(AtomClass, self);    }    /// Makes a boolean atom from `v`. `Value.asAtom` calls it for a boolean atom.    pub fn fromBool(v: bool) Atom {        return .{ .boolean = v };    }    /// Makes a double atom from `v`. `Value.asAtom` calls it for a double atom.    pub fn fromDouble(v: f64) Atom {        return .{ .double = v };    }    /// Makes an integer atom that borrows `v`'s digits. `Value.asAtom` calls it for an integer atom    /// over the value's own digits.    pub fn fromSignedIntegerBorrowed(v: SignedInteger) Atom {        return .{ .signed_integer = CowSignedInteger.borrow(v) };    }    /// Makes an integer atom that owns `v`'s digits, so its `deinit` frees them. Code that hands an    /// integer's digits to the atom calls it.    pub fn fromSignedIntegerOwned(v: SignedInteger) Atom {        return .{ .signed_integer = CowSignedInteger.own(v) };    }    /// Makes a string atom that borrows `bytes`. `Value.asAtom` calls it for a string atom over the    /// value's own bytes.    pub fn fromStringBorrowed(bytes: []const u8) Atom {        return .{ .string = CowBytes.borrow(bytes) };    }    /// Makes a string atom that owns `bytes`, so its `deinit` frees them. Code that hands a    /// string's bytes to the atom calls it.    pub fn fromStringOwned(bytes: []const u8) Atom {        return .{ .string = CowBytes.own(bytes) };    }    /// Makes a byte-string atom that borrows `bytes`. `Value.asAtom` calls it for a byte-string    /// atom over the value's own bytes.    pub fn fromByteStringBorrowed(bytes: []const u8) Atom {        return .{ .byte_string = CowBytes.borrow(bytes) };    }    /// Makes a byte-string atom that owns `bytes`, so its `deinit` frees them. Code that hands a    /// byte string's bytes to the atom calls it.    pub fn fromByteStringOwned(bytes: []const u8) Atom {        return .{ .byte_string = CowBytes.own(bytes) };    }    /// Makes a symbol atom that borrows `bytes`. `Value.asAtom` calls it for a symbol atom over the    /// value's own bytes.    pub fn fromSymbolBorrowed(bytes: []const u8) Atom {        return .{ .symbol = CowBytes.borrow(bytes) };    }    /// Makes a symbol atom that owns `bytes`, so its `deinit` frees them. Code that hands a    /// symbol's bytes to the atom calls it.    pub fn fromSymbolOwned(bytes: []const u8) Atom {        return .{ .symbol = CowBytes.own(bytes) };    }    /// Frees the atom's bytes or digits with `allocator` when it owns them. Code that holds the    /// atom calls it when it is done with it. Booleans and doubles need nothing. The atom is    /// undefined afterward.    pub fn deinit(self: *Atom, allocator: Allocator) void {        switch (self.*) {            .boolean, .double => {},            .signed_integer => |*c| c.deinit(allocator),            .string => |*c| c.deinit(allocator),            .byte_string => |*c| c.deinit(allocator),            .symbol => |*c| c.deinit(allocator),        }        self.* = undefined;    }    /// Returns a copy of the atom that owns its bytes or digits, copying borrowed ones into storage    /// from `allocator`. Code that keeps an atom calls it so the atom outlives the value it came    /// from. Parts that are already owned come back with no copy, so the result shares them with    /// the original and only one of the two may be freed.    pub fn intoOwned(self: Atom, allocator: Allocator) !Atom {        return switch (self) {            .boolean, .double => self,            .signed_integer => |c| .{ .signed_integer = try c.intoOwned(allocator) },            .string => |c| .{ .string = try c.intoOwned(allocator) },            .byte_string => |c| .{ .byte_string = try c.intoOwned(allocator) },            .symbol => |c| .{ .symbol = try c.intoOwned(allocator) },        };    }    /// Returns whether two atoms have the same kind and the same contents, whatever their ownership    /// tags. Code that compares atoms from different owners calls it, so ownership plays no part.    /// Doubles are equal when their bit patterns are equal, so a `NaN` equals a `NaN` with the same    /// bits and `0.0` differs from `-0.0`. Integers compare with `SignedInteger.eql`.    pub fn eql(a: Atom, b: Atom) bool {        if (a.class() != b.class()) return false;        return switch (a) {            .boolean => |v| v == b.boolean,            .double => |v| @as(u64, @bitCast(v)) == @as(u64, @bitCast(b.double)),            .signed_integer => |c| c.value.eql(b.signed_integer.value),            .string => |c| std.mem.eql(u8, c.bytes, b.string.bytes),            .byte_string => |c| std.mem.eql(u8, c.bytes, b.byte_string.bytes),            .symbol => |c| std.mem.eql(u8, c.bytes, b.symbol.bytes),        };    }};test "Atom borrowed slice does not free on deinit" {    var bytes = [_]u8{ 'h', 'i' };    var atom = Atom.fromStringBorrowed(&bytes);    atom.deinit(std.testing.allocator);}test "Atom owned slice frees on deinit" {    const allocator = std.testing.allocator;    const buf = try allocator.dupe(u8, "hello");    var atom = Atom.fromStringOwned(buf);    atom.deinit(allocator);}test "Atom intoOwned promotes borrowed payload" {    const allocator = std.testing.allocator;    const literal = "abc";    const borrowed = Atom.fromSymbolBorrowed(literal);    var owned = try borrowed.intoOwned(allocator);    defer owned.deinit(allocator);    try std.testing.expectEqual(Ownership.owned, owned.symbol.ownership);    try std.testing.expect(std.mem.eql(u8, owned.symbol.bytes, literal));}test "Atom eql compares across ownership tags" {    var buf = [_]u8{'x'};    const borrowed = Atom.fromStringBorrowed(&buf);    const allocator = std.testing.allocator;    const owned_bytes = try allocator.dupe(u8, "x");    var owned = Atom.fromStringOwned(owned_bytes);    defer owned.deinit(allocator);    try std.testing.expect(Atom.eql(borrowed, owned));}

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

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

Audit

Definitions1
Public names1
Members0
Version26.7.0
Revisiondaab053ee433