Skip to documentation
SLOP

tiny.reticulum.packet.hashlist

Reference tiny.reticulum packet hashlist

Defined in packet.

API (16)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

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

Source

Called byCallsNo direct callsnode.Nodeactivatenode.transport.Stateactivatetest sourcelib.reticulum.src.packet.hashlist.test_Reticu...py:2515-2516 removes a hash from the ...test sourcelib.reticulum.src.packet.hashlist.test_Reticu...py:803-805 rotates bounded hash gener...test sourcelib.reticulum.src.packet.hashlisttest: hashlist admits maximum and evi...packet.hashlist.Tableactivate
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callspacket.hashlist.Tableinserttest sourcelib.reticulum.src.packet.hashlist.test_Reticu...py:2515-2516 removes a hash from the ...test sourcelib.reticulum.src.packet.hashlist.test_Reticu...py:803-805 rotates bounded hash gener...test sourcelib.reticulum.src.packet.hashlisttest: hashlist admits maximum and evi...packet.hashlist.Tablecontains
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest sourcelib.reticulum.src.packet.hashlist.test_Reticu...py:2515-2516 removes a hash from the ...test sourcelib.reticulum.src.packet.hashlist.test_Reticu...py:803-805 rotates bounded hash gener...test sourcelib.reticulum.src.packet.hashlisttest: hashlist admits maximum and evi...packet.hashlist.Tablecount
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsnode.Nodedeinitnode.transport.Statedeinittest sourcelib.reticulum.src.packet.hashlist.test_Reticu...py:2515-2516 removes a hash from the ...test sourcelib.reticulum.src.packet.hashlist.test_Reticu...py:803-805 rotates bounded hash gener...test sourcelib.reticulum.src.packet.hashlisttest: hashlist admits maximum and evi...packet.hashlist.Tabledeinit
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsprivate sourcelib.reticulum.src.node.ownerinitSubownersnode.transport.Stateinittest sourcelib.reticulum.src.packet.hashlist.test_Reticu...py:2515-2516 removes a hash from the ...test sourcelib.reticulum.src.packet.hashlist.test_Reticu...py:803-805 rotates bounded hash gener...test sourcelib.reticulum.src.packet.hashlisttest: hashlist admits maximum and evi...packet.hashlist.Tableinit
Static calls · unresolved targets: 1 · external targets: 0.
Called byCallstest sourcelib.reticulum.src.packet.hashlist.test_Reticu...py:2515-2516 removes a hash from the ...test sourcelib.reticulum.src.packet.hashlist.test_Reticu...py:803-805 rotates bounded hash gener...test sourcelib.reticulum.src.packet.hashlisttest: hashlist admits maximum and evi...packet.hashlist.Tablecontainspacket.hashlist.Tableinsert
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest sourcelib.reticulum.src.packet.hashlist.test_Reticu...py:2515-2516 removes a hash from the ...packet.hashlist.TableremoveCurrent
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest sourcelib.reticulum.src.packet.hashlist.test_Reticu...py:2515-2516 removes a hash from the ...test sourcelib.reticulum.src.packet.hashlist.test_Reticu...py:803-805 rotates bounded hash gener...packet.hashlist.Tablerotate
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/reticulum/src/packet/hashlist.zig

