diff --git a/Core/InteropServices/NativeMemoryStream.cs b/Core/InteropServices/NativeMemoryStream.cs index a4f45959a..fb331aa6d 100644 --- a/Core/InteropServices/NativeMemoryStream.cs +++ b/Core/InteropServices/NativeMemoryStream.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; namespace Vanara.InteropServices; @@ -115,6 +116,7 @@ public override long Position /// Ensures the allocated buffer is large enough for the supplied capacity. /// The new capacity. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual void EnsureCapacity(long value) { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value)); @@ -169,7 +171,7 @@ public override int Read(byte[] buffer, int offset, int count) if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset)); ThrowIfDisposed(); if (!CanRead) throw new NotSupportedException(); - if (Position + count > Capacity) throw new ArgumentOutOfRangeException(nameof(count)); + count = (int)Math.Min(count, Capacity - Position); if (count > 0) { Marshal.Copy(PositionPtr, buffer, offset, count); @@ -178,6 +180,19 @@ public override int Read(byte[] buffer, int offset, int count) return count; } + /// + /// 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 replaced by the bytes read + /// from the current source. + /// + /// + /// 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. + /// + public virtual int Read(byte[] buffer) => Read(buffer, 0, buffer.Length); + /// /// Reads a blittable type from the current stream and advances the position within the stream by the number of bytes read. /// @@ -186,6 +201,7 @@ public override int Read(byte[] buffer, int offset, int count) /// An object of type . /// Type to be read must be blittable. - T /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public T? Read(CharSet charSet = CharSet.Auto) => (T?)Read(typeof(T), charSet); /// @@ -332,9 +348,7 @@ public override void SetLength(long value) /// /// 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 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 /// @@ -353,9 +367,16 @@ public override void Write(byte[] buffer, int offset, int count) length += count; } + /// + /// 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 all bytes from to the current stream. + public virtual void Write(byte[] buffer) => Write(buffer, 0, buffer.Length); + /// Writes the specified value into the stream. /// The type of the value. /// The value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(in T value) where T : struct => WriteObject(value); /// Writes the specified string into the stream. @@ -376,6 +397,7 @@ public void Write(string value, CharSet charSetOverride) /// Write values as a referenced array. public void Write(IEnumerable? items, bool byRef = false) { + if (items is byte[] b && byRef == false) { Write(b, 0, b.Length); return; } if (access == FileAccess.Read) throw new NotSupportedException(); if (items == null) return; ResetIfFlushed(); @@ -455,6 +477,7 @@ public virtual void WriteObject(object? value) /// or flushed. /// /// The value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteReference(T value) where T : unmanaged => WriteReferenceObject(value); /// @@ -462,6 +485,7 @@ public virtual void WriteObject(object? value) /// or flushed. /// /// The value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteReference(T? value) where T : unmanaged => WriteReferenceObject(value.HasValue ? value.Value : null); /// @@ -469,6 +493,7 @@ public virtual void WriteObject(object? value) /// or flushed. /// /// The string value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteReference(string? value) => WriteReferenceObject(value); /// Writes the specified value into the stream. This function should fail if the object cannot be blitted. @@ -500,9 +525,11 @@ protected override void Dispose(bool disposing) /// The object to check. /// The character set. /// The size, in bytes, of the object. + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected virtual int GetSize(object? obj, CharSet charSet = CharSet.None) => InteropExtensions.SizeOf(obj, charSet); + [MethodImpl(MethodImplOptions.AggressiveInlining)] private int GetRefSize() => references.Sum(e => GetSize(e.Value, CharSet)); private void ResetIfFlushed() @@ -513,11 +540,13 @@ private void ResetIfFlushed() preflushPos = 0; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ThrowIfDisposed() { if (IsDisposed) throw new ObjectDisposedException(nameof(NativeMemoryStream)); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private T ThrowIfDisposed(T value) => !IsDisposed ? value : throw new ObjectDisposedException(nameof(NativeMemoryStream)); private class Reference diff --git a/UnitTests/Core/InteropServices/NativeMemoryStreamTests.cs b/UnitTests/Core/InteropServices/NativeMemoryStreamTests.cs index e38ca7926..1787c6ffb 100644 --- a/UnitTests/Core/InteropServices/NativeMemoryStreamTests.cs +++ b/UnitTests/Core/InteropServices/NativeMemoryStreamTests.cs @@ -123,6 +123,20 @@ public void NativeMemoryStreamTest2() Assert.That(() => ms.Write(Guid.NewGuid()), Throws.Exception); } + [Test] + public void NativeVsNormalTest() + { + var buffer = new byte[1000]; + var temp1 = new MemoryStream(); + var temp2 = new NativeMemoryStream(); + + temp1.Write(new byte[1000]); + temp2.Write(new byte[1000]); + + Assert.That(temp1.Read(buffer, 0, 100), Is.EqualTo(0)); // Normal for MemoryStream, return value is 0 + Assert.That(temp2.Read(buffer, 0, 100), Is.EqualTo(0)); // NativeMemoryStream throw ArgumentOutOfRangeException here + } + [Test] public void MixedReadWriteTest() {