From 19cf0031609b450cd6388b78d245e3c43db89aa1 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Thu, 31 Jan 2019 20:58:52 -0600 Subject: [PATCH] Add an optional dynamic backing array to MemoryStorage --- src/LibHac/IO/LocalFileSystem.cs | 2 +- src/LibHac/IO/LocalStorage.cs | 2 +- src/LibHac/IO/MemoryStorage.cs | 76 ++++++++++++++++++++++++++++---- src/LibHac/IO/StorageBase.cs | 3 +- 4 files changed, 71 insertions(+), 12 deletions(-) diff --git a/src/LibHac/IO/LocalFileSystem.cs b/src/LibHac/IO/LocalFileSystem.cs index f6aac027..3eae17d0 100644 --- a/src/LibHac/IO/LocalFileSystem.cs +++ b/src/LibHac/IO/LocalFileSystem.cs @@ -142,7 +142,7 @@ namespace LibHac.IO return DirectoryEntryType.File; } - throw new FileNotFoundException("path"); + throw new FileNotFoundException(path); } public void Commit() diff --git a/src/LibHac/IO/LocalStorage.cs b/src/LibHac/IO/LocalStorage.cs index dd08fe52..ec149079 100644 --- a/src/LibHac/IO/LocalStorage.cs +++ b/src/LibHac/IO/LocalStorage.cs @@ -36,6 +36,6 @@ namespace LibHac.IO Storage.Flush(); } - public override long Length => Stream.Length; + public override long Length => Storage.Length; } } diff --git a/src/LibHac/IO/MemoryStorage.cs b/src/LibHac/IO/MemoryStorage.cs index 3a3ca272..26a9e299 100644 --- a/src/LibHac/IO/MemoryStorage.cs +++ b/src/LibHac/IO/MemoryStorage.cs @@ -4,14 +4,26 @@ namespace LibHac.IO { public class MemoryStorage : StorageBase { - private byte[] Buffer { get; } - private int Start { get; } + private byte[] _buffer; + private int _start; + private int _length; + private int _capacity; + private bool _isExpandable; - public MemoryStorage(byte[] buffer) : this(buffer, 0, buffer.Length) + public MemoryStorage() : this(0) { } + + public MemoryStorage(int capacity) { + if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity), "Argument must be positive"); + _capacity = capacity; + _isExpandable = true; + CanAutoExpand = true; + _buffer = new byte[capacity]; } + public MemoryStorage(byte[] buffer) : this(buffer, 0, buffer.Length) { } + public MemoryStorage(byte[] buffer, int index, int count) { if (buffer == null) throw new NullReferenceException(nameof(buffer)); @@ -19,23 +31,69 @@ namespace LibHac.IO if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), "Value must be non-negative."); if (buffer.Length - index < count) throw new ArgumentException("Length, index and count parameters are invalid."); - Buffer = buffer; - Start = index; - Length = count; + _buffer = buffer; + _start = index; + _length = count; + _capacity = count; + _isExpandable = false; } protected override void ReadImpl(Span destination, long offset) { - Buffer.AsSpan((int)(Start + offset), destination.Length).CopyTo(destination); + _buffer.AsSpan((int)(_start + offset), destination.Length).CopyTo(destination); } protected override void WriteImpl(ReadOnlySpan source, long offset) { - source.CopyTo(Buffer.AsSpan((int)(Start + offset), source.Length)); + long requiredCapacity = _start + offset + source.Length; + + if (requiredCapacity > _length) + { + if (requiredCapacity > _capacity) EnsureCapacity(requiredCapacity); + _length = (int)(requiredCapacity - _start); + } + + source.CopyTo(_buffer.AsSpan((int)(_start + offset), source.Length)); + } + + public byte[] ToArray() + { + var array = new byte[_length]; + Buffer.BlockCopy(_buffer, _start, array, 0, _length); + return array; + } + + // returns a bool saying whether we allocated a new array. + private void EnsureCapacity(long value) + { + if (value < 0 || value > int.MaxValue) throw new ArgumentOutOfRangeException(nameof(value)); + if (value <= _capacity) return; + + long newCapacity = Math.Max(value, 256); + newCapacity = Math.Max(newCapacity, _capacity * 2); + + SetCapacity((int)Math.Min(newCapacity, int.MaxValue)); + } + + private void SetCapacity(int value) + { + if (value < Length) + throw new ArgumentOutOfRangeException(nameof(value), "Capacity is smaller than the current length."); + + if (!_isExpandable && value != _capacity) throw new NotSupportedException("MemoryStorage is not expandable."); + + if (_isExpandable && value != _capacity) + { + var newBuffer = new byte[value]; + Buffer.BlockCopy(_buffer, 0, newBuffer, 0, (int)Length); + + _buffer = newBuffer; + _capacity = value; + } } public override void Flush() { } - public override long Length { get; } + public override long Length => _length; } } diff --git a/src/LibHac/IO/StorageBase.cs b/src/LibHac/IO/StorageBase.cs index 41ba3c88..da41459c 100644 --- a/src/LibHac/IO/StorageBase.cs +++ b/src/LibHac/IO/StorageBase.cs @@ -7,6 +7,7 @@ namespace LibHac.IO { private bool _isDisposed; protected internal List ToDispose { get; } = new List(); + protected bool CanAutoExpand { get; set; } protected abstract void ReadImpl(Span destination, long offset); protected abstract void WriteImpl(ReadOnlySpan source, long offset); @@ -53,7 +54,7 @@ namespace LibHac.IO if (span == null) throw new ArgumentNullException(nameof(span)); if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset), "Argument must be non-negative."); - if (Length != -1) + if (Length != -1 && !CanAutoExpand) { if (offset + span.Length > Length) throw new ArgumentException("The given offset and count exceed the length of the Storage"); }