202 lines
8.8 KiB
C#
202 lines
8.8 KiB
C#
using System.Runtime.CompilerServices;
|
||
using AyCode.Core.Compression;
|
||
|
||
namespace AyCode.Core.Serializers.Binaries;
|
||
|
||
/// <summary>
|
||
/// Options for AcBinarySerializer and AcBinaryDeserializer.
|
||
/// Optimized for speed and memory efficiency over raw size.
|
||
/// </summary>
|
||
public sealed class AcBinarySerializerOptions : AcSerializerOptions
|
||
{
|
||
public override AcSerializerType SerializerType { get; init; } = AcSerializerType.Binary;
|
||
|
||
/// <summary>
|
||
/// Current binary format version. Incremented when breaking changes are made.
|
||
/// </summary>
|
||
public const byte FormatVersion = 1;
|
||
|
||
/// <summary>
|
||
/// Default options instance with metadata and string interning enabled.
|
||
/// Returns a new instance each time to prevent shared state corruption.
|
||
/// </summary>
|
||
public static AcBinarySerializerOptions Default => new();
|
||
|
||
/// <summary>
|
||
/// Options optimized for maximum speed (no interning, no references).
|
||
/// Use when deserializer knows the exact type structure.
|
||
/// Returns a new instance each time to prevent shared state corruption.
|
||
/// </summary>
|
||
public static AcBinarySerializerOptions FastMode => new()
|
||
{
|
||
UseStringInterning = StringInterningMode.None,
|
||
ReferenceHandling = ReferenceHandlingMode.None
|
||
};
|
||
|
||
/// <summary>
|
||
/// Options for shallow serialization (root level only).
|
||
/// Returns a new instance each time to prevent shared state corruption.
|
||
/// </summary>
|
||
public static AcBinarySerializerOptions ShallowCopy => new()
|
||
{
|
||
MaxDepth = 0,
|
||
UseStringInterning = StringInterningMode.None,
|
||
ReferenceHandling = ReferenceHandlingMode.None
|
||
};
|
||
|
||
/// <summary>
|
||
/// Options optimized for WASM environment with string caching enabled.
|
||
/// Returns a new instance each time to prevent shared state corruption.
|
||
/// </summary>
|
||
public static AcBinarySerializerOptions WasmOptimized => new()
|
||
{
|
||
IsWasm = true,
|
||
UseStringCaching = true
|
||
};
|
||
|
||
/// <summary>
|
||
/// Whether running in WebAssembly/Browser environment.
|
||
/// When true, enables WASM-specific optimizations like string caching.
|
||
/// Default: auto-detected via OperatingSystem.IsBrowser()
|
||
/// </summary>
|
||
public bool IsWasm { get; init; } = DetectedIsWasm;
|
||
|
||
/// <summary>
|
||
/// Whether to cache short strings during deserialization to reduce allocations.
|
||
/// Most beneficial in WASM where GC is expensive.
|
||
/// Auto-enabled when IsWasm is true, can be overridden.
|
||
/// Default: follows IsWasm setting
|
||
/// </summary>
|
||
public bool UseStringCaching { get; private init; } = DetectedIsWasm;
|
||
|
||
/// <summary>
|
||
/// Maximum string length to cache when UseStringCaching is enabled.
|
||
/// Longer strings are not cached to avoid memory bloat.
|
||
/// Default: 64 characters
|
||
/// </summary>
|
||
public int MaxCachedStringLength { get; init; } = 64;
|
||
|
||
/// <summary>
|
||
/// Whether to include property hash metadata in footer for cross-type deserialization.
|
||
/// When enabled, property name hashes (FNV-1a) are written per type in the footer,
|
||
/// allowing the deserializer to match properties by name between different types.
|
||
/// Default: false (no overhead)
|
||
/// </summary>
|
||
public bool UseMetadata { get; set; } = false;
|
||
public bool UseGeneratedCode { get; set; } = true;
|
||
|
||
/// <summary>
|
||
/// Wire encoding mode.
|
||
/// Compact: VarInt + UTF-8 (default, smaller output).
|
||
/// Fast: Fixed-width integers + UTF-16 (larger output, faster encode/decode).
|
||
/// </summary>
|
||
public WireMode WireMode { get; set; } = WireMode.Compact;
|
||
|
||
/// <summary>
|
||
/// Controls how string interning is applied during serialization.
|
||
/// None: No interning, all strings written inline.
|
||
/// Attribute: Only properties with [AcStringIntern(true)] are interned.
|
||
/// All: All strings within length limits are interned (legacy behavior).
|
||
/// Default: All
|
||
/// </summary>
|
||
public StringInterningMode UseStringInterning { get; set; } = StringInterningMode.All;
|
||
|
||
/// <summary>
|
||
/// When true, checks for duplicate property name hashes during serialization (UseMetadata mode).
|
||
/// Throws exception if FNV-1a hash collision is detected between property names of the same type.
|
||
/// Should be enabled during development/testing, can be disabled in production for performance.
|
||
/// Default: true (safety first)
|
||
/// </summary>
|
||
public bool CheckDuplicatePropName { get; init; } = true;
|
||
|
||
/// <summary>
|
||
/// Minimum string length to consider for interning.
|
||
/// Shorter strings are written inline to avoid overhead.
|
||
/// Default: 4 (strings shorter than 4 chars are not interned)
|
||
/// </summary>
|
||
public byte MinStringInternLength { get; init; } = 4;
|
||
|
||
/// <summary>
|
||
/// Maximum string length to consider for interning.
|
||
/// Longer strings (descriptions, notes, etc.) are usually unique and not worth interning.
|
||
/// Set to 0 to disable max limit.
|
||
/// Default: 64 (strings longer than 64 chars are not interned)
|
||
/// </summary>
|
||
public byte MaxStringInternLength { get; init; } = 64;
|
||
|
||
/// <summary>
|
||
/// Initial capacity for serialization buffer.
|
||
/// Default: 4096 bytes
|
||
/// </summary>
|
||
public int InitialBufferCapacity { get; init; } = 4096;
|
||
|
||
/// <summary>
|
||
/// Chunk size (in bytes) used by <see cref="BufferWriterBinaryOutput"/> when writing to an <see cref="System.Buffers.IBufferWriter{T}"/>.
|
||
/// Controls how much data is accumulated before committing (Advance + GetMemory) to the underlying writer.
|
||
///
|
||
/// <para><b>How it works:</b> The serializer writes into a chunk buffer. When the chunk fills up,
|
||
/// it commits the written bytes to the IBufferWriter and acquires a new chunk. Larger chunks mean
|
||
/// fewer Grow() calls (less overhead), but consume more memory per chunk. Smaller chunks reduce
|
||
/// memory footprint and latency-to-first-byte for streaming, but increase Grow() call frequency.</para>
|
||
///
|
||
/// <para><b>Choosing a value:</b></para>
|
||
/// <list type="bullet">
|
||
/// <item><b>Memory-backed writers</b> (ArrayPooledBufferWriter, file/DB blob): use 65536 (64 KB, the default).
|
||
/// Stays below the .NET LOH threshold (85 KB), minimizes Grow() overhead for large payloads.
|
||
/// An 8 MB payload triggers ~128 Grow() calls.</item>
|
||
/// <item><b>Network streaming</b> (Kestrel PipeWriter, SignalR): use 4096 (4 KB).
|
||
/// Aligns with Kestrel's default memory pool slab size and TCP segment sizes (~1500 byte MTU × 3).
|
||
/// Reduces latency-to-first-byte by flushing data to the wire sooner.</item>
|
||
/// </list>
|
||
///
|
||
/// <para><b>Impact of wrong value:</b> Using 64 KB on a network stream adds minor latency for the first chunk.
|
||
/// Using 4 KB for a memory-backed writer causes ~16× more Grow() calls than necessary (2048 vs 128 for 8 MB).
|
||
/// The default (64 KB) is the safe choice — suboptimal on network streams but never catastrophic.</para>
|
||
///
|
||
/// Default: 65536 (64 KB)
|
||
/// </summary>
|
||
public int BufferWriterChunkSize { get; init; } = 65536;
|
||
|
||
/// <summary>
|
||
/// Optional property-level filter invoked before metadata registration and serialization.
|
||
/// Return false to exclude the property from the payload.
|
||
/// </summary>
|
||
public BinaryPropertyFilter? PropertyFilter { get; init; }
|
||
|
||
/// <summary>
|
||
/// When true, PopulateMerge will remove items from destination collections
|
||
/// that have no matching Id in the source data.
|
||
/// Only applies to IId collections during merge operations.
|
||
/// Default: false (orphaned items are kept)
|
||
/// </summary>
|
||
public bool RemoveOrphanedItems { get; init; } = false;
|
||
|
||
/// <summary>
|
||
/// Controls LZ4 compression for serialized data.
|
||
/// None: No compression (default, fastest).
|
||
/// Block: Compresses entire payload as single block (better compression ratio).
|
||
/// BlockArray: Compresses in 64KB chunks (streaming-friendly, lower memory).
|
||
/// Note: Both modes are WASM-compatible (pure managed implementation).
|
||
/// Default: None
|
||
/// </summary>
|
||
public Lz4CompressionMode UseCompression { get; set; } = Lz4CompressionMode.None;
|
||
|
||
/// <summary>
|
||
/// Creates options with specified max depth.
|
||
/// </summary>
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
public static AcBinarySerializerOptions WithMaxDepth(byte maxDepth) => new() { MaxDepth = maxDepth };
|
||
|
||
/// <summary>
|
||
/// Creates options without reference handling (and string interning disabled for speed).
|
||
/// </summary>
|
||
public static AcBinarySerializerOptions WithoutReferenceHandling => new()
|
||
{
|
||
ReferenceHandling = ReferenceHandlingMode.None,
|
||
};
|
||
|
||
/// <summary>
|
||
/// Creates options without metadata (faster but less flexible).
|
||
/// </summary>
|
||
public static AcBinarySerializerOptions WithoutMetadata => new() { UseMetadata = false };
|
||
} |