zig
const std = @import("std");const alloc_phase = @import("alloc_phase");const packet = @import("root.zig");const TableLimits = struct {    hashes_max: usize,};const TableCapacity = struct {    hashes_max: usize,    storage_bytes: usize,    pub const DeriveError = error{ InvalidLimit, CapacityOverflow };    pub fn derive(limits: TableLimits) DeriveError!TableCapacity {        if (limits.hashes_max == 0) return error.InvalidLimit;        const hashes_max = limits.hashes_max;        const storage_bytes = alloc_phase.capacity.mul(            usize,            hashes_max,            @sizeOf(packet.Hash),        ) catch return error.CapacityOverflow;        return .{ .hashes_max = hashes_max, .storage_bytes = storage_bytes };    }};pub const Table = struct {    phase: alloc_phase.capacity.Phase,    capacity: Capacity,    storage: Storage,    hashes: []packet.Hash,    start: usize = 0,    len: usize = 0,    generation_len: usize = 0,    pub const storage_alignment: usize = 8;    pub const Storage = []align(storage_alignment) u8;    pub const Limits: type = TableLimits;    pub const Capacity: type = TableCapacity;    pub const InitError = Capacity.DeriveError || error{StorageLengthMismatch};    pub const work_limits: alloc_phase.capacity.WorkLimits = .{        .transition_steps_max = 1_000_001,        .cleanup_steps_per_call_max = 0,        .cleanup_calls_at_capacity_max = 0,    };    pub const claim: alloc_phase.capacity.Declaration = .{        .source = .{            .id = "reticulum.hashlist",            .kind = .phase_static,            .limit_source = .caller,            .storage = .{                .covered = &.{.{                    .id = "caller_packet_hash_fifo",                    .lifetime = .transferred,                    .detail = "caller storage for a bounded FIFO of packet hashes",                }},                .excluded = &.{                    "borrowed lookup hashes",                    "encoded packets and wire hash computation",                },            },            .capacity = .{                .inputs = &.{alloc_phase.capacity.bindInput(                    Limits,                    "hashes_max",                    "hashes_max",                )},                .type_selectors = &.{alloc_phase.capacity.bindType(packet.Hash, "hash")},                .nodes = &.{                    .{ .input = 0 },                    .{ .scale = .{                        .node = 0,                        .coefficient = .{ .size_of_concrete_type = 0 },                    } },                },                .assertions = &.{.{                    .scope = .closure_total,                    .measure = .retained,                    .relation = .exact,                    .expression = 1,                }},            },            .overload = .{                .kind = .not_applicable,                .detail = "a new hash replaces the oldest hash when the FIFO is full",            },            .risks = .{                .transitive = .{                    .status = .excluded,                    .detail = "hash lookup and insertion call no allocating owner",                },                .foreign = .{                    .status = .excluded,                    .detail = "the hash FIFO crosses no foreign boundary",                },            },            .work = .{ .equation = "lookup scans at most hashes_max entries" },            .obligations = &.{                .{ .key = "reticulum_hashlist_capacity", .role = .capacity_model },                .{ .key = "reticulum_hashlist_replace", .role = .overload },                .{ .key = "reticulum_hashlist_work", .role = .work_bound },            },        },        .bindings = .{            .owner = @This(),            .seal = .{                .family = alloc_phase.capacity.selector(@This().activate),                .premise = .{ .class = .checked_semantic_fact, .authority = .checker },            },            .teardown = .{                .family = alloc_phase.capacity.selector(@This().deinit),                .premise = .{ .class = .checked_semantic_fact, .authority = .checker },            },        },    };    pub fn init(storage: Storage, limits: Limits) InitError!Table {        const capacity = try Capacity.derive(limits);        if (storage.len != capacity.storage_bytes) return error.StorageLengthMismatch;        return .{            .phase = .initialization,            .capacity = capacity,            .storage = storage,            .hashes = std.mem.bytesAsSlice(packet.Hash, storage),        };    }    pub fn activate(self: *Table) void {        std.debug.assert(self.phase == .initialization);        std.debug.assert(self.len == 0);        self.phase = .steady;    }    pub fn contains(self: *const Table, hash: packet.Hash) bool {        std.debug.assert(self.phase == .steady);        for (0..self.len) |offset| {            const index = (self.start + offset) % self.capacity.hashes_max;            if (std.mem.eql(u8, &self.hashes[index], &hash)) return true;        }        return false;    }    /// Adds the 32-byte SHA-256 digest that names a packet (*packet hash*) so    /// the caller recognizes a later copy of the same packet, and returns the    /// hash it pushed out, or null when it pushed none out. A hash the store    /// already holds is left where it is and null comes back. Once the store is    /// full the oldest hash leaves and the new one takes its slot, so the store    /// keeps the order the hashes arrived in, following Reticulum@1.5.0    /// RNS/Transport.py:167-168,803-805.    pub fn insert(self: *Table, hash: packet.Hash) ?packet.Hash {        std.debug.assert(self.phase == .steady);        std.debug.assert(self.len <= self.capacity.hashes_max);        if (self.contains(hash)) return null;        if (self.len < self.capacity.hashes_max) {            const index = (self.start + self.len) % self.capacity.hashes_max;            self.hashes[index] = hash;            self.len += 1;            self.generation_len += 1;            return null;        }        const evicted = self.hashes[self.start];        self.hashes[self.start] = hash;        self.start = (self.start + 1) % self.capacity.hashes_max;        if (self.generation_len < self.capacity.hashes_max) self.generation_len += 1;        return evicted;    }    /// Drops the hashes stored before the last rotation and keeps the ones    /// added since so the store keeps recent hashes and lets go of old ones on    /// a timer, following Reticulum@1.5.0 RNS/Transport.py:803-805. The call    /// returns with the store untouched until more than half its slots hold    /// hashes added since the last rotation.    pub fn rotate(self: *Table) void {        std.debug.assert(self.phase == .steady);        const threshold = self.capacity.hashes_max / 2;        if (self.generation_len <= threshold) return;        std.debug.assert(self.generation_len <= self.len);        const discarded = self.len - self.generation_len;        self.start = (self.start + discarded) % self.capacity.hashes_max;        self.len = self.generation_len;        self.generation_len = 0;    }    /// Removes a hash added since the last rotation and returns whether it was    /// there. A copy of that hash from before the last rotation stays. The    /// reference does this for a data packet of an encrypted session between    /// two endpoints (*link*) that arrived over a network interface (*carrier*)    /// the link does not run on, so the packet can be acted on again, following    /// Reticulum@1.5.0 RNS/Transport.py:2515-2516.    pub fn removeCurrent(self: *Table, hash: packet.Hash) bool {        std.debug.assert(self.phase == .steady);        std.debug.assert(self.generation_len <= self.len);        const first = self.len - self.generation_len;        for (first..self.len) |offset| {            const index = (self.start + offset) % self.capacity.hashes_max;            if (!std.mem.eql(u8, &self.hashes[index], &hash)) continue;            for (offset + 1..self.len) |later| {                const target = (self.start + later - 1) % self.capacity.hashes_max;                const source = (self.start + later) % self.capacity.hashes_max;                self.hashes[target] = self.hashes[source];            }            self.len -= 1;            self.generation_len -= 1;            std.debug.assert(self.generation_len <= self.len);            return true;        }        return false;    }    pub fn count(self: *const Table) usize {        std.debug.assert(self.phase == .steady);        return self.len;    }    pub fn deinit(self: *Table) Storage {        std.debug.assert(self.phase == .steady);        self.phase = .teardown;        const storage = self.storage;        self.* = undefined;        return storage;    }};comptime {    alloc_phase.capacity.requireProvisionedExactOwnerShape(Table);}test "hashlist admits maximum and evicts oldest at maximum plus one" {    comptime {        @stardustClaim(alloc_phase.capacity.witness(            Table,            "reticulum_hashlist_capacity",        ), null, null, null, null, null, null);        @stardustClaim(alloc_phase.capacity.witness(            Table,            "reticulum_hashlist_replace",        ), null, null, null, null, null, null);        @stardustClaim(alloc_phase.capacity.witness(            Table,            "reticulum_hashlist_work",        ), null, null, null, null, null, null);    }    const capacity = comptime TableCapacity.derive(.{ .hashes_max = 3 }) catch unreachable;    var bytes: [capacity.storage_bytes]u8 align(Table.storage_alignment) = undefined;    var table = try Table.init(&bytes, .{ .hashes_max = 3 });    table.activate();    defer _ = table.deinit();    for (1..4) |value| {        try std.testing.expect(table.insert(@splat(@as(u8, @intCast(value)))) == null);    }    try std.testing.expectEqual(@as(usize, 3), table.count());    try std.testing.expectEqual(@as(packet.Hash, @splat(1)), table.insert(@splat(4)).?);    try std.testing.expect(!table.contains(@splat(1)));    try std.testing.expect(table.contains(@splat(4)));}test "Reticulum@1.5.0 RNS/Transport.py:803-805 rotates bounded hash generations" {    const capacity = comptime TableCapacity.derive(.{ .hashes_max = 4 }) catch unreachable;    var bytes: [capacity.storage_bytes]u8 align(Table.storage_alignment) = undefined;    var table = try Table.init(&bytes, .{ .hashes_max = 4 });    table.activate();    defer _ = table.deinit();    for (1..4) |value| _ = table.insert(@splat(@as(u8, @intCast(value))));    table.rotate();    for (4..7) |value| _ = table.insert(@splat(@as(u8, @intCast(value))));    table.rotate();    try std.testing.expectEqual(@as(usize, 3), table.count());    try std.testing.expect(!table.contains(@splat(1)));    try std.testing.expect(table.contains(@splat(4)));    try std.testing.expect(table.contains(@splat(6)));}test "Reticulum@1.5.0 RNS/Transport.py:2515-2516 removes a hash from the current generation" {    const capacity = comptime TableCapacity.derive(.{ .hashes_max = 4 }) catch unreachable;    var bytes: [capacity.storage_bytes]u8 align(Table.storage_alignment) = undefined;    var table = try Table.init(&bytes, .{ .hashes_max = 4 });    table.activate();    defer _ = table.deinit();    for (1..4) |value| _ = table.insert(@splat(@as(u8, @intCast(value))));    table.rotate();    _ = table.insert(@splat(4));    try std.testing.expect(!table.removeCurrent(@splat(2)));    try std.testing.expect(table.contains(@splat(2)));    try std.testing.expect(table.removeCurrent(@splat(4)));    try std.testing.expect(!table.contains(@splat(4)));    _ = table.insert(@splat(5));    try std.testing.expectEqual(@as(packet.Hash, @splat(1)), table.insert(@splat(6)).?);    try std.testing.expect(table.removeCurrent(@splat(5)));    try std.testing.expectEqual(@as(usize, 3), table.count());    try std.testing.expect(table.contains(@splat(2)));    try std.testing.expect(table.contains(@splat(3)));    try std.testing.expect(table.contains(@splat(6)));    try std.testing.expect(!table.contains(@splat(5)));}

Source: lib/reticulum/src/packet/root.zig:48

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

Audit

Definitions17
Public names17
Members7
Version26.7.0
Revisiondaab053ee433