Skip to documentation
SLOP

tiny.quic.sim

Reference tiny.quic sim

Defined in tiny.quic.

API (36)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

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

Source

Source: lib/quic/src/sim/clock.zig:6

zig
/// A nanosecond counter a test owns and moves forward itself. A test holds a manual clock and/// passes its reading wherever a connection or a link asks for the current time. The clock starts/// at zero. A test alone moves the clock, so the test decides the exact instant of every step.pub const Clock = struct {    now_ns: u64 = 0,    /// Moves the clock forward by a given number of nanoseconds so a test puts a chosen span of    /// time between steps. At the largest representable instant the clock stops there, so the    /// reading never goes backward.    pub fn advance(self: *Clock, delta_ns: u64) void {        const previous = self.now_ns;        self.now_ns = std.math.add(u64, self.now_ns, delta_ns) catch            std.math.maxInt(u64);        std.debug.assert(self.now_ns >= previous);        if (self.now_ns != std.math.maxInt(u64)) {            std.debug.assert(self.now_ns - previous == delta_ns);        }    }};

Source: lib/quic/src/sim/link.zig:36

zig
/// The exact storage layout and byte total derived from one set of limits, so a caller learns the/// exact byte count its storage must have. The total covers queue records and payload bytes for/// both directions. Limits whose product overflows give `CapacityOverflow`.pub const Capacity = struct {    queue_capacity: u32,    datagram_capacity: u16,    entry_count: usize,    entry_bytes: usize,    direction_payload_bytes: usize,    payload_bytes: usize,    storage_bytes: usize,    pub const DeriveError: type = CapacityError;    pub fn derive(limits: Limits) DeriveError!Capacity {        const queue_count: usize = @intCast(limits.queue_capacity);        const datagram_bytes: usize = @intCast(limits.datagram_capacity);        const entry_count = try alloc_phase.capacity.mul(usize, queue_count, 2);        const entry_bytes = try alloc_phase.capacity.mul(            usize,            entry_count,            @sizeOf(Entry),        );        const direction_payload_bytes = try alloc_phase.capacity.mul(            usize,            queue_count,            datagram_bytes,        );        const payload_bytes = try alloc_phase.capacity.mul(            usize,            direction_payload_bytes,            2,        );        const storage_bytes = try alloc_phase.capacity.add(            usize,            entry_bytes,            payload_bytes,        );        return .{            .queue_capacity = limits.queue_capacity,            .datagram_capacity = limits.datagram_capacity,            .entry_count = entry_count,            .entry_bytes = entry_bytes,            .direction_payload_bytes = direction_payload_bytes,            .payload_bytes = payload_bytes,            .storage_bytes = storage_bytes,        };    }};

Source: lib/quic/src/sim/link.zig:24

zig
pub const CapacityError = error{CapacityOverflow};

Source: lib/quic/src/sim/link.zig:11

zig
/// One of the link's two endpoints, `a` and `b`, so every call that sends, receives, or reads/// counters names the endpoint it speaks for. Naming an endpoint picks the queue: a send goes into/// the queue that endpoint owns, and a receive takes from the other one.pub const End = enum(u1) {    a,    b,};

Source: lib/quic/src/sim/link.zig:19

zig
/// The two capacities a caller chooses to set how much traffic the link must hold and derive its/// storage size: the datagrams one direction may hold at once, and the bytes one datagram may/// carry. Both directions get the same capacities.pub const Limits = struct {    queue_capacity: u32,    datagram_capacity: u16,};

Source: lib/quic/src/sim/link.zig:343

