Add an optional dynamic backing array to MemoryStorage

This commit is contained in:
Alex Barney 2019-01-31 20:58:52 -06:00
parent 34e926f2a4
commit 19cf003160
4 changed files with 71 additions and 12 deletions

View file

@ -142,7 +142,7 @@ namespace LibHac.IO
return DirectoryEntryType.File; return DirectoryEntryType.File;
} }
throw new FileNotFoundException("path"); throw new FileNotFoundException(path);
} }
public void Commit() public void Commit()

View file

@ -36,6 +36,6 @@ namespace LibHac.IO
Storage.Flush(); Storage.Flush();
} }
public override long Length => Stream.Length; public override long Length => Storage.Length;
} }
} }

View file

@ -4,14 +4,26 @@ namespace LibHac.IO
{ {
public class MemoryStorage : StorageBase public class MemoryStorage : StorageBase
{ {
private byte[] Buffer { get; } private byte[] _buffer;
private int Start { get; } 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) public MemoryStorage(byte[] buffer, int index, int count)
{ {
if (buffer == null) throw new NullReferenceException(nameof(buffer)); 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 (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."); if (buffer.Length - index < count) throw new ArgumentException("Length, index and count parameters are invalid.");
Buffer = buffer; _buffer = buffer;
Start = index; _start = index;
Length = count; _length = count;
_capacity = count;
_isExpandable = false;
} }
protected override void ReadImpl(Span<byte> destination, long offset) protected override void ReadImpl(Span<byte> 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<byte> source, long offset) protected override void WriteImpl(ReadOnlySpan<byte> 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 void Flush() { }
public override long Length { get; } public override long Length => _length;
} }
} }

View file

@ -7,6 +7,7 @@ namespace LibHac.IO
{ {
private bool _isDisposed; private bool _isDisposed;
protected internal List<IDisposable> ToDispose { get; } = new List<IDisposable>(); protected internal List<IDisposable> ToDispose { get; } = new List<IDisposable>();
protected bool CanAutoExpand { get; set; }
protected abstract void ReadImpl(Span<byte> destination, long offset); protected abstract void ReadImpl(Span<byte> destination, long offset);
protected abstract void WriteImpl(ReadOnlySpan<byte> source, long offset); protected abstract void WriteImpl(ReadOnlySpan<byte> source, long offset);
@ -53,7 +54,7 @@ namespace LibHac.IO
if (span == null) throw new ArgumentNullException(nameof(span)); if (span == null) throw new ArgumentNullException(nameof(span));
if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset), "Argument must be non-negative."); 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"); if (offset + span.Length > Length) throw new ArgumentException("The given offset and count exceed the length of the Storage");
} }