///|
pub struct SessionPool {
mut sessions : Array[Decoder]
generation_limit : Int32
cell_limit : Int32
packet_limit : Int32
mut cells_used : Int32
}
///|
pub fn SessionPool::new(
max_generations~ : Int32 = 16,
max_cells~ : Int32 = 1048576,
max_packets_per_generation~ : Int32 = 4096,
) -> SessionPool raise CodeError {
if max_generations < 1 || max_generations > 32 {
raise Limit("pool generation budget must be 1..32")
}
if max_cells < 2 || max_cells > 4194304 {
raise Limit("pool field-cell budget must be 2..4194304")
}
if max_packets_per_generation < 1 || max_packets_per_generation > 65536 {
raise Limit("pool packet budget must be 1..65536")
}
{
sessions: [],
generation_limit: max_generations,
cell_limit: max_cells,
packet_limit: max_packets_per_generation,
cells_used: 0,
}
}
///|
fn generation_cells(generation : Generation) -> Int32 {
generation.count() * (generation.count() + generation.width())
}
///|
fn SessionPool::find(self : SessionPool, id : Int32) -> Int32? {
for i in 0.. Int32 {
self.sessions.length()
}
///|
pub fn SessionPool::reserved_cells(self : SessionPool) -> Int32 {
self.cells_used
}
///|
pub fn SessionPool::ids(self : SessionPool) -> Array[Int32] {
self.sessions.map(d => d.generation().id())
}
///|
pub fn SessionPool::reports(self : SessionPool) -> Array[DecodeReport] {
self.sessions.map(d => d.report())
}
///|
pub fn SessionPool::admit(
self : SessionPool,
packet : Packet,
) -> Admission raise CodeError {
if self.find(packet.generation().id()) is Some(index) {
return self.sessions[index].admit(packet)
}
if self.sessions.length() == self.generation_limit {
raise Limit("active generation budget exhausted")
}
let required = generation_cells(packet.generation())
if required > self.cell_limit - self.cells_used {
raise Limit("active field-cell budget exhausted")
}
let decoder = Decoder::new(packet.generation(), max_packets=self.packet_limit)
let admission = decoder.admit(packet)
self.sessions.push(decoder)
self.cells_used = self.cells_used + required
admission
}
///|
pub fn SessionPool::recover(
self : SessionPool,
id : Int32,
) -> Symbols raise CodeError {
match self.find(id) {
Some(index) => self.sessions[index].recover()
None => raise Invalid("unknown active generation")
}
}
///|
// Explicit forgetting permits ID reuse; applications must impose replay windows.
pub fn SessionPool::discard(self : SessionPool, id : Int32) -> Bool {
match self.find(id) {
None => false
Some(index) => {
let retained : Array[Decoder] = []
let released = generation_cells(self.sessions[index].generation())
for i in 0.. Symbols raise CodeError {
let output = self.recover(id)
ignore(self.discard(id))
output
}