zig
/// A two-ended path that carries opaque datagrams between its endpoints over caller-owned memory,/// on a schedule the caller can reproduce, so a test holds one and drives both endpoints of a/// simulated path through it. The link holds one policy per direction, a generator started from the/// caller's seed, the counters, the send order, and the pending drop patterns. The same seed and/// the same sequence of calls give the same outcome every run, so a failing test can be replayed./// The bytes the link carries are opaque to it, so QUIC packets remain outside its view.pub const Link = struct {    memory: *Memory,    policies: [2]Policy,    prng: std.Random.DefaultPrng,    counts: [2]Stats = .{ .{}, .{} },    next_sequence: [2]u64 = .{ 0, 0 },    drops: [2]u64 = .{ 0, 0 },    pub const InitError: type = LinkInitError;    pub const SendError: type = LinkSendError;    /// Starts a link once over memory the caller has already built, with one policy for each    /// direction and a seed for the delays and the chances, so the caller receives the value it    /// drives. The call moves the memory from its initialization phase into its steady phase, after    /// which the partition is fixed. Memory already past initialization gives `StorageNotReady`.    /// Limits that differ from the ones the memory was built with give `LimitsMismatch`. Each    /// policy is checked against the datagram capacity before anything is sealed, so an invalid    /// policy fails here.    pub fn init(        limits: Limits,        storage: *Memory,        seed: u64,        policy_a_to_b: Policy,        policy_b_to_a: Policy,    ) InitError!Link {        if (storage.phase != .initialization) return error.StorageNotReady;        if (storage.capacity.queue_capacity != limits.queue_capacity) {            return error.LimitsMismatch;        }        if (storage.capacity.datagram_capacity != limits.datagram_capacity) {            return error.LimitsMismatch;        }        try policy_a_to_b.validate(limits.datagram_capacity);        try policy_b_to_a.validate(limits.datagram_capacity);        storage.activate();        return .{            .memory = storage,            .policies = .{ policy_a_to_b, policy_b_to_a },            .prng = std.Random.DefaultPrng.init(seed),        };    }    /// Marks which of the next datagrams one endpoint sends are thrown away, by bit position,    /// starting from bit 0 and covering the next 64, so a test loses exactly the datagrams it    /// chooses at the moment it chooses. Only datagrams that pass the MTU check are counted against    /// the pattern, because the check comes first. A datagram dropped this way counts as sent and    /// lost. A marked datagram draws nothing from the seeded generator, so marking a drop leaves    /// the rest of the run unchanged. A subsequent call joins the new pattern with the bits still    /// pending, and both are counted from the next datagram.    pub fn dropNext(self: *Link, from: End, pattern: u64) void {        const direction_index = outgoingIndex(from);        std.debug.assert(direction_index < self.drops.len);        self.drops[direction_index] |= pattern;        std.debug.assert(self.drops[direction_index] & pattern == pattern);    }    /// Takes one datagram from an endpoint at a given instant and applies that direction's policy    /// to it, so the link decides what becomes of the datagram. A datagram above the MTU is refused    /// with `Oversize` and counted. A datagram the drop pattern names, or that the loss chance    /// catches, is counted as sent and lost and stops there. A direction without room for the    /// copies is refused with `QueueFull` before anything is queued. Otherwise the bytes are copied    /// into the queue with a delivery time drawn from the policy's delay range, and the duplication    /// chance may add a second copy with its own delay. The reordering chance trades the new copy's    /// delivery time and send order with the most recently queued one.    pub fn send(self: *Link, from: End, bytes: []const u8, now_ns: u64) SendError!void {        const direction_index = outgoingIndex(from);        const policy = self.policies[direction_index];        const mtu: usize = policy.effectiveMtu(self.memory.capacity.datagram_capacity);        if (bytes.len > mtu) {            increment(&self.counts[direction_index].oversize);            return error.Oversize;        }        const dropped = self.takeDrop(direction_index);        if (dropped or self.event(policy.loss_permille)) {            increment(&self.counts[direction_index].sent);            increment(&self.counts[direction_index].lost);            return;        }        const duplicate = self.event(policy.duplicate_permille);        const copies: u2 = if (duplicate) 2 else 1;        self.memory.requireFree(from, copies) catch {            increment(&self.counts[direction_index].queue_full);            return error.QueueFull;        };        const reorder = self.event(policy.reorder_permille);        const previous = if (reorder) self.previousIndex(direction_index) else null;        const first_delay = self.delay(policy);        const second_delay = if (duplicate) self.delay(policy) else 0;        increment(&self.counts[direction_index].sent);        const first = self.enqueue(direction_index, bytes, deliveryAt(now_ns, first_delay));        if (duplicate) {            _ = self.enqueue(direction_index, bytes, deliveryAt(now_ns, second_delay));            increment(&self.counts[direction_index].duplicated);        }        if (previous) |previous_index| {            self.swapDelivery(direction_index, first.index, previous_index);            increment(&self.counts[direction_index].reordered);        }    }    /// Copies the earliest datagram due at one endpoint by a given instant into the caller's buffer    /// and returns its length, so the destination endpoint receives whatever has arrived. The call    /// yields null before any datagram becomes due. Ties between equal delivery times go to the    /// earlier send order, so a reordered pair keeps a definite order. The caller's buffer holds at    /// least the datagram capacity, which debug builds check. The buffer lies outside the link's    /// own bytes, because the copy assumes they do not overlap. Taking a datagram frees its queue    /// slot for the next send.    pub fn receive(self: *Link, at: End, now_ns: u64, out: []u8) ?usize {        const direction_index = incomingIndex(at);        const entry_index = self.earliestIndex(direction_index) orelse return null;        const direction = &self.memory.directions[direction_index];        const entry = &direction.entries[entry_index];        if (entry.delivery_at_ns > now_ns) return null;        const length: usize = @intCast(entry.length);        std.debug.assert(out.len >= self.memory.capacity.datagram_capacity);        const payload = payloadAt(            direction,            entry_index,            self.memory.capacity.datagram_capacity,        );        @memcpy(out[0..length], payload[0..length]);        self.memory.release(direction_index, entry_index);        increment(&self.counts[direction_index].delivered);        return length;    }    /// Returns the instant at which the earliest pending datagram becomes due at one endpoint, so a    /// test driving a manual clock learns how far time may move before something arrives. An empty    /// queue gives null.    pub fn nextDeliveryAt(self: *const Link, at: End) ?u64 {        const direction_index = incomingIndex(at);        const entry_index = self.earliestIndex(direction_index) orelse return null;        return self.memory.directions[direction_index].entries[entry_index].delivery_at_ns;    }    /// Returns a copy of the counters for the datagrams one endpoint sent, so a test reads what the    /// path did to those datagrams after a run. The delivery count belongs to the sending direction    /// too, because the receiving endpoint's take is counted against the queue it came from.    pub fn stats(self: *const Link, from: End) Stats {        return self.counts[outgoingIndex(from)];    }    fn event(self: *Link, permille: u16) bool {        std.debug.assert(permille <= 1000);        if (permille == 0) return false;        if (permille == 1000) return true;        return self.prng.random().uintLessThan(u16, 1000) < permille;    }    fn takeDrop(self: *Link, direction_index: usize) bool {        std.debug.assert(direction_index < self.drops.len);        const pattern = self.drops[direction_index];        self.drops[direction_index] = pattern >> 1;        const dropped = pattern & 1 != 0;        const remaining = @popCount(self.drops[direction_index]);        std.debug.assert(remaining + @intFromBool(dropped) == @popCount(pattern));        return dropped;    }    fn delay(self: *Link, policy: Policy) u64 {        std.debug.assert(policy.delay_min_ns <= policy.delay_max_ns);        if (policy.delay_min_ns == policy.delay_max_ns) return policy.delay_min_ns;        const span = policy.delay_max_ns - policy.delay_min_ns;        const offset = if (span == std.math.maxInt(u64))            self.prng.random().int(u64)        else            self.prng.random().uintLessThan(u64, span + 1);        return policy.delay_min_ns + offset;    }    fn enqueue(        self: *Link,        direction_index: usize,        bytes: []const u8,        delivery_at_ns: u64,    ) EntryRef {        const acquired = self.memory.acquire(direction_index) catch unreachable;        std.debug.assert(bytes.len <= acquired.payload.len);        const sequence = self.takeSequence(direction_index);        @memcpy(acquired.payload[0..bytes.len], bytes);        acquired.entry.* = .{            .delivery_at_ns = delivery_at_ns,            .sequence = sequence,            .length = @intCast(bytes.len),            .occupied = true,        };        return acquired;    }    fn takeSequence(self: *Link, direction_index: usize) u64 {        std.debug.assert(direction_index < self.next_sequence.len);        std.debug.assert(self.next_sequence[direction_index] < std.math.maxInt(u64));        const sequence = self.next_sequence[direction_index];        self.next_sequence[direction_index] += 1;        return sequence;    }    fn previousIndex(self: *const Link, direction_index: usize) ?usize {        const entries = self.memory.directions[direction_index].entries;        var selected: ?usize = null;        for (entries, 0..) |entry, index| {            if (!entry.occupied) continue;            if (selected == null) selected = index;            if (selected) |current| {                if (entry.sequence > entries[current].sequence) selected = index;            }        }        return selected;    }    fn earliestIndex(self: *const Link, direction_index: usize) ?usize {        const entries = self.memory.directions[direction_index].entries;        var selected: ?usize = null;        for (entries, 0..) |entry, index| {            if (!entry.occupied) continue;            if (selected == null) selected = index;            if (selected) |current| {                if (entry.delivery_at_ns < entries[current].delivery_at_ns) selected = index;                if (entry.delivery_at_ns != entries[current].delivery_at_ns) continue;                if (entry.sequence < entries[current].sequence) selected = index;            }        }        return selected;    }    fn swapDelivery(self: *Link, direction_index: usize, first: usize, second: usize) void {        const entries = self.memory.directions[direction_index].entries;        std.debug.assert(first < entries.len);        std.debug.assert(second < entries.len);        std.mem.swap(u64, &entries[first].delivery_at_ns, &entries[second].delivery_at_ns);        std.mem.swap(u64, &entries[first].sequence, &entries[second].sequence);    }};

