Ljh684/moonnet-lab/src/net does not have a README file

    Admission

    pub(all) enum Admission {
    Accepted
    DroppedPacketLimit
    DroppedByteLimit
    DroppedEarly
    } derive(Eq,
    Debug
    )

    Why a queue did or did not accept a packet.

    Returning a reason rather than a Bool is deliberate: "the packet was dropped" is never enough information to explain a throughput result. "The buffer was full" and "the buffer was well below full but the packet had already waited too long" are different diagnoses with different fixes.
    impl Show for Admission

    Admission::render

    fn Admission::render(self : Admission) -> String

    pub struct Link[T] {
    spec : LinkSpec
    rng :
    Rng

    loss_key : UInt64
    queue : PacketQueue[T]
    deliver : (Packet[T]) -> Unit
    busy_until :
    Time

    service_time :
    Time

    started_at :
    Time

    transmitted : Int
    bytes_sent : Int64
    lost : Int
    }

    A one-way link: it serializes packets at its bandwidth, queues what it cannot send yet, and delivers packets to a sink after the propagation delay.

    The link owns no timer of its own; it asks the simulator to wake it up when the current transmission finishes, which is what keeps "how long is the link busy" a question about the model rather than about the host's clock.

    Link::busy_time

    fn[T] Link::busy_time(self : Link[T]) ->
    Time

    How long the link has been transmitting since the run began.

    Link::bytes_sent

    fn[T] Link::bytes_sent(self : Link[T]) -> Int64

    Bytes that reached the far end.

    Link::draw_jitter

    One jitter sample, uniform on [-jitter, +jitter].

    The sample is clamped so that a packet can never arrive before it left; a negative propagation delay would break every later consistency check.

    Link::draw_loss

    fn[T] Link::draw_loss(self : Link[T], sim :
    Sim
    , tag : UInt64) -> Bool

    Decide whether the transmission that just started is lost.

    A packet that carries a tag is judged on its identity: the same tag on the same link always meets the same fate, whatever else was transmitted in between. That is what lets two senders be compared on the same lost packets rather than merely on the same conditions.

    A packet with no tag — an acknowledgment, whose identity is not stable because the same acknowledgment is sent repeatedly — falls back to drawing from the link's own stream. Its loss pattern is therefore still a property of transmission order, which is worth knowing when reading a comparison: the data loss is controlled, the acknowledgment loss is not.

    The draw is skipped entirely on a loss-free link, so adding a reliable link to a scenario cannot shift the random stream of the links around it.

    Link::dropped

    fn[T] Link::dropped(self : Link[T]) -> Int

    Packets this link refused.

    Link::dropped_early

    fn[T] Link::dropped_early(self : Link[T]) -> Int

    Packets this link's queue management refused while the buffer still had room.

    A drop-tail queue never reports one of these, so the counter is a direct reading of whether the configured discipline did anything at all.

    Link::lost

    fn[T] Link::lost(self : Link[T]) -> Int

    Packets this link accepted, transmitted, and then lost on the wire.

    Link::max_sojourn

    fn[T] Link::max_sojourn(self : Link[T]) ->
    Time

    The longest a packet waited in this link's buffer.

    Link::mean_queue_packets

    fn[T] Link::mean_queue_packets(self : Link[T], sim :
    Sim
    ) -> Double

    Mean occupancy of this link's buffer, in packets.

    Peak occupancy says how big the buffer had to be; the mean says how full it usually was, which is what separates a buffer that absorbs bursts from one that permanently holds a backlog.

    Link::name

    fn[T] Link::name(self : Link[T]) -> String

    The name this link was configured with.

    Link::new

    fn[T] Link::new(spec : LinkSpec, seed : UInt64, deliver : (Packet[T]) -> Unit) -> Link[T]

    Link::pump

    fn[T] Link::pump(self : Link[T], sim :
    Sim
    ) -> Unit

    Start transmitting the next queued packet, if any.

    The link may only be pumping one packet at a time. When two packets are handed over in the same picosecond the second one finds the link busy and waits, and it is the "link free" event that wakes the pump up again. Without this guard the two transmissions would overlap and the model would silently invent bandwidth that does not exist.

    Link::send

    fn[T] Link::send(self : Link[T], sim :
    Sim
    , packet : Packet[T]) -> Admission

    Hand a packet to the link.

    A packet that does not fit is dropped here, at the tail of the queue: that is the "drop-tail" behavior every later active-queue experiment is compared against.

    Link::summary

    fn[T] Link::summary(self : Link[T], sim :
    Sim
    ) -> String

    A one-line summary for reports and tests.

    Link::transmitted

    fn[T] Link::transmitted(self : Link[T]) -> Int

    Packets that reached the far end.

    Link::utilization

    fn[T] Link::utilization(self : Link[T], sim :
    Sim
    ) -> Double

    Fraction of the observed interval the link spent serializing bits, in [0, 1]. This is the number that says whether a path is saturated.

    LinkSpec

    pub(all) struct LinkSpec {
    name : String
    bandwidth_bps : Int64
    delay :
    Time

    jitter :
    Time

    loss_rate : Double
    queue : QueueSpec
    } derive(Eq,
    Debug
    )

    The static description of a link.

    Everything here is a property of the medium, not of a particular packet: bandwidth is bits per second on the wire, delay is one-way propagation time, and jitter is the width of a uniform spread around it.

    LinkSpec::new

    fn LinkSpec::new(name : String, bandwidth_bps : Int64, delay :
    Time
    , queue : QueueSpec) -> LinkSpec

    A link with no jitter and a drop-tail queue.

    LinkSpec::with_jitter

    fn LinkSpec::with_jitter(self : LinkSpec, jitter :
    Time
    ) -> LinkSpec

    The same link, with a uniform jitter of plus or minus jitter around the propagation delay. Jitter is drawn from the link's own random stream, so it is reproducible and independent of every other link.

    LinkSpec::with_loss

    fn LinkSpec::with_loss(self : LinkSpec, rate : Double) -> LinkSpec

    The same link, now losing the given fraction of the packets it transmits.

    The decision comes from the link's own random stream, so "1% loss" is not "roughly 1% today" but the exact same set of lost packets on every run.

    Packet

    pub(all) struct Packet[T] {
    id : Int
    flow : Int
    kind : PacketKind
    size : Int
    tag : UInt64
    payload : T
    }

    A packet as the network layer sees it.

    size is the number of bytes that actually occupy the wire, including headers: that is the quantity that costs serialization time, and getting it right is what makes throughput numbers meaningful.

    The payload type is a parameter so that the TCP layer can hang its segment state off the same packet the link layer schedules, without the link layer depending on TCP.

    Packet::label

    fn[T] Packet::label(self : Packet[T]) -> String

    A short, stable label used in journals and reports.

    Packet::new

    fn[T] Packet::new(id : Int, flow : Int, kind : PacketKind, size : Int, tag : UInt64, payload : T) -> Packet[T]

    Build a packet.

    tag is the packet's identity: a number the sender derives from what the packet is rather than from when it was sent. A link decides loss from the tag, so the same packet meets the same fate on the same link whatever else is in flight — which is what turns a comparison between two senders into a controlled experiment. Zero means "no identity", and the link falls back to drawing from its own stream.

    PacketKind

    pub(all) enum PacketKind {
    Data
    Ack
    Syn
    SynAck
    Fin
    }

    What a packet is carrying. The network layer only needs to know enough to model it; whatever protocol produced the packet keeps its own richer payload in Packet::payload.
    impl Show for PacketKind

    PacketKind::render

    fn PacketKind::render(self : PacketKind) -> String

    PacketQueue

    pub struct PacketQueue[T] {
    spec : QueueSpec
    rng :
    Rng

    slots : Array[Queued[T]?]
    head : Int
    count : Int
    bytes : Int
    admitted : Int
    dropped : Int
    dropped_bytes : Int
    dropped_tail : Int
    dropped_early : Int
    peak_packets : Int
    peak_bytes : Int
    occupancy_integral : Int64
    observed_at :
    Time

    max_sojourn :
    Time

    first_above :
    Time

    dropping : Bool
    drop_next :
    Time

    drop_count : Int
    average_occupancy : Double
    }

    A bounded FIFO queue with a configurable discipline.

    The storage is a ring buffer: slots is allocated once and the queue never moves elements, so enqueue and dequeue are both O(1) and no allocation happens during a run. Allocation inside an event handler is the one thing that could make a simulation's timing depend on the host.

    PacketQueue::admitted

    fn[T] PacketQueue::admitted(self : PacketQueue[T]) -> Int

    PacketQueue::bytes

    fn[T] PacketQueue::bytes(self : PacketQueue[T]) -> Int

    PacketQueue::dequeue

    fn[T] PacketQueue::dequeue(self : PacketQueue[T], now :
    Time
    ) -> Packet[T]?

    Remove and return the oldest packet.

    PacketQueue::dropped

    fn[T] PacketQueue::dropped(self : PacketQueue[T]) -> Int

    How many packets were refused by this queue since the run began.

    PacketQueue::dropped_at_tail

    fn[T] PacketQueue::dropped_at_tail(self : PacketQueue[T]) -> Int

    Packets refused because the buffer was full.

    PacketQueue::dropped_early

    fn[T] PacketQueue::dropped_early(self : PacketQueue[T]) -> Int

    Packets refused by queue management before the buffer was full.

    PacketQueue::enqueue

    fn[T] PacketQueue::enqueue(self : PacketQueue[T], packet : Packet[T], now :
    Time
    ) -> Admission

    Append a packet, or explain why it does not fit.

    PacketQueue::is_empty

    fn[T] PacketQueue::is_empty(self : PacketQueue[T]) -> Bool

    PacketQueue::length

    fn[T] PacketQueue::length(self : PacketQueue[T]) -> Int

    PacketQueue::max_sojourn

    The longest a packet waited in this queue.

    Measured on departure, so it is the worst wait of a packet that was actually sent and it is comparable between disciplines. The wait of a packet that a discipline refused belongs to that packet's story rather than to the delay this queue adds to the traffic that gets through.

    PacketQueue::mean_occupancy

    fn[T] PacketQueue::mean_occupancy(self : PacketQueue[T], now :
    Time
    ) -> Double

    Mean occupancy over the interval since the run began, in packets.

    A queue that is frequently full but empties between bursts and one that holds a permanent backlog can have the same peak; only the mean tells them apart.

    PacketQueue::new

    fn[T] PacketQueue::new(spec : QueueSpec, seed : UInt64) -> PacketQueue[T]

    PacketQueue::peak

    fn[T] PacketQueue::peak(self : PacketQueue[T]) -> (Int, Int)

    The largest occupancy seen so far, in packets and in bytes.

    PacketQueue::peek

    fn[T] PacketQueue::peek(self : PacketQueue[T]) -> Packet[T]?

    Look at the oldest packet without removing it.

    PacketQueue::summary

    fn[T] PacketQueue::summary(self : PacketQueue[T], now :
    Time
    ) -> String

    A text summary used by the report renderer and by tests.

    QueueDiscipline

    pub(all) enum QueueDiscipline {
    DropTail
    Red(Int, Int, Double)
    Codel(
    Time
    ,
    Time
    )
    } derive(Eq,
    Debug
    )

    How a queue decides what to admit.

    The set is closed on purpose, unlike congestion control, which is an open interface: a queue discipline is tightly coupled to the buffer's own state — occupancy and the arrival times of the packets in it — which an outside object cannot see, so the policies live next to the queue they manage.

    QueueDiscipline::codel

    CoDel's recommended defaults: 5 ms of standing queue is already too much, and 100 ms is how often to act on it.

    QueueDiscipline::red_for

    fn QueueDiscipline::red_for(max_packets : Int) -> QueueDiscipline

    RED configured from the buffer's own size: start dropping when the mean occupancy passes a quarter of the buffer, refuse everything past three quarters, and mark at most one packet in ten in between.

    QueueSpec

    pub(all) struct QueueSpec {
    max_packets : Int
    max_bytes : Int
    discipline : QueueDiscipline
    } derive(Eq,
    Debug
    )

    Specifications of a bounded queue.

    The packet limit models a switch's descriptor budget, the byte limit models its buffer memory. Real devices limit both, and bufferbloat experiments usually care about the byte limit, so the simulator carries both.

    QueueSpec::bytes

    fn QueueSpec::bytes(max_bytes : Int) -> QueueSpec

    A queue limited by bytes only. max_bytes of 0 means "no byte limit".

    QueueSpec::packets

    fn QueueSpec::packets(max_packets : Int) -> QueueSpec

    A queue with unlimited bytes, limited only by packet count.

    QueueSpec::with_discipline

    fn QueueSpec::with_discipline(self : QueueSpec, discipline : QueueDiscipline) -> QueueSpec

    The same buffer, managed by a different discipline.

    Queued

    type Queued[T]

    A packet waiting in a queue, with the time it arrived.

    The arrival time is what makes queue management possible: a policy that only knows how many bytes are buffered cannot tell a long queue on a fast link from a short queue on a slow one, and that distinction is the whole point of CoDel.

    default_mtu

    fn default_mtu() -> Int

    The MTU the simulator assumes unless a scenario says otherwise. This is the payload size of a standard Ethernet frame; the 14-byte Ethernet header and 4-byte FCS are accounted for separately in frame_overhead.

    format_one_decimal

    fn format_one_decimal(value : Double) -> String

    A number with one decimal, formatted through integers so that two runs produce the same text.

    frame_overhead

    fn frame_overhead() -> Int

    Bytes a payload picks up from link-layer framing.

    percent

    fn percent(fraction : Double) -> String

    A percentage with one fractional digit, formatted with integer arithmetic so that reports are byte-identical across backends.

    wire_size

    fn wire_size(bytes : Int) -> Int

    The wire size of a payload of bytes bytes, including framing.