public async Task WritesBytesFromGivenBufferToGivenFileAtGivenOffsetAsync() { const int fileSize = 4_001; string filePath = GetTestFilePath(); byte[] content = RandomNumberGenerator.GetBytes(fileSize); using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, FileOptions.Asynchronous)) { int total = 0; int current = 0; while (total != fileSize) { Memory <byte> buffer = content.AsMemory(total, Math.Min(content.Length - total, fileSize / 4)); current = await RandomAccess.WriteAsync(handle, buffer, fileOffset : total); Assert.InRange(current, 0, buffer.Length); total += current; } } Assert.Equal(content, File.ReadAllBytes(filePath)); }
public async Task WriteAsyncUsingMultipleBuffers(bool async) { string filePath = GetTestFilePath(); int bufferSize = Environment.SystemPageSize; int fileSize = bufferSize * 10; byte[] content = RandomNumberGenerator.GetBytes(fileSize); using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, FileOptions.Asynchronous | NoBuffering)) using (SectorAlignedMemory <byte> buffer_1 = SectorAlignedMemory <byte> .Allocate(bufferSize)) using (SectorAlignedMemory <byte> buffer_2 = SectorAlignedMemory <byte> .Allocate(bufferSize)) { long total = 0; IReadOnlyList <ReadOnlyMemory <byte> > buffers = new ReadOnlyMemory <byte>[] { buffer_1.Memory, buffer_2.Memory, }; while (total != fileSize) { content.AsSpan((int)total, bufferSize).CopyTo(buffer_1.GetSpan()); content.AsSpan((int)total + bufferSize, bufferSize).CopyTo(buffer_2.GetSpan()); total += async ? await RandomAccess.WriteAsync(handle, buffers, fileOffset : total) : RandomAccess.Write(handle, buffers, fileOffset: total); } } Assert.Equal(content, File.ReadAllBytes(filePath)); }
public async Task WriteUsingEmptyBufferReturnsZeroAsync() { using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.Create, FileAccess.Write, options: FileOptions.Asynchronous)) { Assert.Equal(0, await RandomAccess.WriteAsync(handle, Array.Empty <byte>(), fileOffset: 0)); } }
static async Task Validate(SafeFileHandle handle, FileOptions options, bool[] syncWrites, bool[] syncReads) { byte[] writeBuffer = new byte[1]; byte[] readBuffer = new byte[2]; long fileOffset = 0; foreach (bool syncWrite in syncWrites) { foreach (bool syncRead in syncReads) { writeBuffer[0] = (byte)fileOffset; if (syncWrite) { RandomAccess.Write(handle, writeBuffer, fileOffset); } else { await RandomAccess.WriteAsync(handle, writeBuffer, fileOffset); } Assert.Equal(writeBuffer.Length, syncRead ? RandomAccess.Read(handle, readBuffer, fileOffset) : await RandomAccess.ReadAsync(handle, readBuffer, fileOffset)); Assert.Equal(writeBuffer[0], readBuffer[0]); fileOffset += 1; } } }
public async Task WritesBytesFromGivenBufferToGivenFileAtGivenOffsetAsync(FileOptions options) { const int fileSize = 4_001; string filePath = GetTestFilePath(); byte[] content = RandomNumberGenerator.GetBytes(fileSize); using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, options)) { long total = 0; while (total != fileSize) { int firstBufferLength = (int)Math.Min(content.Length - total, fileSize / 4); Memory <byte> buffer_1 = content.AsMemory((int)total, firstBufferLength); Memory <byte> buffer_2 = content.AsMemory((int)total + firstBufferLength); await RandomAccess.WriteAsync( handle, new ReadOnlyMemory <byte>[] { buffer_1, Array.Empty <byte>(), buffer_2 }, fileOffset : total); total += buffer_1.Length + buffer_2.Length; } } Assert.Equal(content, File.ReadAllBytes(filePath)); }
public async Task ReadWriteAsyncUsingNonPageSizedMultipleBuffers() { string filePath = GetTestFilePath(); // The Windows scatter/gather APIs accept segments that are exactly one page long. // Combined with the FILE_FLAG_NO_BUFFERING's requirements, the segments must also // be aligned at page size boundaries and have a size of a multiple of the page size. // Using segments with a length of twice the page size adheres to the second requirement // but not the first. The RandomAccess implementation will see it and issue sequential // read/write syscalls per segment, instead of one scatter/gather syscall. // This test verifies that fallback behavior. int bufferSize = Environment.SystemPageSize * 2; int fileSize = bufferSize * 2; byte[] content = RandomNumberGenerator.GetBytes(fileSize); using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, FileOptions.Asynchronous | NoBuffering)) using (SectorAlignedMemory <byte> buffer = SectorAlignedMemory <byte> .Allocate(fileSize)) { Memory <byte> firstHalf = buffer.Memory.Slice(0, bufferSize); Memory <byte> secondHalf = buffer.Memory.Slice(bufferSize); content.AsSpan().CopyTo(buffer.GetSpan()); await RandomAccess.WriteAsync(handle, new ReadOnlyMemory <byte>[] { firstHalf, secondHalf }, 0); buffer.GetSpan().Clear(); await RandomAccess.ReadAsync(handle, new Memory <byte>[] { firstHalf, secondHalf }, 0); } Assert.Equal(content, await File.ReadAllBytesAsync(filePath)); }
public async Task WriteUsingEmptyBufferReturnsZeroAsync(FileOptions options) { using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.Create, FileAccess.Write, options: options)) { Assert.Equal(0, await RandomAccess.WriteAsync(handle, new ReadOnlyMemory <byte>[] { Array.Empty <byte>() }, fileOffset: 0)); } }
public async Task WriteUsingSingleBuffer(bool asyncOperation, bool asyncHandle) { string filePath = GetTestFilePath(); int bufferSize = Environment.SystemPageSize; int fileSize = bufferSize * 10; byte[] content = RandomNumberGenerator.GetBytes(fileSize); using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, GetFileOptions(asyncHandle))) using (SectorAlignedMemory <byte> buffer = SectorAlignedMemory <byte> .Allocate(bufferSize)) { int total = 0; while (total != fileSize) { int take = Math.Min(content.Length - total, bufferSize); content.AsSpan(total, take).CopyTo(buffer.GetSpan()); if (asyncOperation) { await RandomAccess.WriteAsync(handle, buffer.Memory, fileOffset : total); } else { RandomAccess.Write(handle, buffer.GetSpan(), fileOffset: total); } total += buffer.Memory.Length; } } Assert.Equal(content, File.ReadAllBytes(filePath)); }
public async Task ReadWriteAsyncUsingMultipleBuffers(bool memoryPageSized) { string filePath = GetTestFilePath(); // We test with buffers both one and two memory pages long. In the former case, // the I/O operations will issue one scatter/gather API call, and in the latter // case they will issue multiple calls; one per buffer. The buffers must still // be aligned to comply with FILE_FLAG_NO_BUFFERING's requirements. int bufferSize = Environment.SystemPageSize * (memoryPageSized ? 1 : 2); int fileSize = bufferSize * 2; byte[] content = RandomNumberGenerator.GetBytes(fileSize); using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, FileOptions.Asynchronous | NoBuffering)) using (SectorAlignedMemory <byte> buffer = SectorAlignedMemory <byte> .Allocate(fileSize)) { Memory <byte> firstHalf = buffer.Memory.Slice(0, bufferSize); Memory <byte> secondHalf = buffer.Memory.Slice(bufferSize); content.AsSpan().CopyTo(buffer.GetSpan()); await RandomAccess.WriteAsync(handle, new ReadOnlyMemory <byte>[] { firstHalf, secondHalf }, 0); buffer.GetSpan().Clear(); long nRead = await RandomAccess.ReadAsync(handle, new Memory <byte>[] { firstHalf, secondHalf }, 0); Assert.Equal(buffer.GetSpan().Length, nRead); AssertExtensions.SequenceEqual(buffer.GetSpan(), content.AsSpan()); } }
public void ThrowsArgumentNullExceptionForNullBuffers(FileOptions options) { using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.Write, FileShare.None, options)) { AssertExtensions.Throws <ArgumentNullException>("buffers", () => RandomAccess.WriteAsync(handle, buffers: null, 0)); } }
public async Task WriteUsingEmptyBufferReturnsAsync(FileOptions options) { using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.Create, FileAccess.Write, options: options)) { await RandomAccess.WriteAsync(handle, Array.Empty <byte>(), fileOffset : 0); } }
/// <summary> /// Writes the bucket to <paramref name="stream"/> and then closes <paramref name="bucket"/> /// </summary> /// <param name="stream"></param> /// <param name="bucket"></param> /// <param name="cancellationToken">Token for <see cref="Stream.WriteAsync(byte[], int, int, CancellationToken)"/></param> /// <returns></returns> /// <exception cref="ArgumentNullException"></exception> public static async ValueTask WriteToAsync(this Bucket bucket, Stream stream, CancellationToken cancellationToken = default) { if (bucket is null) { throw new ArgumentNullException(nameof(bucket)); } else if (stream is null) { throw new ArgumentNullException(nameof(stream)); } using (bucket) { #if NET6_0_OR_GREATER if (stream is FileStream fs && fs.CanSeek && bucket is IBucketReadBuffers rb) { var handle = fs.SafeFileHandle; long pos = fs.Position; while (true) { cancellationToken.ThrowIfCancellationRequested(); var(buffers, done) = await bucket.ReadBuffersAsync().ConfigureAwait(false); if (buffers.Length > 0) { var len = buffers.Sum(x => (long)x.Length); await RandomAccess.WriteAsync(handle, buffers, pos, cancellationToken).ConfigureAwait(false); pos += len; } if (done) { fs.Position = pos; return; } } } #endif while (true) { cancellationToken.ThrowIfCancellationRequested(); var bb = await bucket.ReadAsync().ConfigureAwait(false); if (bb.IsEof) { break; } await stream.WriteAsync(bb, cancellationToken).ConfigureAwait(false); } } }
public async Task ReadWriteAsyncUsingEmptyBuffers() { string filePath = GetTestFilePath(); using SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, FileOptions.Asynchronous | NoBuffering); long nRead = await RandomAccess.ReadAsync(handle, Array.Empty <Memory <byte> >(), 0); Assert.Equal(0, nRead); await RandomAccess.WriteAsync(handle, Array.Empty <ReadOnlyMemory <byte> >(), 0); }
public async Task TaskAlreadyCanceledAsync(FileOptions options) { using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.ReadWrite, options: options)) { CancellationTokenSource cts = GetCancelledTokenSource(); CancellationToken token = cts.Token; Assert.True(RandomAccess.WriteAsync(handle, new ReadOnlyMemory <byte>[] { new byte[1] }, 0, token).IsCanceled); TaskCanceledException ex = await Assert.ThrowsAsync <TaskCanceledException>(() => RandomAccess.WriteAsync(handle, new ReadOnlyMemory <byte>[] { new byte[1] }, 0, token).AsTask()); Assert.Equal(token, ex.CancellationToken); } }
public async Task WriteBeyondEndOfFileExtendsTheFileAsync(FileOptions options) { string filePath = GetTestFilePath(); using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, options: options)) { Assert.Equal(0, RandomAccess.GetLength(handle)); await RandomAccess.WriteAsync(handle, new byte[1] { 1 }, fileOffset : 1); Assert.Equal(2, RandomAccess.GetLength(handle)); } Assert.Equal(new byte[] { 0, 1 }, await File.ReadAllBytesAsync(filePath)); }
public async Task DuplicatedBufferDuplicatesContentAsync(FileOptions options) { const byte value = 1; const int repeatCount = 2; string filePath = GetTestFilePath(); ReadOnlyMemory <byte> buffer = new byte[1] { value }; List <ReadOnlyMemory <byte> > buffers = Enumerable.Repeat(buffer, repeatCount).ToList(); using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Create, FileAccess.Write, options: options)) { Assert.Equal(repeatCount, await RandomAccess.WriteAsync(handle, buffers, fileOffset: 0)); } byte[] actualContent = File.ReadAllBytes(filePath); Assert.Equal(repeatCount, actualContent.Length); Assert.All(actualContent, actual => Assert.Equal(value, actual)); }
public async Task ThrowsOnReadAccess(FileOptions options) { using (SafeFileHandle handle = GetHandleToExistingFile(FileAccess.Read, options)) { await Assert.ThrowsAsync <UnauthorizedAccessException>(async() => await RandomAccess.WriteAsync(handle, new ReadOnlyMemory <byte>[] { new byte[1] }, 0)); } }
protected override ValueTask <long> MethodUnderTest(SafeFileHandle handle, byte[] bytes, long fileOffset) => RandomAccess.WriteAsync(handle, new ReadOnlyMemory <byte>[] { bytes }, fileOffset);
public async Task ThrowsOnReadAccess() { using (SafeFileHandle handle = GetHandleToExistingFile(FileAccess.Read)) { await Assert.ThrowsAsync <UnauthorizedAccessException>(async() => await RandomAccess.WriteAsync(handle, new byte[1], 0)); } }
protected override ValueTask MethodUnderTest(SafeFileHandle handle, byte[] bytes, long fileOffset) => RandomAccess.WriteAsync(handle, bytes, fileOffset);