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); } }