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 | 101 ++++-- .../Server/Serialization/BinaryFileReader.cs | 122 +++++--- Projects/Server/Serialization/BufferReader.cs | 294 +++++++++--------- .../Serialization/GenericPersistence.cs | 82 ++--- Projects/Server/World/EntityPersistence.cs | 10 +- 5 files changed, 324 insertions(+), 285 deletions(-) diff --git a/Projects/Server/Serialization/AdhocPersistence.cs b/Projects/Server/Serialization/AdhocPersistence.cs index 030c189254..83fdc590d5 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) + { + var saveBuffer = new BufferWriter(true); + serializer(saveBuffer); + return saveBuffer; + } + + /** + * Writes a buffer to disk. This function should be called asynchronously. + */ + public static void WriteSnapshot(string filePath, Span buffer) { - public static void Serialize(string filePath, Action serializer) + 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; + } + + var fileLength = file.Length; + if (fileLength == 0) { - var fullPath = Path.Combine(Core.BaseDirectory, filePath); - var file = new FileInfo(fullPath); - file.Directory?.Create(); + return; + } + + string error; - using var bin = new BinaryFileWriter(fullPath, true); - serializer(bin); + 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(); } - public static void Deserialize(string filePath, Action deserializer) + if (error != null) { - var fullPath = Path.Combine(Core.BaseDirectory, filePath); - var file = new FileInfo(fullPath); - file.Directory?.Create(); + Console.WriteLine($"***** Bad deserialize of {file.FullName} *****"); + Console.WriteLine(error); - if (!file.Exists) - { - return; - } + Console.WriteLine("Skip this file and continue? (y/n)"); - try - { - using FileStream fs = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.Read); - using var br = new BinaryFileReader(fs); - deserializer(br); - } - catch (Exception e) + var pressedKey = Console.ReadKey(true).Key; + + if (pressedKey != ConsoleKey.Y) { - 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 e7ba336424..927acad9f2 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; + _reader = br; + _encoding = encoding ?? TextEncoding.UTF8; + } - public BinaryFileReader(BinaryReader br) => _reader = br; + public BinaryFileReader(Stream stream, Encoding encoding = null) : this(new BinaryReader(stream), encoding) + { + } - public BinaryFileReader(Stream stream) => _reader = new BinaryReader(stream); + public long Position => _reader.BaseStream.Position; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Close() => _reader.Close(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Close() => _reader.Close(); - public DateTime LastSerialized { get; init; } + public DateTime LastSerialized { get; init; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadString(bool intern = false) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public string ReadString(bool intern = false) + { + if (!ReadBool()) { - var str = _reader.ReadString(); - return intern ? Utility.Intern(str) : str; + return null; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public long ReadLong() => _reader.ReadInt64(); + var length = ((IGenericReader)this).ReadEncodedInt(); + if (length <= 0) + { + return intern ? Utility.Intern("") : ""; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ulong ReadULong() => _reader.ReadUInt64(); + 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 int ReadInt() => _reader.ReadInt32(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long ReadLong() => _reader.ReadInt64(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public uint ReadUInt() => _reader.ReadUInt32(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong ReadULong() => _reader.ReadUInt64(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public short ReadShort() => _reader.ReadInt16(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReadInt() => _reader.ReadInt32(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ushort ReadUShort() => _reader.ReadUInt16(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public uint ReadUInt() => _reader.ReadUInt32(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public double ReadDouble() => _reader.ReadDouble(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public short ReadShort() => _reader.ReadInt16(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float ReadFloat() => _reader.ReadSingle(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ushort ReadUShort() => _reader.ReadUInt16(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public byte ReadByte() => _reader.ReadByte(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double ReadDouble() => _reader.ReadDouble(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public sbyte ReadSByte() => _reader.ReadSByte(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float ReadFloat() => _reader.ReadSingle(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool ReadBool() => _reader.ReadBoolean(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte ReadByte() => _reader.ReadByte(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Serial ReadSerial() => (Serial)_reader.ReadUInt32(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public sbyte ReadSByte() => _reader.ReadSByte(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Read(Span buffer) => _reader.Read(buffer); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ReadBool() => _reader.ReadBoolean(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public BitArray ReadBitArray() - { - var length = ((IGenericReader)this).ReadEncodedInt(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Serial ReadSerial() => (Serial)_reader.ReadUInt32(); - // 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 int Read(Span buffer) => _reader.Read(buffer); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public long Seek(long offset, SeekOrigin origin) => _reader.BaseStream.Seek(offset, origin); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public BitArray ReadBitArray() + { + var length = ((IGenericReader)this).ReadEncodedInt(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dispose() => Close(); + // 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 7ba58bb16d..7af7bce553 100644 --- a/Projects/Server/Serialization/BufferReader.cs +++ b/Projects/Server/Serialization/BufferReader.cs @@ -22,186 +22,184 @@ using Server.Collections; using Server.Text; -namespace Server +namespace Server; + +public class BufferReader : IGenericReader { - public class BufferReader : IGenericReader - { - private readonly Encoding _encoding; - private byte[] _buffer; - private int _position; + private Encoding _encoding; + private byte[] _buffer; + private int _position; - public long Position => _position; + public long Position => _position; - public BufferReader(byte[] buffer, Encoding encoding = null) - { - _buffer = buffer; - _encoding = encoding ?? TextEncoding.UTF8; - } + public BufferReader(byte[] buffer, Encoding encoding = null) + { + _buffer = buffer; + _encoding = encoding ?? TextEncoding.UTF8; + } - public BufferReader(byte[] buffer, DateTime lastSerialized) : this(buffer) => LastSerialized = lastSerialized; + public BufferReader(byte[] buffer, DateTime lastSerialized) : this(buffer) => LastSerialized = lastSerialized; - public void Reset(byte[] newBuffer, out byte[] oldBuffer) - { - oldBuffer = _buffer; - _buffer = newBuffer; - _position = 0; - } + public void Reset(byte[] newBuffer, out byte[] oldBuffer) + { + oldBuffer = _buffer; + _buffer = newBuffer; + _position = 0; + } - // Compatible with BinaryReader.ReadString() - public DateTime LastSerialized { get; init; } + public DateTime LastSerialized { get; init; } - public string ReadString(bool intern = false) + public string ReadString(bool intern = false) + { + if (!ReadBool()) { - 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; + return null; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public long ReadLong() + var length = ((IGenericReader)this).ReadEncodedInt(); + if (length <= 0) { - var v = BinaryPrimitives.ReadInt64LittleEndian(_buffer.AsSpan(_position, 8)); - _position += 8; - return v; + return intern ? Utility.Intern("") : ""; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ulong ReadULong() - { - var v = BinaryPrimitives.ReadUInt64LittleEndian(_buffer.AsSpan(_position, 8)); - _position += 8; - return v; - } + var str = TextEncoding.GetString(_buffer.AsSpan(_position, length), _encoding); + _position += length; + return intern ? Utility.Intern(str) : str; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int ReadInt() - { - var v = BinaryPrimitives.ReadInt32LittleEndian(_buffer.AsSpan(_position, 4)); - _position += 4; - return v; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long ReadLong() + { + var v = BinaryPrimitives.ReadInt64LittleEndian(_buffer.AsSpan(_position, 8)); + _position += 8; + return v; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public uint ReadUInt() - { - var v = BinaryPrimitives.ReadUInt32LittleEndian(_buffer.AsSpan(_position, 4)); - _position += 4; - return v; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong ReadULong() + { + var v = BinaryPrimitives.ReadUInt64LittleEndian(_buffer.AsSpan(_position, 8)); + _position += 8; + return v; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public short ReadShort() - { - var v = BinaryPrimitives.ReadInt16LittleEndian(_buffer.AsSpan(_position, 2)); - _position += 2; - return v; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReadInt() + { + var v = BinaryPrimitives.ReadInt32LittleEndian(_buffer.AsSpan(_position, 4)); + _position += 4; + return v; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ushort ReadUShort() - { - var v = BinaryPrimitives.ReadUInt16LittleEndian(_buffer.AsSpan(_position, 2)); - _position += 2; - return v; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public uint ReadUInt() + { + var v = BinaryPrimitives.ReadUInt32LittleEndian(_buffer.AsSpan(_position, 4)); + _position += 4; + return v; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public double ReadDouble() - { - var v = BinaryPrimitives.ReadDoubleLittleEndian(_buffer.AsSpan(_position, 8)); - _position += 8; - return v; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public short ReadShort() + { + var v = BinaryPrimitives.ReadInt16LittleEndian(_buffer.AsSpan(_position, 2)); + _position += 2; + return v; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float ReadFloat() - { - var v = BinaryPrimitives.ReadSingleLittleEndian(_buffer.AsSpan(_position, 4)); - _position += 4; - 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 byte ReadByte() => _buffer[_position++]; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public sbyte ReadSByte() => (sbyte)_buffer[_position++]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public sbyte ReadSByte() => (sbyte)_buffer[_position++]; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool ReadBool() => _buffer[_position++] != 0; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ReadBool() => _buffer[_position++] != 0; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Serial ReadSerial() => (Serial)ReadUInt(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Serial ReadSerial() => (Serial)ReadUInt(); - public int Read(Span buffer) + public int Read(Span buffer) + { + var length = buffer.Length; + if (length > _buffer.Length - _position) { - var length = buffer.Length; - if (length > _buffer.Length - _position) - { - throw new OutOfMemoryException(); - } - - _buffer.AsSpan(_position, length).CopyTo(buffer); - _position += length; - return length; + throw new OutOfMemoryException(); } - public BitArray ReadBitArray() - { - var bitLength = ((IGenericReader)this).ReadEncodedInt(); - var length = BitArray.GetByteArrayLengthFromBitLength(bitLength); + _buffer.AsSpan(_position, length).CopyTo(buffer); + _position += length; + return length; + } - if (length > _buffer.Length - _position) - { - throw new OutOfMemoryException(); - } + public BitArray ReadBitArray() + { + var bitLength = ((IGenericReader)this).ReadEncodedInt(); + var length = BitArray.GetByteArrayLengthFromBitLength(bitLength); - var bitArray = new BitArray(_buffer.AsSpan(_position, length), bitLength); - _position += length; - return bitArray; + if (length > _buffer.Length - _position) + { + throw new OutOfMemoryException(); } - public virtual long Seek(long offset, SeekOrigin origin) + 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 { - 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; + 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; } } diff --git a/Projects/Server/Serialization/GenericPersistence.cs b/Projects/Server/Serialization/GenericPersistence.cs index eb878303ce..36909b38c0 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() - { - 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); + BufferWriter saveBuffer = null; - saveBuffer!.Resize((int)saveBuffer.Position); - bin.Write(saveBuffer.Buffer); - } - - void Deserialize(string savePath) - { - var path = Path.Combine(savePath, name); - - PathUtility.EnsureDirectory(path); + void Serialize() + { + saveBuffer ??= new BufferWriter(true); + saveBuffer.Seek(0, SeekOrigin.Begin); - string binPath = Path.Combine(path, $"{name}.bin"); + serializer(saveBuffer); + } - if (!File.Exists(binPath)) - { - return; - } + 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); + } - 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(); - } - } + void Deserialize(string savePath) => + AdhocPersistence.Deserialize(Path.Combine(savePath, name, $"{name}.bin"), deserializer); - Persistence.Register(name, Serialize, WriterSnapshot, Deserialize, priority); - } + Persistence.Register(name, Serialize, WriterSnapshot, Deserialize, priority); } } diff --git a/Projects/Server/World/EntityPersistence.cs b/Projects/Server/World/EntityPersistence.cs index 59a9d5e802..df0feffb7a 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 @@ List> entities 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 @@ List> entities 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 @@ List> entities br.Reset(buffer, out _); } - bin.Read(buffer.AsSpan()); + stream.Read(buffer.AsSpan()); string error; try