using System;
using System.IO;
using System.Runtime.InteropServices;
using Vanara.Extensions;
namespace Vanara.InteropServices
{
/// A derivative for working with unmanaged memory.
///
[Obsolete("This class may not be available in future releases. Please replace uses with Vanara.InteropServices.NativeMemoryStream.")]
public class MarshalingStream : Stream
{
private readonly IntPtr ptr;
/// Initializes a new instance of the class.
/// The unmanaged PTR.
/// The bytes allocated.
public MarshalingStream(IntPtr unmanagedPtr, long bytesAllocated)
{
ptr = unmanagedPtr;
Capacity = bytesAllocated;
}
/// Clears all buffers for this stream and causes any buffered data to be written to the underlying device.
public override void Flush() { }
/// Sets the position within the current stream.
/// A byte offset relative to the parameter.
/// A value of type indicating the reference point used to obtain the new position.
/// The new position within the current stream.
///
public override long Seek(long offset, SeekOrigin origin)
{
var startPos = origin == SeekOrigin.Begin ? 0L : (origin == SeekOrigin.Current ? Position : Capacity);
var reqPos = startPos + offset;
if (reqPos < 0 || reqPos > Capacity)
throw new ArgumentException();
return Position = reqPos;
}
/// Sets the length of the current stream.
/// The desired length of the current stream in bytes.
///
public override void SetLength(long value)
{
throw new InvalidOperationException();
}
/// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
///
/// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and (
/// + - 1) replaced by the bytes read from the current source.
///
/// The zero-based byte offset in at which to begin storing the data read from the current stream.
/// The maximum number of bytes to be read from the current stream.
///
/// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available,
/// or zero (0) if the end of the stream has been reached.
///
/// buffer
///
///
public override int Read(byte[] buffer, int offset, int count)
{
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
if (offset + count > buffer.Length) throw new ArgumentException();
if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException();
if (Position + count > Capacity) count = (int)(Capacity - Position);
if (count > 0)
{
Marshal.Copy(PositionPtr, buffer, offset, count);
Position += count;
}
return count;
}
/// Reads a blittable type from the current stream and advances the position within the stream by the number of bytes read.
/// The type of the object to read.
/// An object of type .
/// Type to be read must be blittable. - T
///
public T Read()
{
//if (!typeof(T).IsBlittable()) throw new ArgumentException(@"Type to be read must be blittable.", nameof(T));
var sz = Marshal.SizeOf(typeof(T));
if (Position + sz > Capacity) throw new ArgumentOutOfRangeException();
var ret = PositionPtr.ToStructure();
Position += sz;
return ret;
}
/// Pokes the specified buffer at the offset from the starting pointer without changing the .
/// The buffer.
/// The offset from start.
/// buffer
///
///
public void Poke(byte[] buffer, long offsetFromStart)
{
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
if (offsetFromStart + buffer.Length > Capacity) throw new ArgumentException();
if (offsetFromStart < 0) throw new ArgumentOutOfRangeException();
Marshal.Copy(buffer, 0, ptr, buffer.Length);
}
/// Pokes the specified IntPtr value at the offset from the starting pointer without changing the .
/// The value.
/// The offset from start.
///
///
public void Poke(IntPtr value, long offsetFromStart)
{
if (offsetFromStart + IntPtr.Size > Capacity) throw new ArgumentException();
if (offsetFromStart < 0) throw new ArgumentOutOfRangeException();
Marshal.WriteIntPtr(ptr.Offset(offsetFromStart), value);
}
/// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
/// An array of bytes. This method copies bytes from to the current stream.
/// The zero-based byte offset in at which to begin copying bytes to the current stream.
/// The number of bytes to be written to the current stream.
/// buffer
///
///
public override void Write(byte[] buffer, int offset, int count)
{
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
if (offset + count > buffer.Length) throw new ArgumentException();
if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException();
if (Position + count - offset > Capacity) throw new ArgumentException();
Marshal.Copy(buffer, offset, PositionPtr, count);
Position += count;
}
/// Writes the specified value into the stream.
/// The type of the value.
/// The value.
public void Write(T value)
{
if (value == null) return;
if (value is string s)
{
var bytes = s.GetBytes(true, CharSet);
Write(bytes, 0, bytes.Length);
}
else
{
Position += Pointer.Write(value, (int)Position, Capacity);
}
}
/// Writes the specified array into the stream.
/// The type of the array item.
/// The items.
public void Write(T[] items)
{
if (items == null) return;
if (items.GetType().GetElementType() == typeof(string))
{
using var hMem = SafeHGlobalHandle.CreateFromStringList(items as string[], StringListPackMethod.Packed, CharSet, 0);
var bytes = hMem.ToArray(hMem.Size);
Write(bytes, 0, bytes.Length);
}
else
{
Position += Pointer.Write(items, (int)Position, Capacity);
}
}
/// Gets a value indicating whether the current stream supports reading.
public override bool CanRead => true;
/// Gets a value indicating whether the current stream supports seeking.
public override bool CanSeek => true;
/// Gets a value indicating whether the current stream supports writing.
public override bool CanWrite => true;
/// Gets the capacity.
/// The capacity.
public long Capacity { get; }
/// Gets or sets the character set.
/// The character set.
public CharSet CharSet { get; set; } = CharSet.Auto;
/// When overridden in a derived class, gets the length in bytes of the stream.
public override long Length => Capacity;
/// Gets the initial pointer supplied to the constructor.
public IntPtr Pointer => ptr;
/// When overridden in a derived class, gets or sets the position within the current stream.
public override long Position { get; set; }
/// Gets the position PTR.
/// The position PTR.
public IntPtr PositionPtr => ptr.Offset(Position);
}
}