mirror of https://github.com/dahall/Vanara.git
201 lines
9.2 KiB
C#
201 lines
9.2 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using Vanara.Extensions;
|
|
|
|
namespace Vanara.InteropServices
|
|
{
|
|
/// <summary>A <see cref="Stream"/> derivative for working with unmanaged memory.</summary>
|
|
/// <seealso cref="System.IO.Stream"/>
|
|
[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;
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="MarshalingStream"/> class.</summary>
|
|
/// <param name="unmanagedPtr">The unmanaged PTR.</param>
|
|
/// <param name="bytesAllocated">The bytes allocated.</param>
|
|
public MarshalingStream(IntPtr unmanagedPtr, long bytesAllocated)
|
|
{
|
|
ptr = unmanagedPtr;
|
|
Capacity = bytesAllocated;
|
|
}
|
|
|
|
/// <summary>Clears all buffers for this stream and causes any buffered data to be written to the underlying device.</summary>
|
|
public override void Flush() { }
|
|
|
|
/// <summary>Sets the position within the current stream.</summary>
|
|
/// <param name="offset">A byte offset relative to the <paramref name="origin"/> parameter.</param>
|
|
/// <param name="origin">A value of type <see cref="T:System.IO.SeekOrigin"/> indicating the reference point used to obtain the new position.</param>
|
|
/// <returns>The new position within the current stream.</returns>
|
|
/// <exception cref="System.ArgumentException"></exception>
|
|
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;
|
|
}
|
|
|
|
/// <summary>Sets the length of the current stream.</summary>
|
|
/// <param name="value">The desired length of the current stream in bytes.</param>
|
|
/// <exception cref="System.InvalidOperationException"></exception>
|
|
public override void SetLength(long value)
|
|
{
|
|
throw new InvalidOperationException();
|
|
}
|
|
|
|
/// <summary>Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.</summary>
|
|
/// <param name="buffer">
|
|
/// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between <paramref name="offset"/> and (
|
|
/// <paramref name="offset"/> + <paramref name="count"/> - 1) replaced by the bytes read from the current source.
|
|
/// </param>
|
|
/// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which to begin storing the data read from the current stream.</param>
|
|
/// <param name="count">The maximum number of bytes to be read from the current stream.</param>
|
|
/// <returns>
|
|
/// 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.
|
|
/// </returns>
|
|
/// <exception cref="System.ArgumentNullException">buffer</exception>
|
|
/// <exception cref="System.ArgumentException"></exception>
|
|
/// <exception cref="System.ArgumentOutOfRangeException"></exception>
|
|
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;
|
|
}
|
|
|
|
/// <summary>Reads a blittable type from the current stream and advances the position within the stream by the number of bytes read.</summary>
|
|
/// <typeparam name="T">The type of the object to read.</typeparam>
|
|
/// <returns>An object of type <typeparamref name="T"/>.</returns>
|
|
/// <exception cref="ArgumentException">Type to be read must be blittable. - T</exception>
|
|
/// <exception cref="ArgumentOutOfRangeException" />
|
|
public T Read<T>()
|
|
{
|
|
//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<T>();
|
|
Position += sz;
|
|
return ret;
|
|
}
|
|
|
|
/// <summary>Pokes the specified buffer at the offset from the starting pointer without changing the <see cref="Position"/>.</summary>
|
|
/// <param name="buffer">The buffer.</param>
|
|
/// <param name="offsetFromStart">The offset from start.</param>
|
|
/// <exception cref="System.ArgumentNullException">buffer</exception>
|
|
/// <exception cref="System.ArgumentException"></exception>
|
|
/// <exception cref="System.ArgumentOutOfRangeException"></exception>
|
|
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);
|
|
}
|
|
|
|
/// <summary>Pokes the specified IntPtr value at the offset from the starting pointer without changing the <see cref="Position"/>.</summary>
|
|
/// <param name="value">The value.</param>
|
|
/// <param name="offsetFromStart">The offset from start.</param>
|
|
/// <exception cref="System.ArgumentException"></exception>
|
|
/// <exception cref="System.ArgumentOutOfRangeException"></exception>
|
|
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);
|
|
}
|
|
|
|
/// <summary>Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.</summary>
|
|
/// <param name="buffer">An array of bytes. This method copies <paramref name="count"/> bytes from <paramref name="buffer"/> to the current stream.</param>
|
|
/// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which to begin copying bytes to the current stream.</param>
|
|
/// <param name="count">The number of bytes to be written to the current stream.</param>
|
|
/// <exception cref="System.ArgumentNullException">buffer</exception>
|
|
/// <exception cref="System.ArgumentException"></exception>
|
|
/// <exception cref="System.ArgumentOutOfRangeException"></exception>
|
|
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;
|
|
}
|
|
|
|
/// <summary>Writes the specified value into the stream.</summary>
|
|
/// <typeparam name="T">The type of the value.</typeparam>
|
|
/// <param name="value">The value.</param>
|
|
public void Write<T>(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);
|
|
}
|
|
}
|
|
|
|
/// <summary>Writes the specified array into the stream.</summary>
|
|
/// <typeparam name="T">The type of the array item.</typeparam>
|
|
/// <param name="items">The items.</param>
|
|
public void Write<T>(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<byte>(hMem.Size);
|
|
Write(bytes, 0, bytes.Length);
|
|
}
|
|
else
|
|
{
|
|
Position += Pointer.Write(items, (int)Position, Capacity);
|
|
}
|
|
}
|
|
|
|
/// <summary>Gets a value indicating whether the current stream supports reading.</summary>
|
|
public override bool CanRead => true;
|
|
|
|
/// <summary>Gets a value indicating whether the current stream supports seeking.</summary>
|
|
public override bool CanSeek => true;
|
|
|
|
/// <summary>Gets a value indicating whether the current stream supports writing.</summary>
|
|
public override bool CanWrite => true;
|
|
|
|
/// <summary>Gets the capacity.</summary>
|
|
/// <value>The capacity.</value>
|
|
public long Capacity { get; }
|
|
|
|
/// <summary>Gets or sets the character set.</summary>
|
|
/// <value>The character set.</value>
|
|
public CharSet CharSet { get; set; } = CharSet.Auto;
|
|
|
|
/// <summary>When overridden in a derived class, gets the length in bytes of the stream.</summary>
|
|
public override long Length => Capacity;
|
|
|
|
/// <summary>Gets the initial pointer supplied to the constructor.</summary>
|
|
public IntPtr Pointer => ptr;
|
|
|
|
/// <summary>When overridden in a derived class, gets or sets the position within the current stream.</summary>
|
|
public override long Position { get; set; }
|
|
|
|
/// <summary>Gets the position PTR.</summary>
|
|
/// <value>The position PTR.</value>
|
|
public IntPtr PositionPtr => ptr.Offset(Position);
|
|
}
|
|
}
|