Packet Registry

Read time 3 minutes Package: Nalix.Codec
On this page

Namespace: Nalix.Codec.DataFrames
Assembly: Nalix.Codec

PacketRegistry is a process-wide, thread-safe catalog that maps 16-bit packet opcodes (ushort) to compile-time generated PacketDeserializer delegates in an $O(1)$ lookup array (PacketDeserializer?[65536]).

Source Mapping

  • src/Nalix.Codec/DataFrames/PacketRegistry.cs
  • analyzers/Nalix.Analyzers.Generators/PacketRegistryGenerator.cs

Core Lifecycle

  1. Static Construction & Compile-Time Registration ([ModuleInitializer]):
    • The PacketRegistry static constructor immediately pre-registers all 11 built-in protocol frames (Control, Directive, TimeSync, SessionInit, SessionChallenge, SessionProof, SessionEstablished, SessionResume, SessionTofu, ProofOfWorkChallenge, ProofOfWorkProof) and populates the built-in s_runtimeFastDispatcher.
    • PacketRegistryGenerator discovers all concrete packet classes implementing IPacket and IPacketDeserializer<TSelf> (with [Packet] or IPacketStaticOpcode) and emits PacketRegistryGenerated.Initialize(), which calls PacketRegistry.RegisterGenerated<TPacket>(name, deserializer) and PacketRegistry.RegisterGenerated(PacketDispatch).
  2. Pool Configuration (PacketRegistry.Configure):
    • Configure(IObjectPoolManager? manager) binds the shared IObjectPoolManager used by PacketBase<TSelf>.Create() and pre-configures pool capacities (128 each) for all 11 built-in protocol frames.
  3. Freezing the Lookup Table (PacketRegistry.Build):
    • Build() materializes the 65,536-entry PacketDeserializer?[] table and composes any registered PacketDispatch fast dispatchers (RegisterGenerated works both before and after Build()). NetworkApplicationBuilder.Build() and TransportSession call PacketRegistry.Build() automatically.

Public API Reference

MemberSignatureDescription
IsBuiltpublic static bool IsBuilt { get; }Returns true after Build() has been called.
DeserializerCountpublic static int DeserializerCount { get; }Number of registered packet deserializers frozen during Build().
Configurepublic static void Configure(IObjectPoolManager? manager)Sets the shared IObjectPoolManager for PacketBase<TSelf> pooling and configures pool capacities for built-in protocol frames.
Buildpublic static void Build()Idempotently builds and freezes the 65,536-entry opcode lookup table.
RegisterGeneratedpublic static void RegisterGenerated(PacketDispatch dispatcher)Registers a fast-path PacketDispatch delegate (bool PacketDispatch(ushort opcode, ReadOnlySpan<byte> raw, out IPacket? packet)).
RegisterGenerated<TPacket>public static void RegisterGenerated<TPacket>(string name, PacketDeserializer deserializer) where TPacket : IPacket, IPacketStaticOpcodeRegisters a packet deserializer for TPacket.StaticOpCode. Throws InternalErrorException if a different packet name is already bound to the same opcode.
IsKnownOpCodepublic static bool IsKnownOpCode(ushort opcode)Returns true if a deserializer is registered for opcode (requires Build()).
IsRegistered<TPacket>public static bool IsRegistered<TPacket>() where TPacket : IPacket, IPacketStaticOpcodeReturns true if TPacket.StaticOpCode is registered (requires Build()).
Deserializepublic static IPacket Deserialize(ReadOnlySpan<byte> raw)Deserializes raw or throws ArgumentException (if < 6 bytes) / InvalidOperationException (if not built or opcode is unregistered).
TryDeserializepublic static bool TryDeserialize(ReadOnlySpan<byte> raw, [NotNullWhen(true)] out IPacket? packet)Non-throwing deserialization. Returns false if raw.Length < 6 (PacketConstants.HeaderSize) or if the opcode is unknown.

Usage

1. Standalone Initialization

NetworkApplicationBuilder and TransportSession call PacketRegistry.Build() automatically. In standalone benchmarks or unit tests, call Build() once before deserializing:

using Nalix.Codec.DataFrames;
using Nalix.Framework.Memory.Objects;

// Optional: attach ObjectPoolManager so PacketBase<T>.Deserialize rents from the pool
PacketRegistry.Configure(new ObjectPoolManager());

// Freeze the O(1) opcode lookup table
PacketRegistry.Build();

2. Deserializing Packets

using System;
using Nalix.Abstractions.Networking.Packets;
using Nalix.Codec.DataFrames;
using Nalix.Framework.Injection;

if (PacketRegistry.TryDeserialize(bufferLease.Span, out IPacket? packet))
{
    try
    {
        Console.WriteLine($"Decoded OpCode=0x{packet.Header.OpCode:X4}, Type={packet.GetType().Name}");
    }
    finally
    {
        // Dispose pooled PacketBase<TSelf> instances to return them under their concrete TSelf pool key
        (packet as IDisposable)?.Dispose();
    }
}