diff --git a/DBCD.Benchmark/Benchmarks/StringTableBench.cs b/DBCD.Benchmark/Benchmarks/StringTableBench.cs index ad2b760..ffaa1be 100644 --- a/DBCD.Benchmark/Benchmarks/StringTableBench.cs +++ b/DBCD.Benchmark/Benchmarks/StringTableBench.cs @@ -1,4 +1,5 @@ -using BenchmarkDotNet.Attributes; +using System.Buffers; +using BenchmarkDotNet.Attributes; using System.Text; namespace DBCD.Benchmark.Benchmarks @@ -50,27 +51,48 @@ public static string ReadCString(this BinaryReader reader) public static Dictionary ReadStringTable(this BinaryReader reader, int stringTableSize, int baseOffset = 0, bool usePos = false) { - var StringTable = new Dictionary(stringTableSize / 0x20); - if (stringTableSize == 0) - return StringTable; + return []; + + var stringTable = new Dictionary(stringTableSize / 0x20); + + byte[] stringTableBytes = ArrayPool.Shared.Rent(stringTableSize); // may return a lager buffer than requested + Span bufferSpan = stringTableBytes.AsSpan(0, stringTableSize); + _ = reader.Read(bufferSpan); - var curOfs = 0; - var decoded = Encoding.UTF8.GetString(reader.ReadBytes(stringTableSize)); - foreach (var str in decoded.Split('\0')) + try { - if (curOfs == stringTableSize) - break; + int start = 0; + for (int i = 0; i < bufferSpan.Length; ++i) + { + if (stringTableBytes[i] == 0) + { + string str = Encoding.UTF8.GetString(bufferSpan.Slice(start, i - start)); + if (usePos) + stringTable[reader.BaseStream.Position - stringTableSize + start] = str; + else + stringTable[baseOffset + start] = str; - if (usePos) - StringTable[(reader.BaseStream.Position - stringTableSize) + curOfs] = str; - else - StringTable[baseOffset + curOfs] = str; + start = i + 1; + } + } - curOfs += Encoding.UTF8.GetByteCount(str) + 1; - } + // Trailing string + if (start < bufferSpan.Length) + { + string str = Encoding.UTF8.GetString(bufferSpan.Slice(start)); + if (usePos) + stringTable[reader.BaseStream.Position - stringTableSize + start] = str; + else + stringTable[baseOffset + start] = str; + } - return StringTable; + return stringTable; + } + finally + { + ArrayPool.Shared.Return(stringTableBytes); + } } } } diff --git a/DBCD.IO/Extensions.cs b/DBCD.IO/Extensions.cs index b26a65c..b7661ed 100644 --- a/DBCD.IO/Extensions.cs +++ b/DBCD.IO/Extensions.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.IO; @@ -6,6 +7,7 @@ using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; namespace DBCD.IO @@ -50,11 +52,11 @@ public static FieldCache[] ToFieldCache(this Type type) public static T Read(this BinaryReader reader) where T : struct { - byte[] result = reader.ReadBytes(Unsafe.SizeOf()); + Span result = stackalloc byte[Unsafe.SizeOf()]; + _ = reader.Read(result); return Unsafe.ReadUnaligned(ref result[0]); } - /// /// Reads a NUL-separated string table from the current stream /// @@ -64,45 +66,82 @@ public static T Read(this BinaryReader reader) where T : struct /// Base offset to use for the string table keys public static Dictionary ReadStringTable(this BinaryReader reader, int stringTableSize, int baseOffset = 0, bool usePos = false) { - var StringTable = new Dictionary(stringTableSize / 0x20); - - if(stringTableSize == 0) - return StringTable; + if (stringTableSize == 0) + return []; - var curOfs = 0; - var decoded = Encoding.UTF8.GetString(reader.ReadBytes(stringTableSize)); - foreach (var str in decoded.Split('\0')) - { - if (curOfs == stringTableSize) - break; + var stringTable = new Dictionary(stringTableSize / 0x20); - if(usePos) - StringTable[(reader.BaseStream.Position - stringTableSize) + curOfs] = str; - else - StringTable[baseOffset + curOfs] = str; + byte[] stringTableBytes = ArrayPool.Shared.Rent(stringTableSize); // may return a lager buffer than requested + Span bufferSpan = stringTableBytes.AsSpan(0, stringTableSize); + _ = reader.Read(bufferSpan); - curOfs += Encoding.UTF8.GetByteCount(str) + 1; + try + { + int start = 0; + for (int i = 0; i < bufferSpan.Length; ++i) + { + if (stringTableBytes[i] == 0) + { + string str = Encoding.UTF8.GetString(bufferSpan.Slice(start, i - start)); + if (usePos) + stringTable[reader.BaseStream.Position - stringTableSize + start] = str; + else + stringTable[baseOffset + start] = str; + + start = i + 1; + } + } + + // Trailing string + if (start < bufferSpan.Length) + { + string str = Encoding.UTF8.GetString(bufferSpan.Slice(start)); + if (usePos) + stringTable[reader.BaseStream.Position - stringTableSize + start] = str; + else + stringTable[baseOffset + start] = str; + } + + return stringTable; + } + finally + { + ArrayPool.Shared.Return(stringTableBytes); } - - return StringTable; } public static T[] ReadArray(this BinaryReader reader) where T : struct { int numBytes = (int)reader.ReadInt64(); + byte[] buffer = ArrayPool.Shared.Rent(numBytes); // may return a lager buffer than requested + Span result = buffer.AsSpan(0, numBytes); - byte[] result = reader.ReadBytes(numBytes); - - reader.BaseStream.Position += (0 - numBytes) & 0x07; - return result.CopyTo(); + try + { + _ = reader.Read(result); + reader.BaseStream.Position += (0 - numBytes) & 0x07; + return MemoryMarshal.Cast(result).ToArray(); + } + finally + { + ArrayPool.Shared.Return(buffer); + } } public static T[] ReadArray(this BinaryReader reader, int size) where T : struct { int numBytes = Unsafe.SizeOf() * size; - - byte[] result = reader.ReadBytes(numBytes); - return result.CopyTo(); + byte[] buffer = ArrayPool.Shared.Rent(numBytes); // may return a lager buffer than requested + Span result = buffer.AsSpan(0, numBytes); + try + { + _ = reader.Read(result); + return MemoryMarshal.Cast(result).ToArray(); + } + finally + { + ArrayPool.Shared.Return(buffer); + } } public static unsafe T[] CopyTo(this byte[] src) where T : struct @@ -171,7 +210,7 @@ public static string ReadCString(this BinaryReader reader) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string ReadCString(this BinaryReader reader, Encoding encoding) { - var bytes = new System.Collections.Generic.List(0x20); + var bytes = new List(0x20); byte b; while ((b = reader.ReadByte()) != 0) bytes.Add(b);