Source: lib/quic/src/sim/link.zig:116

zig
/// The owner of the queue records and payload bytes a link runs on, so one aligned block handed/// over by the caller becomes the only memory the link ever writes to. The owner takes one/// caller-provided aligned byte block and partitions it into the two directions' records and/// payload regions. A block whose length differs from the derived total gives/// `StorageLengthMismatch`. The owner allocates nothing further, so the link's memory use is fixed/// once the block is handed over. Teardown returns the block to the caller.pub const Memory = struct {    phase: alloc_phase.capacity.Phase,    capacity: MemoryCapacity,    storage: Storage,    directions: [2]Direction,    pub const storage_alignment: usize = @alignOf(Entry);    pub const Storage = []align(storage_alignment) u8;    pub const Limits: type = MemoryLimits;    pub const Capacity: type = MemoryCapacity;    pub const Exhaustion = error{QueueFull};    pub const InitError = MemoryCapacity.DeriveError || error{StorageLengthMismatch};    pub const work_limits: alloc_phase.capacity.WorkLimits = .{        .transition_steps_max = 1,        .cleanup_steps_per_call_max = 0,        .cleanup_calls_at_capacity_max = 0,    };    pub const claim: alloc_phase.capacity.Declaration = .{        .source = .{            .id = "quic.sim_memory",            .kind = .phase_static,            .limit_source = .caller,            .storage = .{                .covered = &.{                    .{                        .id = "two_direction_queue_metadata",                        .lifetime = .transferred,                        .detail = "caller storage for two bounded datagram metadata queues",                    },                    .{                        .id = "two_direction_datagram_payload_bytes",                        .lifetime = .transferred,                        .detail = "caller storage for two bounded datagram payload regions",                    },                },                .excluded = &.{                    "link policies, deterministic random state, statistics, and sequence counters",                    "per-direction drop patterns",                    "caller send slices and receive output slices",                    "socket, thread, clock, and operating-system state",                },            },            .capacity = .{                .inputs = &.{                    alloc_phase.capacity.bindInput(                        MemoryLimits,                        "queue_capacity",                        "queue_capacity",                    ),                    alloc_phase.capacity.bindInput(                        MemoryLimits,                        "datagram_capacity",                        "datagram_capacity",                    ),                },                .type_selectors = &.{                    alloc_phase.capacity.bindType(Entry, "entry"),                },                .nodes = &.{                    .{ .input = 0 },                    .{ .constant = 2 },                    .{ .product = .{ .left = 0, .right = 1 } },                    .{ .scale = .{                        .node = 2,                        .coefficient = .{ .size_of_concrete_type = 0 },                    } },                    .{ .input = 1 },                    .{ .product = .{ .left = 2, .right = 4 } },                    .{ .add = .{ .left = 3, .right = 5 } },                },                .assertions = &.{.{                    .scope = .closure_total,                    .measure = .retained,                    .relation = .exact,                    .expression = 6,                }},            },            .overload = .{                .kind = .reject_before_mutation,                .detail = "full direction admission preserves queue metadata and payload bytes",            },            .risks = .{                .transitive = .{                    .status = .witnessed,                    .detail = "link operations use only fixed scans and caller-owned byte slices",                },                .foreign = .{                    .status = .excluded,                    .detail = "simulator memory crosses no operating-system or foreign boundary",                },            },            .work = .{                .equation = "send and receive scan at most queue_capacity entries per direction",            },            .obligations = &.{                .{ .key = "quic_sim_memory_capacity", .role = .capacity_model },                .{ .key = "quic_sim_memory_overload", .role = .overload },                .{ .key = "quic_sim_memory_transitive", .role = .transitive_risk },                .{ .key = "quic_sim_memory_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: MemoryLimits) InitError!Memory {        const capacity = try MemoryCapacity.derive(limits);        if (storage.len != capacity.storage_bytes) return error.StorageLengthMismatch;        const entries = std.mem.bytesAsSlice(Entry, storage[0..capacity.entry_bytes]);        for (entries) |*entry| entry.* = .{};        const queue_count: usize = @intCast(capacity.queue_capacity);        const payload = storage[capacity.entry_bytes..];        return .{            .phase = .initialization,            .capacity = capacity,            .storage = storage,            .directions = .{                .{                    .entries = entries[0..queue_count],                    .payload = payload[0..capacity.direction_payload_bytes],                },                .{                    .entries = entries[queue_count..],                    .payload = payload[capacity.direction_payload_bytes..],                },            },        };    }    pub fn activate(self: *Memory) void {        std.debug.assert(self.phase == .initialization);        std.debug.assert(self.storage.len == self.capacity.storage_bytes);        std.debug.assert(self.directions[0].used == 0);        std.debug.assert(self.directions[1].used == 0);        self.phase = .steady;    }    fn freeCount(self: *const Memory, direction_index: usize) u32 {        std.debug.assert(self.phase == .steady);        std.debug.assert(direction_index < self.directions.len);        const direction = &self.directions[direction_index];        std.debug.assert(direction.used <= self.capacity.queue_capacity);        return self.capacity.queue_capacity - direction.used;    }    /// Reports whether one direction has room for a given number of further datagrams before the    /// link queues anything, so a full direction is refused before any byte moves. Too little room    /// gives `QueueFull`. The check reads the queue and changes nothing, so a refusal leaves the    /// queued datagrams as they were.    pub fn requireFree(self: *const Memory, from: End, copies: u2) Exhaustion!void {        std.debug.assert(copies > 0);        if (self.freeCount(outgoingIndex(from)) < copies) return error.QueueFull;    }    fn acquire(self: *Memory, direction_index: usize) Exhaustion!EntryRef {        std.debug.assert(self.phase == .steady);        std.debug.assert(direction_index < self.directions.len);        var direction = &self.directions[direction_index];        if (direction.used == self.capacity.queue_capacity) return error.QueueFull;        for (direction.entries, 0..) |*entry, index| {            if (entry.occupied) continue;            entry.occupied = true;            direction.used += 1;            return .{                .index = index,                .entry = entry,                .payload = payloadAt(direction, index, self.capacity.datagram_capacity),            };        }        unreachable;    }    fn release(self: *Memory, direction_index: usize, entry_index: usize) void {        std.debug.assert(self.phase == .steady);        std.debug.assert(direction_index < self.directions.len);        var direction = &self.directions[direction_index];        std.debug.assert(entry_index < direction.entries.len);        std.debug.assert(direction.entries[entry_index].occupied);        std.debug.assert(direction.used > 0);        direction.entries[entry_index].occupied = false;        direction.used -= 1;    }    pub fn deinit(self: *Memory) Storage {        std.debug.assert(self.phase == .steady);        self.phase = .teardown;        const storage = self.storage;        self.* = undefined;        return storage;    }};

