From 1080144df632debb113812732f838bd64156a4e4 Mon Sep 17 00:00:00 2001 From: David Date: Tue, 6 Oct 2020 08:19:56 -0400 Subject: [PATCH] feat(storage): Use Memory in buffer instead of byte[] --- .../Media/Imaging/WriteableBitmap.skia.cs | 5 +- .../Midi/MidiChannelPressureMessage.cs | 4 +- .../Devices/Midi/MidiControlChangeMessage.cs | 6 +- .../Devices/Midi/MidiNoteOffMessage.cs | 6 +- src/Uno.UWP/Devices/Midi/MidiNoteOnMessage.cs | 6 +- .../Midi/MidiPitchBendChangeMessage.cs | 4 +- .../Midi/MidiPolyphonicKeyPressureMessage.cs | 6 +- .../Devices/Midi/MidiProgramChangeMessage.cs | 4 +- .../Midi/MidiSongPositionPointerMessage.cs | 2 +- .../Devices/Midi/MidiSongSelectMessage.cs | 2 +- .../Midi/MidiSystemExclusiveMessage.cs | 2 +- .../Devices/Midi/MidiTimeCodeMessage.cs | 4 +- src/Uno.UWP/Storage/FileIO.cs | 12 +-- src/Uno.UWP/Storage/Streams/Buffer.cs | 77 +++++++++++++++++-- src/Uno.UWP/Storage/Streams/DataReader.cs | 36 ++------- src/Uno.UWP/Storage/Streams/DataWriter.cs | 21 ++--- .../WindowsRuntimeBufferExtensions.cs | 48 ++++++------ 17 files changed, 138 insertions(+), 107 deletions(-) diff --git a/src/Uno.UI/UI/Xaml/Media/Imaging/WriteableBitmap.skia.cs b/src/Uno.UI/UI/Xaml/Media/Imaging/WriteableBitmap.skia.cs index c41320d72cc6..535da5a8724f 100644 --- a/src/Uno.UI/UI/Xaml/Media/Imaging/WriteableBitmap.skia.cs +++ b/src/Uno.UI/UI/Xaml/Media/Imaging/WriteableBitmap.skia.cs @@ -16,9 +16,10 @@ private protected override bool TryOpenSourceSync(int? targetWidth, int? targetH { _surface ??= new SkiaCompositionSurface(); - _surface.SetPixels(PixelWidth, PixelHeight, _buffer.Data); + _surface.SetPixels(PixelWidth, PixelHeight, _buffer.ToArray()); - image = new ImageData { + image = new ImageData + { Value = _surface }; diff --git a/src/Uno.UWP/Devices/Midi/MidiChannelPressureMessage.cs b/src/Uno.UWP/Devices/Midi/MidiChannelPressureMessage.cs index 82c33bb12acf..375b2686534f 100644 --- a/src/Uno.UWP/Devices/Midi/MidiChannelPressureMessage.cs +++ b/src/Uno.UWP/Devices/Midi/MidiChannelPressureMessage.cs @@ -47,12 +47,12 @@ internal MidiChannelPressureMessage(byte[] rawData, TimeSpan timestamp) /// /// Gets the channel from 0-15 that this message applies to. /// - public byte Channel => MidiHelpers.GetChannel(_buffer.Data[0]); + public byte Channel => MidiHelpers.GetChannel(_buffer.GetByte(0)); /// /// Gets the pressure from 0-127. /// - public byte Pressure => _buffer.Data[1]; + public byte Pressure => _buffer.GetByte(0); /// /// Gets the array of bytes associated with the MIDI message, including status byte. diff --git a/src/Uno.UWP/Devices/Midi/MidiControlChangeMessage.cs b/src/Uno.UWP/Devices/Midi/MidiControlChangeMessage.cs index 43af4de208b7..cefa26d4bd99 100644 --- a/src/Uno.UWP/Devices/Midi/MidiControlChangeMessage.cs +++ b/src/Uno.UWP/Devices/Midi/MidiControlChangeMessage.cs @@ -51,17 +51,17 @@ internal MidiControlChangeMessage(byte[] rawData, TimeSpan timestamp) /// /// Gets the channel from 0-15 that this message applies to. /// - public byte Channel => MidiHelpers.GetChannel(_buffer.Data[0]); + public byte Channel => MidiHelpers.GetChannel(_buffer.GetByte(0)); /// /// Gets the value from 0-127 to apply to the controller. /// - public byte ControlValue => _buffer.Data[2]; + public byte ControlValue => _buffer.GetByte(2); /// /// Gets controller from 0-127 to receive this message. /// - public byte Controller => _buffer.Data[1]; + public byte Controller => _buffer.GetByte(1); /// /// Gets the array of bytes associated with the MIDI message, including status byte. diff --git a/src/Uno.UWP/Devices/Midi/MidiNoteOffMessage.cs b/src/Uno.UWP/Devices/Midi/MidiNoteOffMessage.cs index 959843c97ad0..deee6523a723 100644 --- a/src/Uno.UWP/Devices/Midi/MidiNoteOffMessage.cs +++ b/src/Uno.UWP/Devices/Midi/MidiNoteOffMessage.cs @@ -50,17 +50,17 @@ internal MidiNoteOffMessage(byte[] rawData, TimeSpan timestamp) /// /// Gets the channel from 0-15 that this message applies to. /// - public byte Channel => MidiHelpers.GetChannel(_buffer.Data[0]); + public byte Channel => MidiHelpers.GetChannel(_buffer.GetByte(0)); /// /// Gets the note to turn off which is specified as a value from 0-127. /// - public byte Note => _buffer.Data[1]; + public byte Note => _buffer.GetByte(1); /// /// Gets the value of the velocity from 0-127. /// - public byte Velocity => _buffer.Data[2]; + public byte Velocity => _buffer.GetByte(2); /// /// Gets the array of bytes associated with the MIDI message, including status byte. diff --git a/src/Uno.UWP/Devices/Midi/MidiNoteOnMessage.cs b/src/Uno.UWP/Devices/Midi/MidiNoteOnMessage.cs index b45122c515bf..83572d8c3b20 100644 --- a/src/Uno.UWP/Devices/Midi/MidiNoteOnMessage.cs +++ b/src/Uno.UWP/Devices/Midi/MidiNoteOnMessage.cs @@ -50,17 +50,17 @@ internal MidiNoteOnMessage(byte[] rawData, TimeSpan timestamp) /// /// Gets the channel from 0-15 that this message applies to. /// - public byte Channel => MidiHelpers.GetChannel(_buffer.Data[0]); + public byte Channel => MidiHelpers.GetChannel(_buffer.GetByte(0)); /// /// Gets the note to turn off which is specified as a value from 0-127. /// - public byte Note => _buffer.Data[1]; + public byte Note => _buffer.GetByte(1); /// /// Gets the value of the velocity from 0-127. /// - public byte Velocity => _buffer.Data[2]; + public byte Velocity => _buffer.GetByte(2); /// /// Gets the array of bytes associated with the MIDI message, including status byte. diff --git a/src/Uno.UWP/Devices/Midi/MidiPitchBendChangeMessage.cs b/src/Uno.UWP/Devices/Midi/MidiPitchBendChangeMessage.cs index ec5cde7ca1f9..de7069df8331 100644 --- a/src/Uno.UWP/Devices/Midi/MidiPitchBendChangeMessage.cs +++ b/src/Uno.UWP/Devices/Midi/MidiPitchBendChangeMessage.cs @@ -48,12 +48,12 @@ internal MidiPitchBendChangeMessage(byte[] rawData, TimeSpan timestamp) /// /// Gets the channel from 0-15 that this message applies to. /// - public byte Channel => MidiHelpers.GetChannel(_buffer.Data[0]); + public byte Channel => MidiHelpers.GetChannel(_buffer.GetByte(0)); /// /// Gets the pitch bend value which is specified as a 14-bit value from 0-16383. /// - public ushort Bend => MidiHelpers.GetBend(_buffer.Data[1], _buffer.Data[2]); + public ushort Bend => MidiHelpers.GetBend(_buffer.GetByte(1), _buffer.GetByte(2)); /// /// Gets the array of bytes associated with the MIDI message, including status byte. diff --git a/src/Uno.UWP/Devices/Midi/MidiPolyphonicKeyPressureMessage.cs b/src/Uno.UWP/Devices/Midi/MidiPolyphonicKeyPressureMessage.cs index 3c495c7ecfdb..8dddeb718674 100644 --- a/src/Uno.UWP/Devices/Midi/MidiPolyphonicKeyPressureMessage.cs +++ b/src/Uno.UWP/Devices/Midi/MidiPolyphonicKeyPressureMessage.cs @@ -51,17 +51,17 @@ internal MidiPolyphonicKeyPressureMessage(byte[] rawData, TimeSpan timestamp) /// /// Gets the channel from 0-15 that this message applies to. /// - public byte Channel => MidiHelpers.GetChannel(_buffer.Data[0]); + public byte Channel => MidiHelpers.GetChannel(_buffer.GetByte(0)); /// /// Gets the note which is specified as a value from 0-127. /// - public byte Note => _buffer.Data[1]; + public byte Note => _buffer.GetByte(1); /// /// Gets the polyphonic key pressure which is specified as a value from 0-127. /// - public byte Pressure => _buffer.Data[2]; + public byte Pressure => _buffer.GetByte(2); /// /// Gets the array of bytes associated with the MIDI message, including status byte. diff --git a/src/Uno.UWP/Devices/Midi/MidiProgramChangeMessage.cs b/src/Uno.UWP/Devices/Midi/MidiProgramChangeMessage.cs index 8ad845c032ab..603fc793e72f 100644 --- a/src/Uno.UWP/Devices/Midi/MidiProgramChangeMessage.cs +++ b/src/Uno.UWP/Devices/Midi/MidiProgramChangeMessage.cs @@ -47,12 +47,12 @@ internal MidiProgramChangeMessage(byte[] rawData, TimeSpan timestamp) /// /// Gets the channel from 0-15 that this message applies to. /// - public byte Channel => MidiHelpers.GetChannel(_buffer.Data[0]); + public byte Channel => MidiHelpers.GetChannel(_buffer.GetByte(0)); /// /// Gets the program to change from 0-127. /// - public byte Program => _buffer.Data[1]; + public byte Program => _buffer.GetByte(1); /// /// Gets the array of bytes associated with the MIDI message, including status byte. diff --git a/src/Uno.UWP/Devices/Midi/MidiSongPositionPointerMessage.cs b/src/Uno.UWP/Devices/Midi/MidiSongPositionPointerMessage.cs index 03f9b08fc06f..e20529118ba9 100644 --- a/src/Uno.UWP/Devices/Midi/MidiSongPositionPointerMessage.cs +++ b/src/Uno.UWP/Devices/Midi/MidiSongPositionPointerMessage.cs @@ -45,7 +45,7 @@ internal MidiSongPositionPointerMessage(byte[] rawData, TimeSpan timestamp) /// /// Gets the song position pointer encoded in a 14-bit value from 0-16383. /// - public ushort Beats => MidiHelpers.GetBeats(_buffer.Data[1], _buffer.Data[2]); + public ushort Beats => MidiHelpers.GetBeats(_buffer.GetByte(1), _buffer.GetByte(2)); /// /// Gets the array of bytes associated with the MIDI message, including status byte. diff --git a/src/Uno.UWP/Devices/Midi/MidiSongSelectMessage.cs b/src/Uno.UWP/Devices/Midi/MidiSongSelectMessage.cs index 857e1aa1ddec..b6c495ed32b4 100644 --- a/src/Uno.UWP/Devices/Midi/MidiSongSelectMessage.cs +++ b/src/Uno.UWP/Devices/Midi/MidiSongSelectMessage.cs @@ -50,7 +50,7 @@ internal MidiSongSelectMessage(byte[] rawData, TimeSpan timestamp) /// /// Gets the song to select from 0-127. /// - public byte Song => _buffer.Data[1]; + public byte Song => _buffer.GetByte(1); /// /// Gets the duration from when the MidiInPort was created to the time the message was received. diff --git a/src/Uno.UWP/Devices/Midi/MidiSystemExclusiveMessage.cs b/src/Uno.UWP/Devices/Midi/MidiSystemExclusiveMessage.cs index 0407bdce431f..6276f4d95c28 100644 --- a/src/Uno.UWP/Devices/Midi/MidiSystemExclusiveMessage.cs +++ b/src/Uno.UWP/Devices/Midi/MidiSystemExclusiveMessage.cs @@ -25,7 +25,7 @@ public MidiSystemExclusiveMessage(IBuffer rawData) } if (rawData is Storage.Streams.Buffer inMemory && - inMemory.Data[0] == (byte)MidiMessageType.EndSystemExclusive) + inMemory.GetByte(0) == (byte)MidiMessageType.EndSystemExclusive) { Type = MidiMessageType.EndSystemExclusive; } diff --git a/src/Uno.UWP/Devices/Midi/MidiTimeCodeMessage.cs b/src/Uno.UWP/Devices/Midi/MidiTimeCodeMessage.cs index d692cd7d3654..411fcf99baa8 100644 --- a/src/Uno.UWP/Devices/Midi/MidiTimeCodeMessage.cs +++ b/src/Uno.UWP/Devices/Midi/MidiTimeCodeMessage.cs @@ -47,12 +47,12 @@ internal MidiTimeCodeMessage(byte[] rawData, TimeSpan timestamp) /// /// Gets the value of the frame type from 0-7. /// - public byte FrameType => MidiHelpers.GetFrame(_buffer.Data[1]); + public byte FrameType => MidiHelpers.GetFrame(_buffer.GetByte(1)); /// /// Gets the time code value from 0-15. /// - public byte Values => MidiHelpers.GetFrameValues(_buffer.Data[1]); + public byte Values => MidiHelpers.GetFrameValues(_buffer.GetByte(1)); /// /// Gets the array of bytes associated with the MIDI message, including status byte. diff --git a/src/Uno.UWP/Storage/FileIO.cs b/src/Uno.UWP/Storage/FileIO.cs index 1f414926bf25..033ebee44e60 100644 --- a/src/Uno.UWP/Storage/FileIO.cs +++ b/src/Uno.UWP/Storage/FileIO.cs @@ -143,7 +143,7 @@ public static IAsyncAction AppendLinesAsync(IStorageFile file, IEnumerableThe array of bytes to write. /// No object or value is returned when this method completes. public static IAsyncAction WriteBytesAsync(IStorageFile file, byte[] buffer) => - WriteBytesTaskAsync(file, buffer).AsAsyncAction(); + WriteBytesTaskAsync(file, buffer, 0, buffer.Length).AsAsyncAction(); /// /// Reads the contents of the specified file and returns a buffer. @@ -249,7 +249,7 @@ private static async Task WriteTextTaskAsync(IStorageFile file, string contents, await streamWriter.WriteAsync(contents); } - private static async Task WriteBytesTaskAsync(IStorageFile file, byte[] buffer) + private static async Task WriteBytesTaskAsync(IStorageFile file, byte[] buffer, int index, int count) { if (file is null) { @@ -269,12 +269,8 @@ private static async Task ReadBufferTaskAsync(IStorageFile file) private static async Task WriteBufferTaskAsync(IStorageFile file, IBuffer buffer) { - if (!(buffer is UwpBuffer inMemoryBuffer)) - { - throw new NotSupportedException("The current implementation can only write a UwpBuffer"); - } - - await WriteBytesTaskAsync(file, inMemoryBuffer.Data); + var data = UwpBuffer.Cast(buffer).GetSegment(); + await WriteBytesTaskAsync(file, data.Array!, data.Offset, data.Count); } private static async Task GetEncodingFromFileAsync(IStorageFile file) diff --git a/src/Uno.UWP/Storage/Streams/Buffer.cs b/src/Uno.UWP/Storage/Streams/Buffer.cs index d2150f84ed24..e9daa59ab704 100644 --- a/src/Uno.UWP/Storage/Streams/Buffer.cs +++ b/src/Uno.UWP/Storage/Streams/Buffer.cs @@ -1,27 +1,90 @@ using System; +using System.Runtime.InteropServices; +using Windows.Foundation; namespace Windows.Storage.Streams { public partial class Buffer : IBuffer { + private readonly Memory _data; + + internal static Buffer Cast(IBuffer impl) + { + if (impl is Buffer buffer) + { + return buffer; + } + else + { + throw new NotSupportedException("This type of buffer is not supported."); + } + } + public Buffer(uint capacity) { - Data = new byte[capacity]; + _data = new Memory(new byte[capacity]); } internal Buffer(byte[] data) { - Data = data; + _data = new Memory(data); + Length = Capacity; } - internal byte[] Data { get; } + internal Buffer(Memory data) + { + _data = data; + Length = (uint)data.Length; + } - public uint Capacity => (uint)Data.Length; + internal Span Span => _data.Span; - public uint Length + internal byte GetByte(int index) { - get => (uint)Data.Length; - set => throw new NotSupportedException(); + return _data.Span[index]; } + + /// + /// Retrieve the underlying data array + /// + internal ArraySegment GetSegment() + { + if (MemoryMarshal.TryGetArray(_data, out var array)) + { + return array; + } + else + { + throw new InvalidOperationException("Cannot get the segment from buffer."); + } + } + + /// + /// **CLONES** the content of this buffer into a new byte[] + /// + internal byte[] ToArray() + { + return _data.ToArray(); + } + + /// + /// **CLONES** a part of the content of this buffer into a new byte[] + /// + internal byte[] ToArray(int start, int count) + { + return _data.Slice(start, count).ToArray(); + } + + internal void CopyTo(int index, byte[] destination, int destinationIndex, int count) + { + var src = _data.Slice(index, count); + var dst = new Memory(destination, destinationIndex, count); + + src.CopyTo(dst); + } + + public uint Capacity => (uint)_data.Length; + + public uint Length { get; set; } } } diff --git a/src/Uno.UWP/Storage/Streams/DataReader.cs b/src/Uno.UWP/Storage/Streams/DataReader.cs index 8a2a118b2638..c0709c91c5a4 100644 --- a/src/Uno.UWP/Storage/Streams/DataReader.cs +++ b/src/Uno.UWP/Storage/Streams/DataReader.cs @@ -11,13 +11,13 @@ namespace Windows.Storage.Streams public sealed partial class DataReader : IDataReader, IDisposable { private readonly static ArrayPool _pool = ArrayPool.Create(); - private readonly IBuffer _buffer; + private readonly Buffer _buffer; private int _bufferPosition = 0; private DataReader(IBuffer buffer) { - _buffer = buffer ?? throw new ArgumentNullException(nameof(buffer)); + _buffer = Buffer.Cast(buffer ?? throw new ArgumentNullException(nameof(buffer))); } /// @@ -247,22 +247,19 @@ public string ReadString(uint codeUnitCount) int length = (int)codeUnitCount; VerifyRead(length); - if (!(_buffer is Buffer buffer)) - { - throw new NotSupportedException("This type of buffer is not supported."); - } + var data = _buffer.GetSegment(); string result; switch (UnicodeEncoding) { case UnicodeEncoding.Utf8: - result = Encoding.UTF8.GetString(buffer.Data, _bufferPosition, length); + result = Encoding.UTF8.GetString(data.Array!, data.Offset + _bufferPosition, length); break; case UnicodeEncoding.Utf16LE: - result = Encoding.Unicode.GetString(buffer.Data, _bufferPosition, length * 2); + result = Encoding.Unicode.GetString(data.Array!, data.Offset + _bufferPosition, length * 2); break; case UnicodeEncoding.Utf16BE: - result = Encoding.BigEndianUnicode.GetString(buffer.Data, _bufferPosition, length * 2); + result = Encoding.BigEndianUnicode.GetString(data.Array!, data.Offset + _bufferPosition, length * 2); break; default: throw new InvalidOperationException("Unsupported UnicodeEncoding value."); @@ -301,29 +298,12 @@ private void VerifyRead(int count) private byte ReadByteFromBuffer() { - byte nextByte; - switch (_buffer) - { - case Buffer buffer: - nextByte = buffer.Data[_bufferPosition]; - break; - default: - throw new NotSupportedException("This buffer is not supported"); - } - _bufferPosition++; - return nextByte; + return _buffer.GetByte(_bufferPosition++); } private void ReadBytesFromBuffer(byte[] data, int length) { - switch (_buffer) - { - case Buffer buffer: - Array.Copy(buffer.Data, _bufferPosition, data, 0, length); - break; - default: - throw new NotSupportedException("This buffer is not supported"); - } + _buffer.CopyTo(_bufferPosition, data, 0, length); _bufferPosition += length; } diff --git a/src/Uno.UWP/Storage/Streams/DataWriter.cs b/src/Uno.UWP/Storage/Streams/DataWriter.cs index b3a99e523d1c..6b1c335dfbc7 100644 --- a/src/Uno.UWP/Storage/Streams/DataWriter.cs +++ b/src/Uno.UWP/Storage/Streams/DataWriter.cs @@ -73,14 +73,8 @@ public void WriteBuffer(IBuffer buffer) throw new ArgumentNullException(nameof(buffer)); } - switch (buffer) - { - case Buffer mb: - WriteBytes(mb.Data); - break; - default: - throw new NotSupportedException("This buffer is not supported"); - } + var data = Buffer.Cast(buffer).GetSegment(); + _memoryStream.Write(data.Array!, data.Offset, data.Count); } /// @@ -101,14 +95,13 @@ public void WriteBuffer(IBuffer buffer, uint start, uint count) throw new ArgumentOutOfRangeException(nameof(start)); } - switch (buffer) + var data = Buffer.Cast(buffer).GetSegment(); + if (count > data.Count) { - case Buffer mb: - _memoryStream.Write(mb.Data, (int)start, (int)count); - break; - default: - throw new NotSupportedException("This buffer is not supported"); + throw new ArgumentOutOfRangeException(nameof(count)); } + + _memoryStream.Write(data.Array!, data.Offset + (int)start, (int)count); } /// diff --git a/src/Uno.UWP/System.Runtime/WindowsRuntimeBufferExtensions.cs b/src/Uno.UWP/System.Runtime/WindowsRuntimeBufferExtensions.cs index 1a528a41d750..1be0ec8e285c 100644 --- a/src/Uno.UWP/System.Runtime/WindowsRuntimeBufferExtensions.cs +++ b/src/Uno.UWP/System.Runtime/WindowsRuntimeBufferExtensions.cs @@ -27,9 +27,24 @@ public static IBuffer AsBuffer(this byte[] source, int offset, int length, int c throw new ArgumentNullException(nameof(source)); } - var buffer = new UwpBuffer((uint)capacity); - Array.Copy(source, offset, buffer.Data, 0, length); - return buffer; + if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset), "offset is less than 0 (zero)."); + if (length < 0) throw new ArgumentOutOfRangeException(nameof(length), "length is less than 0 (zero)."); + if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity), "capacity is less than 0 (zero)."); + + if (length > capacity) + { + throw new ArgumentException("length is greater than capacity"); + } + + if (source.Length - offset < Math.Max(length, capacity)) + { + throw new ArgumentException("The array is not large enough to serve as a backing store for the IBuffer; that is, the number of bytes in source, beginning at offset, is less than length or capacity."); + } + + return new UwpBuffer(new Memory(source, offset, capacity)) + { + Length = (uint)length + }; } public static Stream AsStream(this IBuffer source) @@ -39,14 +54,10 @@ public static Stream AsStream(this IBuffer source) throw new ArgumentNullException(nameof(source)); } - switch (source) - { - case UwpBuffer mb: - return new MemoryStream(mb.Data); + var data = UwpBuffer.Cast(source).GetSegment(); + var stream = new MemoryStream(data.Array!, data.Offset, data.Count); - default: - throw new NotSupportedException("This buffer is not supported"); - } + return stream; } [Uno.NotImplemented] @@ -79,13 +90,7 @@ public static byte[] ToArray(this IBuffer source) throw new ArgumentNullException(nameof(source)); } - switch (source) - { - case UwpBuffer b: - return b.Data; - default: - throw new NotSupportedException("This buffer is not supported"); - } + return UwpBuffer.Cast(source).ToArray(); } public static byte[] ToArray(this IBuffer source, uint sourceIndex, int count) @@ -95,14 +100,7 @@ public static byte[] ToArray(this IBuffer source, uint sourceIndex, int count) throw new ArgumentNullException(nameof(source)); } - switch (source) - { - case UwpBuffer mb: - return mb.Data.Skip((int)sourceIndex).Take(count).ToArray(); - - default: - throw new NotSupportedException("This buffer is not supported"); - } + return UwpBuffer.Cast(source).ToArray(sourceIndex, count); } } }