tiny.preserves.SignedInteger
Defined in integer_mod.
A signed integer of any size, stored in the smallest of three representations that holds it.
API (19)
Actions
Public operations.
clone: Returns a copy that owns its storage, copying the heap bytes of a large number withallocator.deinit: Frees the heap bytes of a number too large for 128 bits, and does nothing for any other number.eql: Returns whether two integers are equal.fromCanonicalBytes: Reads a number from its shortest big-endian two's-complement bytes.fromI128: Returnsv, stored in 128 signed bits.fromU128: Returnsv, stored in 128 signed bits when it fits there and in 128 unsigned bits otherwise.isCanonicalBytes: Returns whetherbytesare the shortest big-endian two's-complement spelling of their number.isNegative: Returns whether the number is below zero.isZero: Returns whether the number is zero.order: Returns the numeric order of two integers.toCanonicalBytes: Returns a new allocation fromallocatorholding the number's shortest big-endian two's-complement bytes, which the caller frees.toI128: Returns the number when it is stored in 128 signed bits, anderror.OutOfRangeotherwise.toI64: Returns the number when it fits in 64 signed bits, anderror.OutOfRangeotherwise.toI64Lossy: Returns the number clamped to the 64-bit signed range: the largesti64for any number above it and the smallest for any number below.toU128: Returns the number when it is zero or positive and fits in 128 unsigned bits, anderror.OutOfRangeotherwise.toU64: Returns the number when it is zero or positive and fits in 64 unsigned bits, anderror.OutOfRangeotherwise.zero: Returns zero, stored in 128 signed bits.
Types and contracts
Public types and contracts.
Repr: The number stored as 128 signed bits, 128 unsigned bits or heap bytes, tagged by which.
Fields and members
Public fields and members.
Source
Source: lib/preserves/src/integer.zig:47
zig
/// A signed integer of any size, stored in the smallest of three representations that holds it./// Every integer in a value is one of these, so every reader, writer and comparison of integers/// uses it. A value built by `fromCanonicalBytes` or `clone` owns its heap bytes, and `deinit`/// frees them. Equality and order assume the smallest representation, so a value written directly/// through `repr` has to keep it.pub const SignedInteger = struct { /// The number in one of its three representations. A value set here directly has to use the /// smallest representation that holds the number, because nothing checks it. repr: Repr, /// The number stored as 128 signed bits, 128 unsigned bits or heap bytes, tagged by which. Code /// that switches on how an integer is stored reads this. pub const Repr = union(Tier) { /// The number when it fits in 128 signed bits. i128: i128, /// The number when it is above the largest 128-bit signed integer and fits in 128 unsigned /// bits. u128: u128, /// The number's shortest big-endian two's-complement bytes, for a number outside both /// 128-bit ranges. These bytes are at least 17 long. big: []const u8, }; /// Returns zero, stored in 128 signed bits. Code that needs a zero integer calls it. pub fn zero() SignedInteger { return .{ .repr = .{ .i128 = 0 } }; } /// Returns `v`, stored in 128 signed bits. Readers and constructors call it for any integer /// that fits in 128 signed bits. pub fn fromI128(v: i128) SignedInteger { return .{ .repr = .{ .i128 = v } }; } /// Returns `v`, stored in 128 signed bits when it fits there and in 128 unsigned bits /// otherwise. Readers call it for a non-negative integer read as 128 unsigned bits. pub fn fromU128(v: u128) SignedInteger { const limit: u128 = @intCast(std.math.maxInt(i128)); if (v <= limit) { return .{ .repr = .{ .i128 = @intCast(v) } }; } return .{ .repr = .{ .u128 = v } }; } /// Reads a number from its shortest big-endian two's-complement bytes. The binary and text /// readers call it for integers too large for fixed-width parsing. Empty bytes are zero, up to /// 16 bytes give a 128-bit signed number, and 17 bytes of a number that fits 128 unsigned bits /// give a 128-bit unsigned number. Numbers with more bytes keep a copy of the bytes from /// `allocator`, which `deinit` frees. The call returns `error.NonCanonicalInteger` for any /// spelling longer than the shortest. pub fn fromCanonicalBytes(allocator: Allocator, bytes: []const u8) !SignedInteger { if (!isCanonicalBytes(bytes)) return DecodeError.NonCanonicalInteger; if (bytes.len == 0) return zero(); if (bytes.len <= 16) return fromI128(decodeI128Bytes(bytes)); if (bytes.len == 17 and bytes[0] == 0x00 and (bytes[1] & 0x80) != 0) { return .{ .repr = .{ .u128 = decode16BytesAsU128(bytes[1..]) } }; } return .{ .repr = .{ .big = try allocator.dupe(u8, bytes) } }; } /// Frees the heap bytes of a number too large for 128 bits, and does nothing for any other /// number. `Value.deinit` and `freeValueDeep` call it for every integer they free. The value is /// undefined afterward. pub fn deinit(self: *SignedInteger, allocator: Allocator) void { switch (self.repr) { .big => |b| if (b.len != 0) allocator.free(b), else => {}, } self.* = undefined; } /// Returns a copy that owns its storage, copying the heap bytes of a large number with /// `allocator`. `cloneValueDeep` calls it for every integer it copies. A number that fits in /// 128 bits is copied without allocating. pub fn clone(self: SignedInteger, allocator: Allocator) !SignedInteger { return switch (self.repr) { .i128 => |v| fromI128(v), .u128 => |v| .{ .repr = .{ .u128 = v } }, .big => |b| .{ .repr = .{ .big = try allocator.dupe(u8, b) } }, }; } /// Returns a new allocation from `allocator` holding the number's shortest big-endian /// two's-complement bytes, which the caller frees. The binary writer calls it for the bytes of /// each integer it writes. Zero gives an empty slice. pub fn toCanonicalBytes(self: SignedInteger, allocator: Allocator) ![]u8 { return switch (self.repr) { .i128 => |v| try encodeI128(allocator, v), .u128 => |v| try encodeU128(allocator, v), .big => |b| try allocator.dupe(u8, b), }; } /// Returns the number when it is stored in 128 signed bits, and `error.OutOfRange` otherwise. /// Code that needs the number as a Zig `i128` calls it, as the package's tests do. pub fn toI128(self: SignedInteger) RangeError!i128 { return switch (self.repr) { .i128 => |v| v, .u128, .big => RangeError.OutOfRange, }; } /// Returns the number when it is zero or positive and fits in 128 unsigned bits, and /// `error.OutOfRange` otherwise. Code that needs a 128-bit unsigned integer calls it. pub fn toU128(self: SignedInteger) RangeError!u128 { return switch (self.repr) { .i128 => |v| if (v < 0) RangeError.OutOfRange else @intCast(v), .u128 => |v| v, .big => RangeError.OutOfRange, }; } /// Returns the number when it fits in 64 signed bits, and `error.OutOfRange` otherwise. Code /// that reads a field it expects to fit in 64 bits calls it for an `i64`. pub fn toI64(self: SignedInteger) RangeError!i64 { const v = try self.toI128(); if (v < std.math.minInt(i64) or v > std.math.maxInt(i64)) return RangeError.OutOfRange; return @intCast(v); } /// Returns the number clamped to the 64-bit signed range: the largest `i64` for any number /// above it and the smallest for any number below. The JSON writer calls it to print an integer /// too large for 128 bits. The call never fails. pub fn toI64Lossy(self: SignedInteger) i64 { return switch (self.repr) { .i128 => |v| blk: { if (v > std.math.maxInt(i64)) break :blk std.math.maxInt(i64); if (v < std.math.minInt(i64)) break :blk std.math.minInt(i64); break :blk @intCast(v); }, .u128 => std.math.maxInt(i64), .big => |b| if (b.len > 0 and (b[0] & 0x80) != 0) std.math.minInt(i64) else std.math.maxInt(i64), }; } /// Returns the number when it is zero or positive and fits in 64 unsigned bits, and /// `error.OutOfRange` otherwise. Code that reads a count or a size calls it for a `u64`. pub fn toU64(self: SignedInteger) RangeError!u64 { const v = try self.toU128(); if (v > std.math.maxInt(u64)) return RangeError.OutOfRange; return @intCast(v); } /// Returns whether the number is zero. Code that tests for zero calls it. Only a zero stored in /// 128 signed bits counts, so a zero written directly into another representation reads as /// nonzero. pub fn isZero(self: SignedInteger) bool { return switch (self.repr) { .i128 => |v| v == 0, .u128, .big => false, }; } /// Returns whether the number is below zero. Code that needs the sign calls it. pub fn isNegative(self: SignedInteger) bool { return switch (self.repr) { .i128 => |v| v < 0, .u128 => false, .big => |b| b.len > 0 and (b[0] & 0x80) != 0, }; } /// Returns whether two integers are equal. `Value.eql` calls it for two integers. Integers in /// different representations are never equal, so the call is exact only when both use the /// smallest representation that holds their number. pub fn eql(a: SignedInteger, b: SignedInteger) bool { return switch (a.repr) { .i128 => |av| switch (b.repr) { .i128 => |bv| av == bv, else => false, }, .u128 => |av| switch (b.repr) { .u128 => |bv| av == bv, else => false, }, .big => |av| switch (b.repr) { .big => |bv| std.mem.eql(u8, av, bv), else => false, }, }; } /// Returns the numeric order of two integers. `Value.compare` calls it for two integers. Large /// numbers compare by sign, then by byte length, then byte by byte. The order across /// representations is exact only when both integers use the smallest representation that holds /// their number. pub fn order(a: SignedInteger, b: SignedInteger) std.math.Order { return switch (a.repr) { .i128 => |av| switch (b.repr) { .i128 => |bv| std.math.order(av, bv), .u128 => .lt, .big => |bv| if (bigIsNegative(bv)) .gt else .lt, }, .u128 => |av| switch (b.repr) { .i128 => .gt, .u128 => |bv| std.math.order(av, bv), .big => |bv| if (bigIsNegative(bv)) .gt else .lt, }, .big => |av| switch (b.repr) { .i128, .u128 => if (bigIsNegative(av)) .lt else .gt, .big => |bv| compareBig(av, bv), }, }; } /// Returns whether `bytes` are the shortest big-endian two's-complement spelling of their /// number. The binary reader calls it before reading an integer, so a longer spelling is /// rejected. Empty bytes spell zero. A lone `0x00` byte, a leading `0x00` before a byte whose /// top bit is clear, and a leading `0xff` before a byte whose top bit is set are all longer /// than needed. pub fn isCanonicalBytes(bytes: []const u8) bool { if (bytes.len == 0) return true; if (bytes.len == 1) return bytes[0] != 0x00; const first = bytes[0]; const second = bytes[1]; if (first == 0x00 and (second & 0x80) == 0) return false; if (first == 0xff and (second & 0x80) != 0) return false; return true; }};Source: lib/preserves/src/root.zig:128
zig
pub const SignedInteger = integer_mod.SignedInteger;Also reachable as
atom.SignedInteger, json.SignedInteger, value.SignedInteger.
Complete caller list for SignedInteger.fromI128
10 direct callers.
tiny.preserves.SignedInteger.clone[method] atlib/preserves/src/integer.zig:116tiny.preserves.SignedInteger.fromCanonicalBytes[function] atlib/preserves/src/integer.zig:92lib.preserves.src.integer.test_SignedInteger.toI64Lossy_saturates_out-of-range_tiers[function] — test source atlib/preserves/src/integer.zig:397in nearest public ownertiny.preserves.integer_modlib.preserves.src.integer.test_SignedInteger_cross-tier_ordering[function] — test source atlib/preserves/src/integer.zig:388in nearest public ownertiny.preserves.integer_modlib.preserves.src.integer.test_SignedInteger_round-trip_I128_fast_path[function] — test source atlib/preserves/src/integer.zig:337in nearest public ownertiny.preserves.integer_modlib.preserves.src.json.jsonToValue[function] — private source atlib/preserves/src/json.zig:317in nearest public ownertiny.preserves.jsonlib.preserves.src.json.test_toJsonString:_primitives[function] — test source atlib/preserves/src/json.zig:500in nearest public ownertiny.preserves.jsonlib.preserves.src.json.test_toJsonString:_unknown_symbol-label_record_uses___record___envelope[function] — test source atlib/preserves/src/json.zig:618in nearest public ownertiny.preserves.jsonlib.preserves.src.json.test_toJsonString_+_fromJsonString:_sequence_round-trip[function] — test source atlib/preserves/src/json.zig:714in nearest public ownertiny.preserves.jsontiny.preserves.value.Value[function] atlib/preserves/src/value.zig:116
Audit
| Definitions | 19 |
|---|---|
| Public names | 95 |
| Members | 4 |
| Version | 26.7.0 |
| Revision | daab053ee433 |