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;
}
throw new FileNotFoundException("path");
throw new FileNotFoundException(path);
}
public void Commit()

View file

@ -36,6 +36,6 @@ namespace LibHac.IO
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
{
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<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)
{
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;
}
}

View file

@ -7,6 +7,7 @@ namespace LibHac.IO
{
private bool _isDisposed;
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 WriteImpl(ReadOnlySpan<byte> 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");
}