From 2ae762904f94cd7880301e12f8c5db753dbe56b2 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Tue, 16 Aug 2022 08:59:00 -0700 Subject: [PATCH] fix: Fixes serializing strings and uses memory mapped files for loading (#1133) --- .../Server/Serialization/AdhocPersistence.cs | 103 ++++-- .../Server/Serialization/BinaryFileReader.cs | 170 ++++++---- Projects/Server/Serialization/BufferReader.cs | 318 +++++++++--------- .../Serialization/GenericPersistence.cs | 86 ++--- Projects/Server/World/EntityPersistence.cs | 10 +- 5 files changed, 363 insertions(+), 324 deletions(-) diff --git a/Projects/Server/Serialization/AdhocPersistence.cs b/Projects/Server/Serialization/AdhocPersistence.cs index 030c18925..83fdc590d 100644 --- a/Projects/Server/Serialization/AdhocPersistence.cs +++ b/Projects/Server/Serialization/AdhocPersistence.cs @@ -15,44 +15,91 @@ using System; using System.IO; +using System.IO.MemoryMappedFiles; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; -namespace Server +namespace Server; + +public static class AdhocPersistence { - public static class AdhocPersistence + /** + * Serializes to memory synchronously. Optional buffer can be provided. + * Note: The buffer may not be the same after returning from the function if more data is written + * than the initial buffer can handle. + */ + public static BufferWriter Serialize(Action serializer) { - public static void Serialize(string filePath, Action serializer) - { - var fullPath = Path.Combine(Core.BaseDirectory, filePath); - var file = new FileInfo(fullPath); - file.Directory?.Create(); + var saveBuffer = new BufferWriter(true); + serializer(saveBuffer); + return saveBuffer; + } - using var bin = new BinaryFileWriter(fullPath, true); - serializer(bin); + /** + * Writes a buffer to disk. This function should be called asynchronously. + */ + public static void WriteSnapshot(string filePath, Span buffer) + { + var fullPath = PathUtility.GetFullPath(filePath, Core.BaseDirectory); + var file = new FileInfo(fullPath); + PathUtility.EnsureDirectory(file.DirectoryName); + + using var fs = new FileStream(fullPath, FileMode.Create, FileAccess.Write); + fs.Write(buffer); + } + + public static void SerializeAndSnapshot(string filePath, Action serializer) + { + var saveBuffer = Serialize(serializer); + Task.Run(() => { WriteSnapshot(filePath, saveBuffer.Buffer.AsSpan(0, (int)saveBuffer.Position)); }); + } + + public static void Deserialize(string filePath, Action deserializer) + { + var fullPath = PathUtility.GetFullPath(filePath, Core.BaseDirectory); + var file = new FileInfo(fullPath); + + if (!file.Exists) + { + return; } - public static void Deserialize(string filePath, Action deserializer) + var fileLength = file.Length; + if (fileLength == 0) { - var fullPath = Path.Combine(Core.BaseDirectory, filePath); - var file = new FileInfo(fullPath); - file.Directory?.Create(); + return; + } - if (!file.Exists) - { - return; - } + string error; - try + try + { + using var mmf = MemoryMappedFile.CreateFromFile(fullPath, FileMode.Open); + using var stream = mmf.CreateViewStream(); + using var br = new BinaryFileReader(stream); + deserializer(br); + + error = br.Position != fileLength + ? $"Serialized {fileLength} bytes, but {br.Position} bytes deserialized" + : null; + } + catch (Exception e) + { + error = e.ToString(); + } + + if (error != null) + { + Console.WriteLine($"***** Bad deserialize of {file.FullName} *****"); + Console.WriteLine(error); + + Console.WriteLine("Skip this file and continue? (y/n)"); + + var pressedKey = Console.ReadKey(true).Key; + + if (pressedKey != ConsoleKey.Y) { - using FileStream fs = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.Read); - using var br = new BinaryFileReader(fs); - deserializer(br); - } - catch (Exception e) - { - Utility.PushColor(ConsoleColor.Red); - Console.WriteLine($"***** Bad deserialize of {file.FullName} *****"); - Console.WriteLine(e.ToString()); - Utility.PopColor(); + throw new Exception("Deserialization failed."); } } } diff --git a/Projects/Server/Serialization/BinaryFileReader.cs b/Projects/Server/Serialization/BinaryFileReader.cs index e7ba33642..927acad9f 100644 --- a/Projects/Server/Serialization/BinaryFileReader.cs +++ b/Projects/Server/Serialization/BinaryFileReader.cs @@ -16,82 +16,106 @@ using System; using System.IO; using System.Runtime.CompilerServices; +using System.Text; +using Server.Buffers; using Server.Collections; +using Server.Text; -namespace Server +namespace Server; + +public class BinaryFileReader : IGenericReader, IDisposable { - public class BinaryFileReader : IGenericReader, IDisposable + private BinaryReader _reader; + private Encoding _encoding; + + public BinaryFileReader(BinaryReader br, Encoding encoding = null) { - private readonly BinaryReader _reader; - - public BinaryFileReader(BinaryReader br) => _reader = br; - - public BinaryFileReader(Stream stream) => _reader = new BinaryReader(stream); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Close() => _reader.Close(); - - public DateTime LastSerialized { get; init; } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadString(bool intern = false) - { - var str = _reader.ReadString(); - return intern ? Utility.Intern(str) : str; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public long ReadLong() => _reader.ReadInt64(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ulong ReadULong() => _reader.ReadUInt64(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int ReadInt() => _reader.ReadInt32(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public uint ReadUInt() => _reader.ReadUInt32(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public short ReadShort() => _reader.ReadInt16(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ushort ReadUShort() => _reader.ReadUInt16(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public double ReadDouble() => _reader.ReadDouble(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float ReadFloat() => _reader.ReadSingle(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public byte ReadByte() => _reader.ReadByte(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public sbyte ReadSByte() => _reader.ReadSByte(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool ReadBool() => _reader.ReadBoolean(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Serial ReadSerial() => (Serial)_reader.ReadUInt32(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Read(Span buffer) => _reader.Read(buffer); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public BitArray ReadBitArray() - { - var length = ((IGenericReader)this).ReadEncodedInt(); - - // BinaryReader doesn't expose a Span slice of the buffer, so we use a custom ctor - return new BitArray(_reader, length); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public long Seek(long offset, SeekOrigin origin) => _reader.BaseStream.Seek(offset, origin); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dispose() => Close(); + _reader = br; + _encoding = encoding ?? TextEncoding.UTF8; } + + public BinaryFileReader(Stream stream, Encoding encoding = null) : this(new BinaryReader(stream), encoding) + { + } + + public long Position => _reader.BaseStream.Position; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Close() => _reader.Close(); + + public DateTime LastSerialized { get; init; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public string ReadString(bool intern = false) + { + if (!ReadBool()) + { + return null; + } + + var length = ((IGenericReader)this).ReadEncodedInt(); + if (length <= 0) + { + return intern ? Utility.Intern("") : ""; + } + + byte[] buffer = STArrayPool.Shared.Rent(length); + var str = TextEncoding.GetString(buffer.AsSpan(0, length), _encoding); + STArrayPool.Shared.Return(buffer); + return intern ? Utility.Intern(str) : str; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long ReadLong() => _reader.ReadInt64(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong ReadULong() => _reader.ReadUInt64(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReadInt() => _reader.ReadInt32(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public uint ReadUInt() => _reader.ReadUInt32(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public short ReadShort() => _reader.ReadInt16(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ushort ReadUShort() => _reader.ReadUInt16(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double ReadDouble() => _reader.ReadDouble(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float ReadFloat() => _reader.ReadSingle(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte ReadByte() => _reader.ReadByte(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public sbyte ReadSByte() => _reader.ReadSByte(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ReadBool() => _reader.ReadBoolean(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Serial ReadSerial() => (Serial)_reader.ReadUInt32(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Read(Span buffer) => _reader.Read(buffer); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public BitArray ReadBitArray() + { + var length = ((IGenericReader)this).ReadEncodedInt(); + + // BinaryReader doesn't expose a Span slice of the buffer, so we use a custom ctor + return new BitArray(_reader, length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long Seek(long offset, SeekOrigin origin) => _reader.BaseStream.Seek(offset, origin); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() => Close(); } diff --git a/Projects/Server/Serialization/BufferReader.cs b/Projects/Server/Serialization/BufferReader.cs index 7ba58bb16..7af7bce55 100644 --- a/Projects/Server/Serialization/BufferReader.cs +++ b/Projects/Server/Serialization/BufferReader.cs @@ -22,186 +22,184 @@ using System.Text; using Server.Collections; using Server.Text; -namespace Server +namespace Server; + +public class BufferReader : IGenericReader { - public class BufferReader : IGenericReader + private Encoding _encoding; + private byte[] _buffer; + private int _position; + + public long Position => _position; + + public BufferReader(byte[] buffer, Encoding encoding = null) { - private readonly Encoding _encoding; - private byte[] _buffer; - private int _position; + _buffer = buffer; + _encoding = encoding ?? TextEncoding.UTF8; + } - public long Position => _position; + public BufferReader(byte[] buffer, DateTime lastSerialized) : this(buffer) => LastSerialized = lastSerialized; - public BufferReader(byte[] buffer, Encoding encoding = null) + public void Reset(byte[] newBuffer, out byte[] oldBuffer) + { + oldBuffer = _buffer; + _buffer = newBuffer; + _position = 0; + } + + public DateTime LastSerialized { get; init; } + + public string ReadString(bool intern = false) + { + if (!ReadBool()) { - _buffer = buffer; - _encoding = encoding ?? TextEncoding.UTF8; + return null; } - public BufferReader(byte[] buffer, DateTime lastSerialized) : this(buffer) => LastSerialized = lastSerialized; - - public void Reset(byte[] newBuffer, out byte[] oldBuffer) + var length = ((IGenericReader)this).ReadEncodedInt(); + if (length <= 0) { - oldBuffer = _buffer; - _buffer = newBuffer; - _position = 0; + return intern ? Utility.Intern("") : ""; } - // Compatible with BinaryReader.ReadString() - public DateTime LastSerialized { get; init; } + var str = TextEncoding.GetString(_buffer.AsSpan(_position, length), _encoding); + _position += length; + return intern ? Utility.Intern(str) : str; + } - public string ReadString(bool intern = false) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long ReadLong() + { + var v = BinaryPrimitives.ReadInt64LittleEndian(_buffer.AsSpan(_position, 8)); + _position += 8; + return v; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong ReadULong() + { + var v = BinaryPrimitives.ReadUInt64LittleEndian(_buffer.AsSpan(_position, 8)); + _position += 8; + return v; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReadInt() + { + var v = BinaryPrimitives.ReadInt32LittleEndian(_buffer.AsSpan(_position, 4)); + _position += 4; + return v; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public uint ReadUInt() + { + var v = BinaryPrimitives.ReadUInt32LittleEndian(_buffer.AsSpan(_position, 4)); + _position += 4; + return v; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public short ReadShort() + { + var v = BinaryPrimitives.ReadInt16LittleEndian(_buffer.AsSpan(_position, 2)); + _position += 2; + return v; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ushort ReadUShort() + { + var v = BinaryPrimitives.ReadUInt16LittleEndian(_buffer.AsSpan(_position, 2)); + _position += 2; + return v; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double ReadDouble() + { + var v = BinaryPrimitives.ReadDoubleLittleEndian(_buffer.AsSpan(_position, 8)); + _position += 8; + return v; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float ReadFloat() + { + var v = BinaryPrimitives.ReadSingleLittleEndian(_buffer.AsSpan(_position, 4)); + _position += 4; + return v; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte ReadByte() => _buffer[_position++]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public sbyte ReadSByte() => (sbyte)_buffer[_position++]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ReadBool() => _buffer[_position++] != 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Serial ReadSerial() => (Serial)ReadUInt(); + + public int Read(Span buffer) + { + var length = buffer.Length; + if (length > _buffer.Length - _position) { - if (!ReadBool()) - { - return null; - } - - var length = ((IGenericReader)this).ReadEncodedInt(); - if (length <= 0) - { - return intern ? Utility.Intern("") : ""; - } - - var str = TextEncoding.GetString(_buffer.AsSpan(_position, length), _encoding); - _position += length; - return intern ? Utility.Intern(str) : str; + throw new OutOfMemoryException(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public long ReadLong() + _buffer.AsSpan(_position, length).CopyTo(buffer); + _position += length; + return length; + } + + public BitArray ReadBitArray() + { + var bitLength = ((IGenericReader)this).ReadEncodedInt(); + var length = BitArray.GetByteArrayLengthFromBitLength(bitLength); + + if (length > _buffer.Length - _position) { - var v = BinaryPrimitives.ReadInt64LittleEndian(_buffer.AsSpan(_position, 8)); - _position += 8; - return v; + throw new OutOfMemoryException(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ulong ReadULong() + var bitArray = new BitArray(_buffer.AsSpan(_position, length), bitLength); + _position += length; + return bitArray; + } + + public virtual long Seek(long offset, SeekOrigin origin) + { + Debug.Assert( + origin != SeekOrigin.End || offset <= 0 && offset > -_buffer.Length, + "Attempting to seek to an invalid position using SeekOrigin.End" + ); + Debug.Assert( + origin != SeekOrigin.Begin || offset >= 0 && offset < _buffer.Length, + "Attempting to seek to an invalid position using SeekOrigin.Begin" + ); + Debug.Assert( + origin != SeekOrigin.Current || _position + offset >= 0 && _position + offset < _buffer.Length, + "Attempting to seek to an invalid position using SeekOrigin.Current" + ); + + var position = Math.Max(0L, origin switch { - var v = BinaryPrimitives.ReadUInt64LittleEndian(_buffer.AsSpan(_position, 8)); - _position += 8; - return v; + SeekOrigin.Current => _position + offset, + SeekOrigin.End => _buffer.Length + offset, + _ => offset // Begin + }); + + if (position > int.MaxValue) + { + throw new ArgumentException($"BufferReader does not support {nameof(offset)} beyond Int32.MaxValue"); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int ReadInt() - { - var v = BinaryPrimitives.ReadInt32LittleEndian(_buffer.AsSpan(_position, 4)); - _position += 4; - return v; - } + _position = (int)position; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public uint ReadUInt() - { - var v = BinaryPrimitives.ReadUInt32LittleEndian(_buffer.AsSpan(_position, 4)); - _position += 4; - return v; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public short ReadShort() - { - var v = BinaryPrimitives.ReadInt16LittleEndian(_buffer.AsSpan(_position, 2)); - _position += 2; - return v; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ushort ReadUShort() - { - var v = BinaryPrimitives.ReadUInt16LittleEndian(_buffer.AsSpan(_position, 2)); - _position += 2; - return v; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public double ReadDouble() - { - var v = BinaryPrimitives.ReadDoubleLittleEndian(_buffer.AsSpan(_position, 8)); - _position += 8; - return v; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float ReadFloat() - { - var v = BinaryPrimitives.ReadSingleLittleEndian(_buffer.AsSpan(_position, 4)); - _position += 4; - return v; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public byte ReadByte() => _buffer[_position++]; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public sbyte ReadSByte() => (sbyte)_buffer[_position++]; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool ReadBool() => _buffer[_position++] != 0; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Serial ReadSerial() => (Serial)ReadUInt(); - - public int Read(Span buffer) - { - var length = buffer.Length; - if (length > _buffer.Length - _position) - { - throw new OutOfMemoryException(); - } - - _buffer.AsSpan(_position, length).CopyTo(buffer); - _position += length; - return length; - } - - public BitArray ReadBitArray() - { - var bitLength = ((IGenericReader)this).ReadEncodedInt(); - var length = BitArray.GetByteArrayLengthFromBitLength(bitLength); - - if (length > _buffer.Length - _position) - { - throw new OutOfMemoryException(); - } - - var bitArray = new BitArray(_buffer.AsSpan(_position, length), bitLength); - _position += length; - return bitArray; - } - - public virtual long Seek(long offset, SeekOrigin origin) - { - Debug.Assert( - origin != SeekOrigin.End || offset <= 0 && offset > -_buffer.Length, - "Attempting to seek to an invalid position using SeekOrigin.End" - ); - Debug.Assert( - origin != SeekOrigin.Begin || offset >= 0 && offset < _buffer.Length, - "Attempting to seek to an invalid position using SeekOrigin.Begin" - ); - Debug.Assert( - origin != SeekOrigin.Current || _position + offset >= 0 && _position + offset < _buffer.Length, - "Attempting to seek to an invalid position using SeekOrigin.Current" - ); - - var position = Math.Max(0L, origin switch - { - SeekOrigin.Current => _position + offset, - SeekOrigin.End => _buffer.Length + offset, - _ => offset // Begin - }); - - if (position > int.MaxValue) - { - throw new ArgumentException($"BufferReader does not support {nameof(offset)} beyond Int32.MaxValue"); - } - - _position = (int)position; - - return _position; - } + return _position; } } diff --git a/Projects/Server/Serialization/GenericPersistence.cs b/Projects/Server/Serialization/GenericPersistence.cs index eb878303c..36909b38c 100644 --- a/Projects/Server/Serialization/GenericPersistence.cs +++ b/Projects/Server/Serialization/GenericPersistence.cs @@ -16,69 +16,37 @@ using System; using System.IO; -namespace Server +namespace Server; + +public static class GenericPersistence { - public static class GenericPersistence + public static void Register( + string name, + Action serializer, + Action deserializer, + int priority = Persistence.DefaultPriority + ) { - public static void Register( - string name, - Action serializer, - Action deserializer, - int priority = Persistence.DefaultPriority - ) + BufferWriter saveBuffer = null; + + void Serialize() { - BufferWriter saveBuffer = null; + saveBuffer ??= new BufferWriter(true); + saveBuffer.Seek(0, SeekOrigin.Begin); - void Serialize() - { - saveBuffer ??= new BufferWriter(true); - saveBuffer.Seek(0, SeekOrigin.Begin); - - serializer(saveBuffer); - } - - void WriterSnapshot(string savePath) - { - var path = Path.Combine(savePath, name); - - PathUtility.EnsureDirectory(path); - - string binPath = Path.Combine(path, $"{name}.bin"); - using var bin = new BinaryFileWriter(binPath, true); - - saveBuffer!.Resize((int)saveBuffer.Position); - bin.Write(saveBuffer.Buffer); - } - - void Deserialize(string savePath) - { - var path = Path.Combine(savePath, name); - - PathUtility.EnsureDirectory(path); - - string binPath = Path.Combine(path, $"{name}.bin"); - - if (!File.Exists(binPath)) - { - return; - } - - try - { - using FileStream fs = new FileStream(binPath, FileMode.Open, FileAccess.Read, FileShare.Read); - using var br = new BinaryFileReader(fs); - deserializer(br); - } - catch (Exception e) - { - Utility.PushColor(ConsoleColor.Red); - Console.WriteLine($"***** Bad deserialize of {name} *****"); - Console.WriteLine(e.ToString()); - Utility.PopColor(); - } - } - - Persistence.Register(name, Serialize, WriterSnapshot, Deserialize, priority); + serializer(saveBuffer); } + + void WriterSnapshot(string savePath) + { + string binPath = Path.Combine(savePath, name, $"{name}.bin"); + var buffer = saveBuffer!.Buffer.AsSpan(0, (int)saveBuffer.Position); + AdhocPersistence.WriteSnapshot(binPath, buffer); + } + + void Deserialize(string savePath) => + AdhocPersistence.Deserialize(Path.Combine(savePath, name, $"{name}.bin"), deserializer); + + Persistence.Register(name, Serialize, WriterSnapshot, Deserialize, priority); } } diff --git a/Projects/Server/World/EntityPersistence.cs b/Projects/Server/World/EntityPersistence.cs index 59a9d5e80..df0feffb7 100644 --- a/Projects/Server/World/EntityPersistence.cs +++ b/Projects/Server/World/EntityPersistence.cs @@ -16,6 +16,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.IO.MemoryMappedFiles; using System.Reflection; using System.Runtime.CompilerServices; using System.Threading.Tasks; @@ -177,12 +178,13 @@ namespace Server string dataPath = Path.Combine(path, indexType, $"{indexType}.bin"); - if (!File.Exists(dataPath)) + if (!File.Exists(dataPath) || new FileInfo(dataPath).Length == 0) { return; } - using FileStream bin = new FileStream(dataPath, FileMode.Open, FileAccess.Read, FileShare.Read); + using var mmf = MemoryMappedFile.CreateFromFile(dataPath, FileMode.Open); + using var stream = mmf.CreateViewStream(); BufferReader br = null; var deleteAllFailures = false; @@ -192,7 +194,7 @@ namespace Server T t = entry.Entity; var position = entry.Position; - bin.Seek(position, SeekOrigin.Begin); + stream.Seek(position, SeekOrigin.Begin); // Skip this entry if (t == null) @@ -216,7 +218,7 @@ namespace Server br.Reset(buffer, out _); } - bin.Read(buffer.AsSpan()); + stream.Read(buffer.AsSpan()); string error; try