diff --git a/AyCode.Core.Serializers.Console/Program.cs b/AyCode.Core.Serializers.Console/Program.cs
index 5a600a6..5fbc82b 100644
--- a/AyCode.Core.Serializers.Console/Program.cs
+++ b/AyCode.Core.Serializers.Console/Program.cs
@@ -43,7 +43,7 @@ public static class Program
private static readonly UTF8Encoding Utf8NoBom = new(encoderShouldEmitUTF8Identifier: false);
- private static int WarmupIterations = 10;
+ private static int WarmupIterations = 5;
private static int TestIterations = 1000;
public static void Main(string[] args)
@@ -55,10 +55,17 @@ public static class Program
if (mode == "quick")
{
- WarmupIterations = 10;
+ WarmupIterations = 5;
TestIterations = 100;
mode = "all";
}
+
+ // Profiler mode: warmup only, then exit (for memory profiler analysis)
+ if (mode == "profiler")
+ {
+ RunProfilerMode();
+ return;
+ }
System.Console.WriteLine("╔══════════════════════════════════════════════════════════════════════╗");
System.Console.WriteLine("║ COMPREHENSIVE SERIALIZER BENCHMARK SUITE ║");
@@ -90,6 +97,47 @@ public static class Program
System.Console.WriteLine("\n✓ Benchmark complete!");
}
+
+ ///
+ /// Profiler mode: warmup only, then EXIT immediately.
+ /// Usage: dotnet run -- profiler
+ ///
+ private static void RunProfilerMode()
+ {
+ System.Console.WriteLine("╔══════════════════════════════════════════════════════════════════════╗");
+ System.Console.WriteLine("║ PROFILER MODE (AcBinary only) ║");
+ System.Console.WriteLine("╚══════════════════════════════════════════════════════════════════════╝");
+ System.Console.WriteLine($"Build: {BuildConfiguration} | .NET: {Environment.Version}");
+ System.Console.WriteLine();
+
+ // Create medium test data
+ TestDataFactory.ResetIdCounter();
+ var sharedTag = TestDataFactory.CreateTag("SharedTag");
+ var sharedUser = TestDataFactory.CreateUser("shareduser");
+ var order = TestDataFactory.CreateOrder(
+ itemCount: 3,
+ palletsPerItem: 3,
+ measurementsPerPallet: 3,
+ pointsPerMeasurement: 4,
+ sharedTag: sharedTag,
+ sharedUser: sharedUser);
+
+ var options = AcBinarySerializerOptions.WithoutReferenceHandling();
+
+ // Warmup (fills caches)
+ System.Console.WriteLine("Warming up (10 iterations)...");
+ for (var i = 0; i < 10; i++)
+ {
+ _ = AcBinarySerializer.Serialize(order, options);
+ }
+ System.Console.WriteLine("Warmup complete. Caches are now populated.");
+ System.Console.WriteLine();
+ System.Console.WriteLine(">>> ATTACH MEMORY PROFILER NOW <<<");
+ System.Console.WriteLine("Press any key to exit...");
+ System.Console.ReadKey(intercept: true);
+ System.Console.WriteLine();
+ System.Console.WriteLine("✓ Profiler mode complete. Exiting now.");
+ }
#region Test Data Creation
@@ -766,28 +814,49 @@ public static class Program
System.Console.WriteLine($"{"Fastest Round-trip",-20} │ {fastestRt.Name,-25} │ {fastestRt.AvgTime,15:F2} ms");
// Overall AcBinary Default vs MessagePack comparison
- var msgPackAvgSer = results.Where(r => r.SerializerName == SerializerMessagePack && r.SerializeTimeMs > 0).Average(r => r.SerializeTimeMs);
- var msgPackAvgDes = results.Where(r => r.SerializerName == SerializerMessagePack && r.DeserializeTimeMs > 0).Average(r => r.DeserializeTimeMs);
- var msgPackAvgRt = results.Where(r => r.SerializerName == SerializerMessagePack && r.RoundTripTimeMs > 0).Average(r => r.RoundTripTimeMs);
+ var msgPackSerResults = results.Where(r => r.SerializerName == SerializerMessagePack && r.SerializeTimeMs > 0).ToList();
+ var msgPackDesResults = results.Where(r => r.SerializerName == SerializerMessagePack && r.DeserializeTimeMs > 0).ToList();
+ var msgPackRtResults = results.Where(r => r.SerializerName == SerializerMessagePack && r.RoundTripTimeMs > 0).ToList();
+
+ var acBinarySerResults = results.Where(r => r.SerializerName == SerializerAcBinaryDefault && r.SerializeTimeMs > 0).ToList();
+ var acBinaryDesResults = results.Where(r => r.SerializerName == SerializerAcBinaryDefault && r.DeserializeTimeMs > 0).ToList();
+ var acBinaryRtResults = results.Where(r => r.SerializerName == SerializerAcBinaryDefault && r.RoundTripTimeMs > 0).ToList();
+
+ // Skip comparison if no data available
+ if (msgPackRtResults.Count == 0 || acBinaryRtResults.Count == 0)
+ {
+ System.Console.WriteLine();
+ System.Console.WriteLine($"── {SerializerAcBinaryDefault} vs {SerializerMessagePack} (Overall) ──");
+ System.Console.WriteLine(" (Comparison requires both serialize and deserialize data)");
+ return;
+ }
+
+ var msgPackAvgSer = msgPackSerResults.Count > 0 ? msgPackSerResults.Average(r => r.SerializeTimeMs) : 0;
+ var msgPackAvgDes = msgPackDesResults.Average(r => r.DeserializeTimeMs);
+ var msgPackAvgRt = msgPackRtResults.Average(r => r.RoundTripTimeMs);
var msgPackAvgSize = results.Where(r => r.SerializerName == SerializerMessagePack).Average(r => r.SerializedSize);
- var acBinaryAvgSer = results.Where(r => r.SerializerName == SerializerAcBinaryDefault && r.SerializeTimeMs > 0).Average(r => r.SerializeTimeMs);
- var acBinaryAvgDes = results.Where(r => r.SerializerName == SerializerAcBinaryDefault && r.DeserializeTimeMs > 0).Average(r => r.DeserializeTimeMs);
- var acBinaryAvgRt = results.Where(r => r.SerializerName == SerializerAcBinaryDefault && r.RoundTripTimeMs > 0).Average(r => r.RoundTripTimeMs);
+ var acBinaryAvgSer = acBinarySerResults.Count > 0 ? acBinarySerResults.Average(r => r.SerializeTimeMs) : 0;
+ var acBinaryAvgDes = acBinaryDesResults.Average(r => r.DeserializeTimeMs);
+ var acBinaryAvgRt = acBinaryRtResults.Average(r => r.RoundTripTimeMs);
var acBinaryAvgSize = results.Where(r => r.SerializerName == SerializerAcBinaryDefault).Average(r => r.SerializedSize);
System.Console.WriteLine();
System.Console.WriteLine($"── {SerializerAcBinaryDefault} vs {SerializerMessagePack} (Overall) ──");
- var serPctAll = (acBinaryAvgSer / msgPackAvgSer - 1) * 100;
+ // Only show serialize comparison if data available
+ if (msgPackAvgSer > 0 && acBinaryAvgSer > 0)
+ {
+ var serPctAll = (acBinaryAvgSer / msgPackAvgSer - 1) * 100;
+ System.Console.ForegroundColor = serPctAll <= 0 ? ConsoleColor.Green : ConsoleColor.Red;
+ System.Console.WriteLine($" Serialize: {serPctAll:+0;-0}% ({acBinaryAvgSer:F2} ms vs {msgPackAvgSer:F2} ms)");
+ System.Console.ResetColor();
+ }
+
var desPctAll = (acBinaryAvgDes / msgPackAvgDes - 1) * 100;
var rtPctAll = (acBinaryAvgRt / msgPackAvgRt - 1) * 100;
var sizePctAll = (acBinaryAvgSize / msgPackAvgSize - 1) * 100;
- System.Console.ForegroundColor = serPctAll <= 0 ? ConsoleColor.Green : ConsoleColor.Red;
- System.Console.WriteLine($" Serialize: {serPctAll:+0;-0}% ({acBinaryAvgSer:F2} ms vs {msgPackAvgSer:F2} ms)");
- System.Console.ResetColor();
-
System.Console.ForegroundColor = desPctAll <= 0 ? ConsoleColor.Green : ConsoleColor.Red;
System.Console.WriteLine($" Deserialize: {desPctAll:+0;-0}% ({acBinaryAvgDes:F2} ms vs {msgPackAvgDes:F2} ms)");
System.Console.ResetColor();
@@ -806,9 +875,33 @@ public static class Program
Directory.CreateDirectory(ResultsDirectory);
var timestamp = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
- var fileName = $"Console.FullBenchmark_{BuildConfiguration}_{timestamp}.log";
- var filePath = Path.Combine(ResultsDirectory, fileName);
+ var baseFileName = $"Console.FullBenchmark_{BuildConfiguration}_{timestamp}";
+ var logFilePath = Path.Combine(ResultsDirectory, $"{baseFileName}.log");
+ var outputFilePath = Path.Combine(ResultsDirectory, $"{baseFileName}.output");
+ // Save binary output to separate .output file
+ var largeTestData = testDataSets.FirstOrDefault(t => t.Name.StartsWith("Large"));
+ if (largeTestData != null)
+ {
+ var outputSb = new StringBuilder();
+ outputSb.AppendLine("╔══════════════════════════════════════════════════════════════════════════════════════════════════════╗");
+ outputSb.AppendLine("║ SERIALIZED BINARY OUTPUT ║");
+ outputSb.AppendLine($"║ Generated: {DateTime.Now:yyyy-MM-dd HH:mm:ss}".PadRight(100) + "║");
+ outputSb.AppendLine("╚══════════════════════════════════════════════════════════════════════════════════════════════════════╝");
+ outputSb.AppendLine();
+
+ outputSb.AppendLine("=== SERIALIZED BYTES: Large (5x5x5x10) - AcBinary (Default) ===");
+ var serializedBytes = AcBinarySerializer.Serialize(largeTestData.Order, AcBinarySerializerOptions.Default);
+ outputSb.AppendLine($"Size: {serializedBytes.Length:N0} bytes");
+ outputSb.AppendLine();
+ outputSb.AppendLine("Hex dump:");
+ outputSb.AppendLine(FormatHexDump(serializedBytes));
+
+ File.WriteAllText(outputFilePath, outputSb.ToString(), Utf8NoBom);
+ System.Console.WriteLine($"✓ Binary output saved to: {outputFilePath}");
+ }
+
+ // Save benchmark results to .log file
var sb = new StringBuilder();
sb.AppendLine("╔══════════════════════════════════════════════════════════════════════════════════════════════════════╗");
sb.AppendLine("║ SERIALIZER BENCHMARK RESULTS ║");
@@ -818,25 +911,12 @@ public static class Program
sb.AppendLine("╚══════════════════════════════════════════════════════════════════════════════════════════════════════╝");
sb.AppendLine();
- // Serialized bytes for Large test data (AcBinary Default)
- var largeTestData = testDataSets.FirstOrDefault(t => t.Name.StartsWith("Large"));
- if (largeTestData != null)
- {
- sb.AppendLine("=== SERIALIZED BYTES: Large (5x5x5x10) - AcBinary (Default) ===");
- var serializedBytes = AcBinarySerializer.Serialize(largeTestData.Order, AcBinarySerializerOptions.Default);
- sb.AppendLine($"Size: {serializedBytes.Length:N0} bytes");
- sb.AppendLine();
- sb.AppendLine("Hex dump:");
- sb.AppendLine(FormatHexDump(serializedBytes));
- sb.AppendLine();
- }
-
// CSV-like data for easy import
sb.AppendLine("=== RAW DATA (CSV) ===");
sb.AppendLine("TestData,Serializer,Size,SerializeMs,DeserializeMs,RoundTripMs");
foreach (var testData in testDataSets)
{
- var testResults = results.Where(r => r.TestDataName == testData.Name).ToList();
+ var testResults = results.Where(r => r.TestDataName == testData.DisplayName).ToList();
foreach (var result in testResults)
{
sb.AppendLine($"{result.TestDataName},{result.SerializerName},{result.SerializedSize},{result.SerializeTimeMs:F2},{result.DeserializeTimeMs:F2},{result.RoundTripTimeMs:F2}");
@@ -851,12 +931,12 @@ public static class Program
foreach (var testData in testDataSets)
{
- var testResults = results.Where(r => r.TestDataName == testData.Name).OrderBy(r => r.RoundTripTimeMs).ToList();
+ var testResults = results.Where(r => r.TestDataName == testData.DisplayName).OrderBy(r => r.RoundTripTimeMs).ToList();
var msgPackResult = testResults.FirstOrDefault(r => r.SerializerName == SerializerMessagePack);
var acBinaryResult = testResults.FirstOrDefault(r => r.SerializerName == SerializerAcBinaryDefault);
sb.AppendLine();
- sb.AppendLine($"--- {testData.Name} ---");
+ sb.AppendLine($"--- {testData.DisplayName} ---");
sb.AppendLine($"{"#",-4} {"Serializer",-26} {"Size",-12} {"Serialize",-14} {"Deserialize",-14} {"Round-trip",-14}");
sb.AppendLine(new string('-', 86));
@@ -890,23 +970,41 @@ public static class Program
sb.AppendLine();
sb.AppendLine($"=== {SerializerAcBinaryDefault} vs {SerializerMessagePack} (Overall) ===");
- var msgPackAvgSer = results.Where(r => r.SerializerName == SerializerMessagePack && r.SerializeTimeMs > 0).Average(r => r.SerializeTimeMs);
- var msgPackAvgDes = results.Where(r => r.SerializerName == SerializerMessagePack && r.DeserializeTimeMs > 0).Average(r => r.DeserializeTimeMs);
- var msgPackAvgRt = results.Where(r => r.SerializerName == SerializerMessagePack && r.RoundTripTimeMs > 0).Average(r => r.RoundTripTimeMs);
- var msgPackAvgSize = results.Where(r => r.SerializerName == SerializerMessagePack).Average(r => r.SerializedSize);
+ var msgPackSerResults2 = results.Where(r => r.SerializerName == SerializerMessagePack && r.SerializeTimeMs > 0).ToList();
+ var msgPackDesResults2 = results.Where(r => r.SerializerName == SerializerMessagePack && r.DeserializeTimeMs > 0).ToList();
+ var msgPackRtResults2 = results.Where(r => r.SerializerName == SerializerMessagePack && r.RoundTripTimeMs > 0).ToList();
- var acBinaryAvgSer = results.Where(r => r.SerializerName == SerializerAcBinaryDefault && r.SerializeTimeMs > 0).Average(r => r.SerializeTimeMs);
- var acBinaryAvgDes = results.Where(r => r.SerializerName == SerializerAcBinaryDefault && r.DeserializeTimeMs > 0).Average(r => r.DeserializeTimeMs);
- var acBinaryAvgRt = results.Where(r => r.SerializerName == SerializerAcBinaryDefault && r.RoundTripTimeMs > 0).Average(r => r.RoundTripTimeMs);
- var acBinaryAvgSize = results.Where(r => r.SerializerName == SerializerAcBinaryDefault).Average(r => r.SerializedSize);
+ var acBinarySerResults2 = results.Where(r => r.SerializerName == SerializerAcBinaryDefault && r.SerializeTimeMs > 0).ToList();
+ var acBinaryDesResults2 = results.Where(r => r.SerializerName == SerializerAcBinaryDefault && r.DeserializeTimeMs > 0).ToList();
+ var acBinaryRtResults2 = results.Where(r => r.SerializerName == SerializerAcBinaryDefault && r.RoundTripTimeMs > 0).ToList();
- sb.AppendLine($" Serialize: {((acBinaryAvgSer / msgPackAvgSer - 1) * 100):+0;-0}% ({acBinaryAvgSer:F2} ms vs {msgPackAvgSer:F2} ms)");
- sb.AppendLine($" Deserialize: {((acBinaryAvgDes / msgPackAvgDes - 1) * 100):+0;-0}% ({acBinaryAvgDes:F2} ms vs {msgPackAvgDes:F2} ms)");
- sb.AppendLine($" Round-trip: {((acBinaryAvgRt / msgPackAvgRt - 1) * 100):+0;-0}% ({acBinaryAvgRt:F2} ms vs {msgPackAvgRt:F2} ms)");
- sb.AppendLine($" Size: {((acBinaryAvgSize / msgPackAvgSize - 1) * 100):+0;-0}% ({acBinaryAvgSize:F0} B vs {msgPackAvgSize:F0} B)");
+ if (msgPackSerResults2.Count > 0 && acBinarySerResults2.Count > 0)
+ {
+ var msgPackAvgSer2 = msgPackSerResults2.Average(r => r.SerializeTimeMs);
+ var acBinaryAvgSer2 = acBinarySerResults2.Average(r => r.SerializeTimeMs);
+ sb.AppendLine($" Serialize: {((acBinaryAvgSer2 / msgPackAvgSer2 - 1) * 100):+0;-0}% ({acBinaryAvgSer2:F2} ms vs {msgPackAvgSer2:F2} ms)");
+ }
+
+ if (msgPackDesResults2.Count > 0 && acBinaryDesResults2.Count > 0)
+ {
+ var msgPackAvgDes2 = msgPackDesResults2.Average(r => r.DeserializeTimeMs);
+ var acBinaryAvgDes2 = acBinaryDesResults2.Average(r => r.DeserializeTimeMs);
+ sb.AppendLine($" Deserialize: {((acBinaryAvgDes2 / msgPackAvgDes2 - 1) * 100):+0;-0}% ({acBinaryAvgDes2:F2} ms vs {msgPackAvgDes2:F2} ms)");
+ }
+
+ if (msgPackRtResults2.Count > 0 && acBinaryRtResults2.Count > 0)
+ {
+ var msgPackAvgRt2 = msgPackRtResults2.Average(r => r.RoundTripTimeMs);
+ var acBinaryAvgRt2 = acBinaryRtResults2.Average(r => r.RoundTripTimeMs);
+ sb.AppendLine($" Round-trip: {((acBinaryAvgRt2 / msgPackAvgRt2 - 1) * 100):+0;-0}% ({acBinaryAvgRt2:F2} ms vs {msgPackAvgRt2:F2} ms)");
+ }
+
+ var msgPackAvgSize2 = results.Where(r => r.SerializerName == SerializerMessagePack).Average(r => r.SerializedSize);
+ var acBinaryAvgSize2 = results.Where(r => r.SerializerName == SerializerAcBinaryDefault).Average(r => r.SerializedSize);
+ sb.AppendLine($" Size: {((acBinaryAvgSize2 / msgPackAvgSize2 - 1) * 100):+0;-0}% ({acBinaryAvgSize2:F0} B vs {msgPackAvgSize2:F0} B)");
- File.WriteAllText(filePath, sb.ToString(), Utf8NoBom);
- System.Console.WriteLine($"\n✓ Results saved to: {filePath}");
+ File.WriteAllText(logFilePath, sb.ToString(), Utf8NoBom);
+ System.Console.WriteLine($"✓ Results saved to: {logFilePath}");
}
///
diff --git a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializationContext.cs b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializationContext.cs
index 3bec675..558abf8 100644
--- a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializationContext.cs
+++ b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializationContext.cs
@@ -67,6 +67,11 @@ public static partial class AcBinarySerializer
private Dictionary? _internedStrings;
private List? _internedStringList;
+ // Single contiguous buffer for all interned string UTF8 bytes (reused across serializations)
+ private byte[]? _internedStringBuffer;
+ private int _internedStringBufferPos;
+ private List? _internedStringLengths;
+
private Dictionary? _propertyNames;
private List? _propertyNameList;
private int[]? _propertyIndexBuffer;
@@ -125,7 +130,10 @@ public static partial class AcBinarySerializer
_propertyNameList?.Clear();
_internedStringList?.Clear();
- _internedStringUtf8?.Clear();
+ _internedStringLengths?.Clear();
+
+ // Reset intern buffer position (no deallocation - buffer is reused!)
+ _internedStringBufferPos = 0;
// Reset cached property indices
ResetCachedPropertyIndices();
@@ -170,25 +178,23 @@ public static partial class AcBinarySerializer
ArrayPool.Shared.Return(_propertyStateBuffer);
_propertyStateBuffer = null;
}
+
+ // _internedStringBuffer is a simple byte[] - no pool return needed, GC handles it
+ _internedStringBuffer = null;
}
#region String Interning
- ///
- /// Cached UTF8 bytes for interned strings to avoid re-encoding in FinalizeHeaderSections.
- ///
- private List? _internedStringUtf8;
-
///
/// Registers a string for interning. Returns the index of the string.
- /// Uses CollectionsMarshal.GetValueRefOrAddDefault for single-operation lookup+add.
+ /// Uses single contiguous buffer for UTF8 bytes to minimize allocations.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int RegisterInternedString(string value)
{
_internedStrings ??= new Dictionary(InitialInternCapacity, StringComparer.Ordinal);
_internedStringList ??= new List(InitialInternCapacity);
- _internedStringUtf8 ??= new List(InitialInternCapacity);
+ _internedStringLengths ??= new List(InitialInternCapacity);
// Single operation: lookup + conditional add
ref var index = ref CollectionsMarshal.GetValueRefOrAddDefault(_internedStrings, value, out var exists);
@@ -197,28 +203,57 @@ public static partial class AcBinarySerializer
return index;
}
- // New string - add to lists
+ // New string - add to list and write UTF8 to buffer
index = _internedStringList.Count;
_internedStringList.Add(value);
- _internedStringUtf8.Add(GetUtf8BytesCached(value));
+
+ // Calculate UTF8 byte length
+ var utf8Length = Ascii.IsValid(value) ? value.Length : Utf8NoBom.GetByteCount(value);
+
+ // Ensure intern buffer has capacity
+ EnsureInternBufferCapacity(utf8Length);
+
+ // Write UTF8 bytes to contiguous buffer
+ if (Ascii.IsValid(value))
+ {
+ Ascii.FromUtf16(value.AsSpan(), _internedStringBuffer.AsSpan(_internedStringBufferPos, utf8Length), out _);
+ }
+ else
+ {
+ Utf8NoBom.GetBytes(value.AsSpan(), _internedStringBuffer.AsSpan(_internedStringBufferPos, utf8Length));
+ }
+
+ _internedStringLengths.Add(utf8Length);
+ _internedStringBufferPos += utf8Length;
+
return index;
}
///
- /// Get UTF8 bytes for a string, optimized for ASCII strings.
+ /// Ensures the intern buffer has enough capacity for additional bytes.
+ /// Initial size is calculated from MaxStringInternLength * InitialInternCapacity.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static byte[] GetUtf8BytesCached(string value)
+ private void EnsureInternBufferCapacity(int additionalBytes)
{
- // Fast path for ASCII strings - direct char to byte conversion
- if (Ascii.IsValid(value))
+ var required = _internedStringBufferPos + additionalBytes;
+
+ if (_internedStringBuffer == null)
{
- var bytes = new byte[value.Length];
- Ascii.FromUtf16(value.AsSpan(), bytes, out _);
- return bytes;
+ // Initial size: MaxStringInternLength * InitialInternCapacity (e.g., 64 * 32 = 2048)
+ var initialSize = MaxStringInternLength * InitialInternCapacity;
+ _internedStringBuffer = new byte[Math.Max(initialSize, required)];
+ return;
}
- // Standard path for multi-byte UTF8
- return Utf8NoBom.GetBytes(value);
+
+ if (required <= _internedStringBuffer.Length)
+ {
+ return;
+ }
+
+ // Grow buffer (double size)
+ var newSize = Math.Max(_internedStringBuffer.Length * 2, required);
+ Array.Resize(ref _internedStringBuffer, newSize);
}
#endregion
@@ -989,19 +1024,21 @@ public static partial class AcBinarySerializer
///
/// Writes interned strings to the footer (end of stream).
- /// No shifting or estimation needed - just append.
+ /// Uses contiguous buffer - no re-encoding needed.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WriteFooterStrings()
{
WriteVarUInt((uint)_internedStringList!.Count);
- // Use cached UTF8 bytes - no re-encoding needed!
- for (var i = 0; i < _internedStringUtf8!.Count; i++)
+ // Write from contiguous buffer using stored lengths
+ var offset = 0;
+ for (var i = 0; i < _internedStringLengths!.Count; i++)
{
- var utf8Bytes = _internedStringUtf8[i];
- WriteVarUInt((uint)utf8Bytes.Length);
- WriteBytes(utf8Bytes);
+ var length = _internedStringLengths[i];
+ WriteVarUInt((uint)length);
+ WriteBytes(_internedStringBuffer.AsSpan(offset, length));
+ offset += length;
}
}
diff --git a/AyCode.Core/Serializers/Binaries/AcBinarySerializerOptions.cs b/AyCode.Core/Serializers/Binaries/AcBinarySerializerOptions.cs
index d8238cb..e5b41d7 100644
--- a/AyCode.Core/Serializers/Binaries/AcBinarySerializerOptions.cs
+++ b/AyCode.Core/Serializers/Binaries/AcBinarySerializerOptions.cs
@@ -135,10 +135,13 @@ public sealed class AcBinarySerializerOptions : AcSerializerOptions
public static AcBinarySerializerOptions WithMaxDepth(byte maxDepth) => new() { MaxDepth = maxDepth };
///
- /// Creates options without reference handling.
+ /// Creates options without reference handling (and string interning disabled for speed).
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static AcBinarySerializerOptions WithoutReferenceHandling() => new() { ReferenceHandling = ReferenceHandlingMode.None };
+ public static AcBinarySerializerOptions WithoutReferenceHandling() => new()
+ {
+ ReferenceHandling = ReferenceHandlingMode.None,
+ };
///
/// Creates options without metadata (faster but less flexible).