Source: lib/quic/src/sim/policy.zig:15

zig
/// Impairments one direction of a simulated link applies, so a test builds one per direction and/// hands both to the link at startup. The link consults the policy on every send in that direction,/// and the two directions carry their own values. The default policy is a clean path: no loss, no/// duplication, no reordering, no delay, and the caller's full datagram capacity.pub const Policy = struct {    /// The chance out of one thousand, or permille, that the link drops a datagram it has accepted,    /// so a test makes the link throw datagrams away. A dropped datagram raises the sent count and    /// the lost count together.    loss_permille: u16 = 0,    /// The chance out of one thousand that the link queues a second copy of a datagram, so a test    /// makes the peer see a datagram twice. The two copies draw their delivery delays separately.    duplicate_permille: u16 = 0,    /// The chance out of one thousand that a newly queued copy trades delivery times with the most    /// recently queued one, so a test makes datagrams arrive out of order. The swap covers the    /// delivery time and the send order together, so the two copies exchange places in the queue.    reorder_permille: u16 = 0,    /// The smallest delivery delay in nanoseconds, which is itself a possible draw, so a test gives    /// the path a floor on its latency.    delay_min_ns: u64 = 0,    /// The largest delivery delay in nanoseconds, which is itself a possible draw, so a test gives    /// the path a ceiling on its latency. Each datagram takes a delay drawn uniformly between the    /// two bounds.    delay_max_ns: u64 = 0,    /// The largest datagram in bytes the link will accept in this direction, or MTU, so a test    /// makes the path refuse datagrams above a chosen size. Zero selects the caller's datagram    /// capacity. A larger send is refused with `Oversize` and counted.    mtu: u16 = 0,    pub const ValidationError: type = PolicyValidationError;    /// Checks one policy against the caller's datagram capacity for the link at startup so a    /// nonsensical policy fails before any datagram moves. A loss, duplication, or reordering    /// chance above one thousand gives `InvalidPermille`. A smallest delay above the largest gives    /// `InvalidDelayBounds`. An MTU above the caller's datagram capacity gives    /// `MtuExceedsCapacity`.    pub fn validate(self: Policy, datagram_capacity: u16) PolicyValidationError!void {        if (self.loss_permille > 1000) return error.InvalidPermille;        if (self.duplicate_permille > 1000) return error.InvalidPermille;        if (self.reorder_permille > 1000) return error.InvalidPermille;        if (self.delay_min_ns > self.delay_max_ns) return error.InvalidDelayBounds;        if (self.mtu > datagram_capacity) return error.MtuExceedsCapacity;        std.debug.assert(self.loss_permille <= 1000);        std.debug.assert(self.duplicate_permille <= 1000);        std.debug.assert(self.reorder_permille <= 1000);        std.debug.assert(self.delay_min_ns <= self.delay_max_ns);        std.debug.assert(self.mtu <= datagram_capacity);    }    /// Returns the policy's own MTU, or the caller's datagram capacity when the policy leaves it at    /// zero, so the link gets the size it must compare against on every send. The result is at most    /// the caller's datagram capacity.    pub fn effectiveMtu(self: Policy, datagram_capacity: u16) u16 {        std.debug.assert(self.mtu <= datagram_capacity);        const result = if (self.mtu == 0) datagram_capacity else self.mtu;        std.debug.assert(result <= datagram_capacity);        return result;    }};

