Enable FastWire mode for binary string serialization

Activate FastWire encoding for both serialization and deserialization, using UTF-16 and fixed-width lengths for strings. WireMode is now public and defaults to FastWire. Removes unused ObjectEnd marker, clarifying object end handling. This improves string (de)serialization speed at the cost of larger output.
This commit is contained in:
Loretta 2026-03-03 13:53:58 +01:00
parent a1a2a90ef7
commit bbed1caf44
6 changed files with 38 additions and 38 deletions

View File

@ -330,15 +330,15 @@ public static partial class AcBinaryDeserializer
}
// FastWire: length is char count, data is UTF-16 (2 bytes per char)
//if (FastWire)
//{
// var byteLen = length * 2;
// EnsureAvailable(byteLen);
// var chars = MemoryMarshal.Cast<byte, char>(_buffer.AsSpan(_position, byteLen));
// var value = new string(chars);
// _position += byteLen;
// return value;
//}
if (FastWire)
{
var byteLen = length * 2;
EnsureAvailable(byteLen);
var chars = MemoryMarshal.Cast<byte, char>(_buffer.AsSpan(_position, byteLen));
var value = new string(chars);
_position += byteLen;
return value;
}
EnsureAvailable(length);

View File

@ -63,7 +63,7 @@ public static partial class AcBinaryDeserializer
public bool HasMetadata;
public bool IsMergeMode;
public bool RemoveOrphanedItems;
//public bool FastWire;
public bool FastWire;
// Options-derived properties
public byte MinStringInternLength => Options.MinStringInternLength;
@ -139,7 +139,7 @@ public static partial class AcBinaryDeserializer
HasMetadata = false;
IsMergeMode = false;
RemoveOrphanedItems = false;
//FastWire = Options.WireMode == WireMode.Fast;
FastWire = Options.WireMode == WireMode.Fast;
ChainTracker = null;
}

View File

@ -242,7 +242,7 @@ public static partial class AcBinarySerializer
/// Reference handling is safe because generated code inlines TryConsumeWritePlanEntry for IId types.
/// </summary>
public bool IsDirectObjectWrite => !UseMetadata;
//public bool FastWire { get; private set; }
public bool FastWire { get; private set; }
public byte MinStringInternLength => Options.MinStringInternLength;
public byte MaxStringInternLength => Options.MaxStringInternLength;
public BinaryPropertyFilter? PropertyFilter => Options.PropertyFilter;
@ -281,7 +281,7 @@ public static partial class AcBinarySerializer
HasPropertyFilter = Options.PropertyFilter != null;
InternBit = 1 << (int)Options.UseStringInterning;
HasStringInterning = Options.UseStringInterning != StringInterningMode.None;
//FastWire = Options.WireMode == WireMode.Fast;
FastWire = Options.WireMode == WireMode.Fast;
}
public override void Clear()
@ -630,17 +630,17 @@ public static partial class AcBinarySerializer
public void WriteStringUtf8(string value)
{
//if (FastWire)
//{
// // UTF-16: char count (fixed uint) + raw char data (zero-encoding memcopy)
// var charLen = value.Length;
// var byteLen = charLen * 2;
// WriteRaw(charLen);
// EnsureCapacity(byteLen);
// MemoryMarshal.AsBytes(value.AsSpan()).CopyTo(_buffer.AsSpan(_position, byteLen));
// _position += byteLen;
// return;
//}
if (FastWire)
{
// UTF-16: char count (VarUInt) + raw char data (zero-encoding memcopy)
var charLen = value.Length;
var byteLen = charLen * 2;
WriteVarUInt((uint)charLen);
EnsureCapacity(byteLen);
MemoryMarshal.AsBytes(value.AsSpan()).CopyTo(_buffer.AsSpan(_position, byteLen));
_position += byteLen;
return;
}
var charLength = value.Length;

View File

@ -1023,12 +1023,12 @@ public static partial class AcBinarySerializer
}
// FastWire: skip FixStr optimization (UTF-8 specific), write String marker + UTF-16 data
//if (context.FastWire)
//{
// context.WriteByte(BinaryTypeCode.String);
// context.WriteStringUtf8(value);
// return;
//}
if (context.FastWire)
{
context.WriteByte(BinaryTypeCode.String);
context.WriteStringUtf8(value);
return;
}
// Fast path for short strings: check length first (cheap), then ASCII
// FixStr encodes type+length in single byte for strings <= 31 chars

View File

@ -85,6 +85,13 @@ public sealed class AcBinarySerializerOptions : AcSerializerOptions
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.Fast;
/// <summary>
/// Controls how string interning is applied during serialization.
/// None: No interning, all strings written inline.
@ -94,13 +101,6 @@ public sealed class AcBinarySerializerOptions : AcSerializerOptions
/// </summary>
public StringInterningMode UseStringInterning { get; set; } = StringInterningMode.Attribute;
/// <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>
/// 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.

View File

@ -44,7 +44,7 @@ internal static class BinaryTypeCode
// Complex types (25-31)
public const byte Object = 25; // Start of object (non-tracked OR first occurrence when ref tracking)
public const byte ObjectEnd = 26; // End of object marker
//public const byte ObjectEnd = 26; // UNUSED — property count is known at compile-time (SGen) or reflection-time (runtime), no end marker needed
public const byte ObjectRef = 27; // Reference to previously serialized object (2+ occurrence)
public const byte Array = 28; // Start of array/list
public const byte Dictionary = 29; // Start of dictionary