} // ReadAsync_MemoryStream internal static IAsyncOperationWithProgress <IBuffer, uint> ReadAsync_AbstractStream(Stream stream, IBuffer buffer, uint count, InputStreamOptions options) { Debug.Assert(stream != null); Debug.Assert(stream.CanRead); Debug.Assert(buffer != null); Debug.Assert(0 <= count); Debug.Assert(count <= int.MaxValue); Debug.Assert(count <= buffer.Capacity); Debug.Assert(options == InputStreamOptions.None || options == InputStreamOptions.Partial || options == InputStreamOptions.ReadAhead); int bytesrequested = (int)count; // Check if the buffer is our implementation. // IF YES: In that case, we can read directly into its data array. // IF NO: The buffer is of unknown implementation. It's not backed by a managed array, but the wrapped stream can only // read into a managed array. If we used the user-supplied buffer we would need to copy data into it after every read. // The spec allows to return a buffer instance that is not the same as passed by the user. So, we will create an own // buffer instance, read data *directly* into the array backing it and then return it to the user. // Note: the allocation costs we are paying for the new buffer are unavoidable anyway, as we would need to create // an array to read into either way. IBuffer dataBuffer = buffer as WindowsRuntimeBuffer; if (dataBuffer == null) { dataBuffer = WindowsRuntimeBuffer.Create((int)Math.Min((uint)int.MaxValue, buffer.Capacity)); } // This operation delegate will we run inside of the returned IAsyncOperationWithProgress: Func <CancellationToken, IProgress <uint>, Task <IBuffer> > readOperation = async(cancelToken, progressListener) => { // No bytes read yet: dataBuffer.Length = 0; // Get the buffer backing array: byte[] data; int offset; bool managedBufferAssert = dataBuffer.TryGetUnderlyingData(out data, out offset); Debug.Assert(managedBufferAssert); // Init tracking values: bool done = cancelToken.IsCancellationRequested; int bytesCompleted = 0; // Loop until EOS, cancelled or read enough data according to options: while (!done) { int bytesread = 0; try { // Read asynchronously: bytesread = await stream.ReadAsync(data !, offset + bytesCompleted, bytesrequested - bytesCompleted, cancelToken) .ConfigureAwait(continueOnCapturedContext: false); // We will continue here on a different thread when read async completed: bytesCompleted += bytesread; // We will handle a cancelation exception and re-throw all others: } catch (OperationCanceledException) { // We assume that cancelToken.IsCancellationRequested is has been set and simply proceed. // (we check cancelToken.IsCancellationRequested later) Debug.Assert(cancelToken.IsCancellationRequested); // This is because if the cancellation came after we read some bytes we want to return the results we got instead // of an empty cancelled task, so if we have not yet read anything at all, then we can throw cancellation: if (bytesCompleted == 0 && bytesread == 0) { throw; } } // Update target buffer: dataBuffer.Length = (uint)bytesCompleted; Debug.Assert(bytesCompleted <= bytesrequested); // Check if we are done: done = options == InputStreamOptions.Partial || // If no complete read was requested, any amount of data is OK bytesread == 0 || // this implies EndOfStream bytesCompleted == bytesrequested || // read all requested bytes cancelToken.IsCancellationRequested; // operation was cancelled // Call user Progress handler: if (progressListener != null) { progressListener.Report(dataBuffer.Length); } } // while (!done) // If we got here, then no error was detected. Return the results buffer: return(dataBuffer); }; // readOperation return(AsyncInfo.Run <IBuffer, uint>(readOperation)); } // ReadAsync_AbstractStream
} // ReadAsync_AbstractStream #endregion ReadAsync implementations #region WriteAsync implementations internal static IAsyncOperationWithProgress <uint, uint> WriteAsync_AbstractStream(Stream stream, IBuffer buffer) { Debug.Assert(stream != null); Debug.Assert(stream.CanWrite); Debug.Assert(buffer != null); // Choose the optimal writing strategy for the kind of buffer supplied: Func <CancellationToken, IProgress <uint>, Task <uint> > writeOperation; byte[] data; int offset; // If buffer is backed by a managed array: if (buffer.TryGetUnderlyingData(out data, out offset)) { writeOperation = async(cancelToken, progressListener) => { if (cancelToken.IsCancellationRequested) // CancellationToken is non-nullable { return(0); } Debug.Assert(buffer.Length <= int.MaxValue); int bytesToWrite = (int)buffer.Length; await stream.WriteAsync(data, offset, bytesToWrite, cancelToken).ConfigureAwait(continueOnCapturedContext: false); if (progressListener != null) { progressListener.Report((uint)bytesToWrite); } return((uint)bytesToWrite); }; // Otherwise buffer is of an unknown implementation: } else { writeOperation = async(cancelToken, progressListener) => { if (cancelToken.IsCancellationRequested) // CancellationToken is non-nullable { return(0); } uint bytesToWrite = buffer.Length; Stream dataStream = buffer.AsStream(); int buffSize = 0x4000; if (bytesToWrite < buffSize) { buffSize = (int)bytesToWrite; } if (buffSize > 0) { await dataStream.CopyToAsync(stream, buffSize, cancelToken).ConfigureAwait(continueOnCapturedContext: false); } if (progressListener != null) { progressListener.Report((uint)bytesToWrite); } return((uint)bytesToWrite); }; } // if-else // Construct and run the async operation: return(AsyncInfo.Run <uint, uint>(writeOperation)); } // WriteAsync_AbstractStream
public static bool IsSameData(this IBuffer buffer, IBuffer otherBuffer) { if (buffer == null) throw new ArgumentNullException(nameof(buffer)); Contract.EndContractBlock(); if (otherBuffer == null) return false; if (buffer == otherBuffer) return true; Byte[] thisDataArr, otherDataArr; Int32 thisDataOffs, otherDataOffs; bool thisIsManaged = buffer.TryGetUnderlyingData(out thisDataArr, out thisDataOffs); bool otherIsManaged = otherBuffer.TryGetUnderlyingData(out otherDataArr, out otherDataOffs); if (thisIsManaged != otherIsManaged) return false; if (thisIsManaged) return (thisDataArr == otherDataArr) && (thisDataOffs == otherDataOffs); IBufferByteAccess thisBuff = (IBufferByteAccess)buffer; IBufferByteAccess otherBuff = (IBufferByteAccess)otherBuffer; unsafe { return (thisBuff.GetBuffer() == otherBuff.GetBuffer()); } }
public static void CopyTo(this IBuffer source, UInt32 sourceIndex, IBuffer destination, UInt32 destinationIndex, UInt32 count) { if (source == null) throw new ArgumentNullException(nameof(source)); if (destination == null) throw new ArgumentNullException(nameof(destination)); if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); if (sourceIndex < 0) throw new ArgumentOutOfRangeException(nameof(sourceIndex)); if (destinationIndex < 0) throw new ArgumentOutOfRangeException(nameof(destinationIndex)); if (source.Capacity <= sourceIndex) throw new ArgumentException(SR.Argument_BufferIndexExceedsCapacity); if (source.Capacity - sourceIndex < count) throw new ArgumentException(SR.Argument_InsufficientSpaceInSourceBuffer); if (destination.Capacity <= destinationIndex) throw new ArgumentException(SR.Argument_BufferIndexExceedsCapacity); if (destination.Capacity - destinationIndex < count) throw new ArgumentException(SR.Argument_InsufficientSpaceInTargetBuffer); Contract.EndContractBlock(); // If source are destination are backed by managed arrays, use the arrays instead of the pointers as it does not require pinning: Byte[] srcDataArr, destDataArr; Int32 srcDataOffs, destDataOffs; bool srcIsManaged = source.TryGetUnderlyingData(out srcDataArr, out srcDataOffs); bool destIsManaged = destination.TryGetUnderlyingData(out destDataArr, out destDataOffs); if (srcIsManaged && destIsManaged) { Debug.Assert(count <= Int32.MaxValue); Debug.Assert(sourceIndex <= Int32.MaxValue); Debug.Assert(destinationIndex <= Int32.MaxValue); Buffer.BlockCopy(srcDataArr, srcDataOffs + (Int32)sourceIndex, destDataArr, destDataOffs + (Int32)destinationIndex, (Int32)count); return; } IntPtr srcPtr, destPtr; if (srcIsManaged) { Debug.Assert(count <= Int32.MaxValue); Debug.Assert(sourceIndex <= Int32.MaxValue); destPtr = destination.GetPointerAtOffset(destinationIndex); Marshal.Copy(srcDataArr, srcDataOffs + (Int32)sourceIndex, destPtr, (Int32)count); return; } if (destIsManaged) { Debug.Assert(count <= Int32.MaxValue); Debug.Assert(destinationIndex <= Int32.MaxValue); srcPtr = source.GetPointerAtOffset(sourceIndex); Marshal.Copy(srcPtr, destDataArr, destDataOffs + (Int32)destinationIndex, (Int32)count); return; } srcPtr = source.GetPointerAtOffset(sourceIndex); destPtr = destination.GetPointerAtOffset(destinationIndex); MemCopy(srcPtr, destPtr, count); }
public static void CopyTo(this Byte[] source, Int32 sourceIndex, IBuffer destination, UInt32 destinationIndex, Int32 count) { if (source == null) throw new ArgumentNullException(nameof(source)); if (destination == null) throw new ArgumentNullException(nameof(destination)); if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); if (sourceIndex < 0) throw new ArgumentOutOfRangeException(nameof(sourceIndex)); if (destinationIndex < 0) throw new ArgumentOutOfRangeException(nameof(destinationIndex)); if (source.Length <= sourceIndex) throw new ArgumentException(SR.Argument_IndexOutOfArrayBounds, nameof(sourceIndex)); if (source.Length - sourceIndex < count) throw new ArgumentException(SR.Argument_InsufficientArrayElementsAfterOffset); if (destination.Capacity - destinationIndex < count) throw new ArgumentException(SR.Argument_InsufficientSpaceInTargetBuffer); Contract.EndContractBlock(); // If destination is backed by a managed array, use the array instead of the pointer as it does not require pinning: Byte[] destDataArr; Int32 destDataOffs; if (destination.TryGetUnderlyingData(out destDataArr, out destDataOffs)) { Buffer.BlockCopy(source, sourceIndex, destDataArr, (int)(destDataOffs + destinationIndex), count); return; } IntPtr destPtr = destination.GetPointerAtOffset(destinationIndex); Marshal.Copy(source, sourceIndex, destPtr, count); }
} // ReadAsync_AbstractStream #endregion ReadAsync implementations #region WriteAsync implementations internal static IAsyncOperationWithProgress<UInt32, UInt32> WriteAsync_AbstractStream(Stream stream, IBuffer buffer) { Debug.Assert(stream != null); Debug.Assert(stream.CanWrite); Debug.Assert(buffer != null); Contract.EndContractBlock(); // Choose the optimal writing strategy for the kind of buffer supplied: Func<CancellationToken, IProgress<UInt32>, Task<UInt32>> writeOperation; Byte[] data; Int32 offset; // If buffer is backed by a managed array: if (buffer.TryGetUnderlyingData(out data, out offset)) { writeOperation = async (cancelToken, progressListener) => { if (cancelToken.IsCancellationRequested) // CancellationToken is non-nullable return 0; Debug.Assert(buffer.Length <= Int32.MaxValue); Int32 bytesToWrite = (Int32)buffer.Length; await stream.WriteAsync(data, offset, bytesToWrite, cancelToken).ConfigureAwait(continueOnCapturedContext: false); if (progressListener != null) progressListener.Report((UInt32)bytesToWrite); return (UInt32)bytesToWrite; }; // Otherwise buffer is of an unknown implementation: } else { writeOperation = async (cancelToken, progressListener) => { if (cancelToken.IsCancellationRequested) // CancellationToken is non-nullable return 0; UInt32 bytesToWrite = buffer.Length; Stream dataStream = buffer.AsStream(); Int32 buffSize = 0x4000; if (bytesToWrite < buffSize) buffSize = (Int32)bytesToWrite; await dataStream.CopyToAsync(stream, buffSize, cancelToken).ConfigureAwait(continueOnCapturedContext: false); if (progressListener != null) progressListener.Report((UInt32)bytesToWrite); return (UInt32)bytesToWrite; }; } // if-else // Construct and run the async operation: return AsyncInfo.Run<UInt32, UInt32>(writeOperation); } // WriteAsync_AbstractStream