Source: lib/quic/src/sim/policy.zig:3

zig
pub const ValidationError = error{    InvalidDelayBounds,    InvalidPermille,    MtuExceedsCapacity,};

Source: lib/quic/src/sim/stats.zig:8

zig
/// The counters one direction of a simulated link keeps. A test reads one after a run to check how/// many datagrams the link dropped, duplicated, or reordered. It counts datagrams the caller sent,/// datagrams delivered, datagrams lost, extra copies made, copies whose delivery time was swapped,/// sends refused for a full queue, and sends refused for exceeding the MTU. Every counter starts at/// zero.pub const Stats = struct {    sent: u64 = 0,    delivered: u64 = 0,    lost: u64 = 0,    duplicated: u64 = 0,    reordered: u64 = 0,    queue_full: u64 = 0,    oversize: u64 = 0,};
Called byCallsNo direct callstest sourcelib.quic.src.sim.linktest: simulator memory admits its que...test sourcelib.quic.src.sim.linktest: simulator memory capacity match...test sourcelib.quic.src.sim.linktest: simulator memory requires the e...sim.Capacityderive
Static calls · unresolved targets: 1 · external targets: 1.
Called byCallsNo direct callersprivate sourcelib.quic.src.sim.linkoutgoingIndexsim.LinkdropNext
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersprivate sourcelib.quic.src.sim.link.LinkearliestIndexprivate sourcelib.quic.src.sim.linkincomingIndexsim.LinknextDeliveryAt
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersprivate sourcelib.quic.src.sim.link.LinkearliestIndexprivate sourcelib.quic.src.sim.link.Memoryreleaseprivate sourcelib.quic.src.sim.linkincomingIndexprivate sourcelib.quic.src.sim.linkincrementprivate sourcelib.quic.src.sim.linkpayloadAtsim.Linkreceive
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersprivate sourcelib.quic.src.sim.link.Linkdelayprivate sourcelib.quic.src.sim.link.Linkenqueueprivate sourcelib.quic.src.sim.link.Linkeventprivate sourcelib.quic.src.sim.link.LinkpreviousIndexprivate sourcelib.quic.src.sim.link.LinkswapDelivery+5 moresim.Linksend
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callersprivate sourcelib.quic.src.sim.linkoutgoingIndexsim.Linkstats
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest sourcelib.quic.src.sim.linktest: simulator memory admits its que...test sourcelib.quic.src.sim.linktest: simulator memory requires the e...sim.Memoryactivate
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest sourcelib.quic.src.sim.linktest: simulator memory admits its que...test sourcelib.quic.src.sim.linktest: simulator memory requires the e...sim.Memorydeinit
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.quic.src.sim.linktest: simulator memory admits its que...test sourcelib.quic.src.sim.linktest: simulator memory requires the e...private sourcelib.reticulum.src.carrier.memory.MemoryCapacityderivesim.Memoryinit
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallssim.Linksendprivate sourcelib.quic.src.sim.link.MemoryfreeCountprivate sourcelib.quic.src.sim.linkoutgoingIndexsim.MemoryrequireFree
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/quic/src/root.zig:49

zig
pub const sim = @import("sim/root.zig");

Source: lib/quic/src/sim/root.zig

zig
const clock = @import("clock.zig");const link = @import("link.zig");const policy = @import("policy.zig");const stats = @import("stats.zig");pub const Clock = clock.Clock;pub const Capacity = link.Capacity;pub const CapacityError = link.CapacityError;pub const End = link.End;pub const Limits = link.Limits;pub const Link = link.Link;pub const Memory = link.Memory;pub const Policy = policy.Policy;pub const PolicyError = policy.ValidationError;pub const Stats = stats.Stats;

Complete call list for sim.Link.send

10 direct calls.

Audit

Definitions37
Public names37
Members40
Version26.7.0
Revisiondaab053ee433