Livt packages
Livt.Net
Version 1.4.1
Ethernet, IPv4, TCP, UDP, ARP, TFTP, and Telnet networking components for Livt hardware.
Published files (163)
src/tcp/TcpSendBuffer.lvt
namespace Livt.Net
using Livt.IO
/**
* Banked retained TCP payloads with a borrowed transmission view; serialize every call.
* Owns B in the inherited clock/reset context. B requires a parameterless constructor and
* at least CAPACITY * SESSIONS byte addresses. The default uses block RAM; callers may
* substitute array, automatic, distributed or custom scheduled storage. Access has no
* fixed-cycle guarantee. Length metadata guards unwritten bytes; reset discards all views
* and lengths without requiring payload initialization or retention.
*/
component TcpSendBuffer<CAPACITY: int = 256, SESSIONS: int = 1,
B: IRam<byte> & new() = BlockRam<byte, (CAPACITY * SESSIONS)>> : IPacketData
{
static assert SESSIONS > 0 && SESSIONS <= 16, "TCP sessions must be 1..16"
static assert CAPACITY > 0 && CAPACITY <= 536, "TCP send capacity must be 1..536"
// Width of the retained or received byte count, including full capacity.
const COUNT_BITS: int = Bits.RequiredFor(CAPACITY)
// Owned payload storage and retained byte count; no payload initialization is required.
storage: B
length: logic[SESSIONS, COUNT_BITS]
// Selected retained payload; selection is pinned while a view is open.
session: int
// Borrowed transmission window over retained bytes.
offset: logic[COUNT_BITS]
viewLength: logic[COUNT_BITS]
opened: bool
/**
* Creates owned storage and empty payload metadata in the inherited clock/reset context.
*/
new()
{
this.storage = new B()
}
/**
* Selects one retained payload when no view is published. Invalid indices or an open view
* return false without changing the selection. All calls share one serialized owner.
*/
public fn SelectSession(index: int) bool
{
if (this.opened || index < 0 || index >= SESSIONS) {
return false
}
// A single session has no runtime slot selection hardware.
static if (SESSIONS == 1) {
this.session = 0
} else {
this.session = index
}
return true
}
/**
* Returns the retained byte count, including bytes outside the current transmission view.
*/
public fn GetLength() int
{
return (this.length[this.session] as uint) as int
}
/**
* Appends one byte when no view is open and capacity remains. Success means the RAM write
* completed; rejection leaves the payload unchanged.
*/
public fn TryAppend(value: byte) bool
{
if (this.opened || this.GetLength() == CAPACITY) {
return false
}
this.storage.Write(this.session * CAPACITY + this.GetLength(), value)
this.length[this.session] = ((this.length[this.session] as uint) + (1 as uint)) as logic[COUNT_BITS]
return true
}
/**
* Discards retained length and any view without clearing RAM. Call only after all readers have
* released their borrow.
*/
public fn Clear()
{
this.opened = false
this.length[this.session] = 0
}
/**
* Borrows the retained range [start, start + count) for transmission. Requires non-negative
* bounds within GetLength(); failure leaves the existing view unchanged.
*/
public fn Open(start: int, count: int) bool
{
if (this.opened || start < 0 || count < 0 || start > this.GetLength() || count > this.GetLength() - start) {
return false
}
this.offset = start as logic[COUNT_BITS]
this.viewLength = count as logic[COUNT_BITS]
this.opened = true
return true
}
/**
* Ends the transmission view without discarding retained bytes. The caller must first finish all
* reads.
*/
public fn Close()
{
this.opened = false
}
/**
* Reports whether a transmission view is open; this is a snapshot, not a reservation.
*/
override fn IsReady() bool
{
return this.opened
}
/**
* Returns the open view length, or zero when closed.
*/
override fn GetAvailableLength() int
{
if (!this.opened) {
return 0
}
return (this.viewLength as uint) as int
}
/**
* Reads a view-relative byte through scheduled RAM access. Failure assigns zero and reports
* NotReady or Invalid; reads do not consume payload.
*/
override fn TryRead(index: int, value: out byte) PacketDataResult
{
value = 0
if (!this.opened) {
return PacketDataResult.NotReady
}
if (index < 0 || index >= this.GetAvailableLength()) {
return PacketDataResult.Invalid
}
value = this.storage.Read(this.session * CAPACITY + ((this.offset as uint) as int) + index)
return PacketDataResult.Success
}
}