From 48d45602888e66f813b9dc00df448ec1147bef31 Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Sun, 15 Sep 2024 23:07:14 +0300 Subject: [PATCH] Optimize reading binary strings Fixes #98 --- .../KeyValues1/KV1BinaryReader.cs | 44 +++++++++++++++---- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/ValveKeyValue/ValveKeyValue/Deserialization/KeyValues1/KV1BinaryReader.cs b/ValveKeyValue/ValveKeyValue/Deserialization/KeyValues1/KV1BinaryReader.cs index d987ca9..7f367af 100644 --- a/ValveKeyValue/ValveKeyValue/Deserialization/KeyValues1/KV1BinaryReader.cs +++ b/ValveKeyValue/ValveKeyValue/Deserialization/KeyValues1/KV1BinaryReader.cs @@ -1,3 +1,4 @@ +using System.Buffers; using System.Text; using ValveKeyValue.Abstraction; using ValveKeyValue.KeyValues1; @@ -84,7 +85,7 @@ string ReadKeyForNextValue() return stringTable[index]; } - return Encoding.UTF8.GetString(ReadNullTerminatedBytes()); + return ReadNullTerminatedUtf8String(); } void ReadValue(KV1BinaryNodeType type) @@ -102,7 +103,7 @@ void ReadValue(KV1BinaryNodeType type) case KV1BinaryNodeType.String: // UTF8 encoding is used for string values - value = new KVObjectValue(Encoding.UTF8.GetString(ReadNullTerminatedBytes()), KVValueType.String); + value = new KVObjectValue(ReadNullTerminatedUtf8String(), KVValueType.String); break; case KV1BinaryNodeType.WideString: @@ -137,16 +138,41 @@ void ReadValue(KV1BinaryNodeType type) listener.OnKeyValuePair(name, value); } - byte[] ReadNullTerminatedBytes() + string ReadNullTerminatedUtf8String() { - using var mem = new MemoryStream(); - byte nextByte; - while ((nextByte = reader.ReadByte()) != 0) + var buffer = ArrayPool.Shared.Rent(32); + + try { - mem.WriteByte(nextByte); + var position = 0; + + do + { + var b = reader.ReadByte(); + + if (b <= 0) // null byte or stream ended + { + break; + } + + if (position >= buffer.Length) + { + var newBuffer = ArrayPool.Shared.Rent(buffer.Length * 2); + Buffer.BlockCopy(buffer, 0, newBuffer, 0, buffer.Length); + ArrayPool.Shared.Return(buffer); + buffer = newBuffer; + } + + buffer[position++] = b; + } + while (true); + + return Encoding.UTF8.GetString(buffer[..position]); + } + finally + { + ArrayPool.Shared.Return(buffer); } - - return mem.ToArray(); } void DetectMagicHeader()