// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace FlaxEngine.Json
{
///
/// Implements a that reads from unmanaged buffer (provided as raw pointer and length).
///
internal class UnmanagedMemoryStream : Stream
{
private unsafe byte* _ptr;
private int _length;
private int _pos;
private FileAccess _access;
internal bool _isOpen;
private Task _lastReadTask;
internal UnmanagedMemoryStream()
{
}
internal unsafe UnmanagedMemoryStream(byte* pointer, int length, FileAccess access = FileAccess.Read) => Initialize(pointer, length, access);
internal unsafe void Initialize(byte* pointer, int length, FileAccess access = FileAccess.Read)
{
_ptr = pointer;
_length = length;
_pos = 0;
_access = access;
_isOpen = true;
_lastReadTask = null;
}
public override bool CanRead => _isOpen && (uint)(_access & FileAccess.Read) > 0U;
public override bool CanSeek => _isOpen;
public override bool CanWrite => _isOpen && (uint)(_access & FileAccess.Write) > 0U;
protected override unsafe void Dispose(bool disposing)
{
_isOpen = false;
_ptr = null;
base.Dispose(disposing);
}
public override void Flush()
{
}
public override Task FlushAsync(CancellationToken cancellationToken)
{
Flush();
return Task.FromResult(0);
}
public unsafe byte* Pointer => _ptr;
public override long Length => _length;
public long Capacity => _length;
public override long Position
{
get => _pos;
set => _pos = (int)value;
}
public unsafe byte* PositionPointer
{
get => _ptr + _pos;
set => _pos = (int)(value - _ptr);
}
public override unsafe int Read(byte[] buffer, int offset, int count)
{
int toRead = _length - _pos;
if (toRead > count)
toRead = count;
if (toRead <= 0)
return 0;
fixed (byte* bufferPtr = buffer)
Utils.MemoryCopy(new IntPtr(bufferPtr), new IntPtr(_ptr + _pos), (ulong)toRead);
_pos += toRead;
return toRead;
}
public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
int result = Read(buffer, offset, count);
return _lastReadTask == null || _lastReadTask.Result != result ? (_lastReadTask = Task.FromResult(result)) : _lastReadTask;
}
public override unsafe int ReadByte()
{
int index = _pos;
if (index >= _length)
return -1;
_pos = index + 1;
return _ptr[index];
}
public override long Seek(long offset, SeekOrigin loc)
{
switch (loc)
{
case SeekOrigin.Begin:
_pos = (int)offset;
break;
case SeekOrigin.Current:
_pos += (int)offset;
break;
case SeekOrigin.End:
_pos = _length + (int)offset;
break;
default: throw new ArgumentOutOfRangeException();
}
return _pos;
}
public override void SetLength(long value)
{
_length = (int)value;
if (_pos > value)
_pos = _length;
}
public override unsafe void Write(byte[] buffer, int offset, int count)
{
int newPos = _pos + count;
if (newPos > _length)
_length = newPos;
fixed (byte* bufferPtr = buffer)
Utils.MemoryCopy(new IntPtr(bufferPtr), new IntPtr(_pos + _pos), (ulong)count);
_pos = newPos;
}
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
Write(buffer, offset, count);
return Task.FromResult(0);
}
public override unsafe void WriteByte(byte value)
{
long newPos = _pos + 1;
if (_pos >= _length)
_length = (int)newPos;
_ptr[_pos] = value;
_pos = (int)newPos;
}
}
}