When a Terbin sender has a payload larger thanDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/FarlandsModdingTeam/TerbinProyect/llms.txt
Use this file to discover all available pages before exploring further.
MAX_PLD (65,520 bytes), it splits the data into chunks and streams each chunk as a separate packet with an incrementing OrderRequest index. The receiver must accumulate these fragments in the correct order and reassemble them into the original byte stream before dispatching the handler. The memory subsystem — TerbinMemory, TerbinMemoryManager, and TerbinMemoryHelper — is responsible for the entire receive-side fragment lifecycle.
The first 10 memory slot IDs (0–9, the
RESERVE_MEMORY constant) are reserved for internal protocol operations. Application code should never manually allocate, store to, or release slots in this range. TerbinMemoryHelper.TryReleaseMemory enforces this guard by refusing to release any slot with IdMemory <= RESERVE_MEMORY.TerbinMemory — A Single Fragment Store
A TerbinMemory instance represents one reassembly slot. Internally it holds a Dictionary<ushort, byte[]> keyed by fragment order index.
Properties
The slot’s unique byte identifier within
TerbinMemoryManager. Assigned when the slot is created.The
IdRequest of the fragmented transmission currently using this slot. Initialized to CodeTerbinMemory.NotAsign (1) when free.Computed from
IdRequest != (ushort)CodeTerbinMemory.NotAsign. true while a fragmented send is in progress.AddFragment(ushort order, byte[] data)
OrderRequest key. Duplicate order values are silently ignored inside the lock (the first arrival wins) and _totalSize is only incremented for new entries. OnAdd fires unconditionally after the lock block — even if the fragment was a duplicate — so subscribers must not assume a new fragment was actually stored.
TryGetFullData(out byte[] data)
- Takes a thread-safe snapshot of
_fragmentsunder a lock. - Returns
(false, TerbinErrorCode.InvalidLength)if no fragments have arrived yet. - Sorts fragments by
OrderRequestkey. - Calls the private
chechMissingvalidator — iterates the sorted array and confirms that the keys are exactly1, 2, 3, ..., Nwith no gaps. - On success, concatenates all fragment byte arrays into a single buffer using
Buffer.BlockCopyand returns(true, TerbinErrorCode.None).
Release()
IdRequest to CodeTerbinMemory.NotAsign, nulls OnAdd, fires OnRelease, then calls Clear(). After Release() the slot can be reused for a new fragmented transfer.
Clear()
_totalSize to zero. Thread-safe via lock (_fragments). Called by Release() and also usable independently to abandon a partial transfer.
Events
| Event | When it fires |
|---|---|
OnAdd | After every AddFragment call (including duplicate-key calls that did not store a new fragment) |
OnRelease | During Release(), before Clear() |
TerbinMemoryManager — The Slot Pool
TerbinMemoryManager is a static class that manages the global pool of TerbinMemory slots using a ConcurrentDictionary<byte, TerbinMemory>.
Requesting a New Slot — GetFreeStore()
_containers for any slot where IsOcupated == false. If one exists, returns its ID immediately. If all existing slots are occupied, creates a new TerbinMemory instance with a fresh MiniID.NewB byte identifier, adds it to the dictionary, and returns the new ID.
This is called by TerbinExecutor.Solicit when an incoming packet carries IdMemory = CodeTerbinMemory.New — the standard fragmentation handshake sends CodeTerbinProtocol.Solicit as the action key to trigger this handler.
Storing Fragments — Store() and OverwriteStore()
Store uses GetOrAdd to locate (or create) the slot and calls AddFragment. OverwriteStore clears any existing content in the slot before adding the fragment — used internally by TerbinExecutor.Load when OrderRequest == 0.
Retrieving — TryGetResult()
TerbinMemory.TryGetFullData. Returns the same (succes, typeError) tuple.
Releasing — Release()
TerbinMemory.Release(). Returns true if the slot existed. Does not remove the slot from the dictionary; it remains available for reuse.
Removing — Remove()
true if the entry existed.
TerbinMemoryHelper — Integration with the Receive Path
TerbinMemoryHelper is the bridge between TerbinCommunicator.handleReceive and the memory subsystem.
TryGetMemoryStream(PacketRequest, out byte[])
- If
pCapsule.Head.OrderRequest != FINAL_PACKET— the packet is either a single-packet transmission or an intermediate fragment. The payload is returned directly andTerbinErrorCode.Noneis returned. - If
OrderRequest == FINAL_PACKET— callsTryAssembleStreamto merge all stored fragments with the final packet’s payload, then callsTryReleaseMemoryto free the slot. If reassembly succeeds but release fails, the error code is upgraded toTerbinErrorCode.MemoryReleaseFailed.
TryAssembleStream(PacketRequest, out byte[])
TerbinMemoryManager.TryGetResult for the slot ID from the header, then calls CombinePayload to concatenate the stored fragments with the final packet’s payload ([stored bytes] + [final payload]).
TryReleaseMemory(byte)
TerbinMemoryManager.Release when pIdMemory > TerbinProtocol.RESERVE_MEMORY. Protects protocol-reserved slot IDs from accidental release.
CombinePayload(PacketRequest, byte[])
pBytes (all previously stored fragments) and pCapsule.Payload (the final chunk) into a single contiguous array using Buffer.BlockCopy. The result is the complete reassembled payload delivered to the handler.
Memory Lifecycle Sequence
Practical Notes
Thread Safety
Fragment insertion in
AddFragment is protected by lock (_fragments). The ConcurrentDictionary in TerbinMemoryManager makes slot-level operations safe across multiple concurrent transfers.Slot Reuse
Slots are never permanently removed by the normal fragment flow —
Release() resets state but leaves the slot in the pool. This avoids repeated dictionary allocation for frequently used large-payload routes.No Manual Management
Application handlers receive the fully assembled payload. They never need to call
TerbinMemoryManager or TerbinMemoryHelper directly.Ordered Validation
chechMissing requires keys 1, 2, 3, ..., N in exact consecutive order. A sender that accidentally sends two fragments with the same order index or skips an index will cause TerbinErrorCode.OrderMismatch.