Exemple #1
0
        public async Task ReadsBytesFromGivenFileAtGivenOffsetAsync()
        {
            const int fileSize = 4_001;
            string    filePath = GetTestFilePath();

            byte[] expected = RandomNumberGenerator.GetBytes(fileSize);
            File.WriteAllBytes(filePath, expected);

            using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: FileOptions.Asynchronous))
            {
                byte[] actual  = new byte[fileSize + 1];
                int    current = 0;
                int    total   = 0;

                do
                {
                    Memory <byte> buffer = actual.AsMemory(total, Math.Min(actual.Length - total, fileSize / 4));

                    current = await RandomAccess.ReadAsync(handle, buffer, fileOffset : total);

                    Assert.InRange(current, 0, buffer.Length);

                    total += current;
                } while (current != 0);

                Assert.Equal(fileSize, total);
                Assert.Equal(expected, actual.Take(total).ToArray());
            }
        }
        public async Task ReadAsyncUsingSingleBuffer()
        {
            const int fileSize = 1_000_000; // 1 MB
            string    filePath = GetTestFilePath();

            byte[] expected = RandomNumberGenerator.GetBytes(fileSize);
            File.WriteAllBytes(filePath, expected);

            using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: FileOptions.Asynchronous | NoBuffering))
                using (SectorAlignedMemory <byte> buffer = SectorAlignedMemory <byte> .Allocate(Environment.SystemPageSize))
                {
                    int current = 0;
                    int total   = 0;

                    // From https://docs.microsoft.com/en-us/windows/win32/fileio/file-buffering:
                    // "File access sizes, including the optional file offset in the OVERLAPPED structure,
                    // if specified, must be for a number of bytes that is an integer multiple of the volume sector size."
                    // So if buffer and physical sector size is 4096 and the file size is 4097:
                    // the read from offset=0 reads 4096 bytes
                    // the read from offset=4096 reads 1 byte
                    // the read from offset=4097 THROWS (Invalid argument, offset is not a multiple of sector size!)
                    // That is why we stop at the first incomplete read (the next one would throw).
                    // It's possible to get 0 if we are lucky and file size is a multiple of physical sector size.
                    do
                    {
                        current = await RandomAccess.ReadAsync(handle, buffer.Memory, fileOffset : total);

                        Assert.True(expected.AsSpan(total, current).SequenceEqual(buffer.GetSpan().Slice(0, current)));

                        total += current;
                    }while (current == buffer.Memory.Length);

                    Assert.Equal(fileSize, total);
                }
        }
Exemple #3
0
        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));
        }
Exemple #4
0
 public void ThrowsArgumentNullExceptionForNullBuffers(FileOptions options)
 {
     using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.Write, options: options))
     {
         AssertExtensions.Throws <ArgumentNullException>("buffers", () => RandomAccess.ReadAsync(handle, buffers: null, 0));
     }
 }
Exemple #5
0
        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());
                }
        }
Exemple #6
0
        public async Task ReadUsingSingleBuffer(bool asyncOperation, bool asyncHandle)
        {
            const int fileSize = 1_000_000; // 1 MB
            string    filePath = GetTestFilePath();

            byte[] expected = RandomNumberGenerator.GetBytes(fileSize);
            File.WriteAllBytes(filePath, expected);

            using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: GetFileOptions(asyncHandle)))
                using (SectorAlignedMemory <byte> buffer = SectorAlignedMemory <byte> .Allocate(Environment.SystemPageSize))
                {
                    int current = 0;
                    int total   = 0;

                    do
                    {
                        current = asyncOperation
                        ? await RandomAccess.ReadAsync(handle, buffer.Memory, fileOffset : total)
                        : RandomAccess.Read(handle, buffer.GetSpan(), fileOffset: total);

                        Assert.True(expected.AsSpan(total, current).SequenceEqual(buffer.GetSpan().Slice(0, current)));

                        total += current;
                    }while (current != 0);

                    Assert.Equal(fileSize, total);
                }
        }
Exemple #7
0
        public async Task ReadToAnEmptyBufferReturnsZeroAsync()
        {
            string filePath = GetTestFilePath();

            File.WriteAllBytes(filePath, new byte[1]);

            using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: FileOptions.Asynchronous))
            {
                Assert.Equal(0, await RandomAccess.ReadAsync(handle, Array.Empty <byte>(), fileOffset: 0));
            }
        }
Exemple #8
0
        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);
        }
Exemple #9
0
        public async Task ReadFromBeyondEndOfFileReturnsZeroAsync(FileOptions options)
        {
            string filePath = GetTestFilePath();

            File.WriteAllBytes(filePath, new byte[100]);

            using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: options))
            {
                long eof = RandomAccess.GetLength(handle);
                Assert.Equal(0, await RandomAccess.ReadAsync(handle, new Memory <byte>[] { new byte[1] }, fileOffset: eof + 1));
            }
        }
Exemple #10
0
        public async Task TaskAlreadyCanceledAsync()
        {
            using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.ReadWrite, options: FileOptions.Asynchronous))
            {
                CancellationTokenSource cts   = GetCancelledTokenSource();
                CancellationToken       token = cts.Token;

                Assert.True(RandomAccess.ReadAsync(handle, new byte[1], 0, token).IsCanceled);

                TaskCanceledException ex = await Assert.ThrowsAsync <TaskCanceledException>(() => RandomAccess.ReadAsync(handle, new byte[1], 0, token).AsTask());

                Assert.Equal(token, ex.CancellationToken);
            }
        }
Exemple #11
0
        public async Task ReadToTheSameBufferOverwritesContent(FileOptions options)
        {
            string filePath = GetTestFilePath();

            File.WriteAllBytes(filePath, new byte[3] {
                1, 2, 3
            });

            using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: options))
            {
                byte[] buffer = new byte[1];
                Assert.Equal(buffer.Length + buffer.Length, await RandomAccess.ReadAsync(handle, Enumerable.Repeat(buffer.AsMemory(), 2).ToList(), fileOffset: 0));
                Assert.Equal(2, buffer[0]);
            }
        }
Exemple #12
0
        public async Task ReadsBytesFromGivenFileAtGivenOffsetAsync(FileOptions options)
        {
            const int fileSize = 4_001;
            string    filePath = GetTestFilePath();

            byte[] expected = RandomNumberGenerator.GetBytes(fileSize);
            File.WriteAllBytes(filePath, expected);

            using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: options))
            {
                byte[] actual  = new byte[fileSize + 1];
                long   current = 0;
                long   total   = 0;

                do
                {
                    int           firstBufferLength = (int)Math.Min(actual.Length - total, fileSize / 4);
                    Memory <byte> buffer_1          = actual.AsMemory((int)total, firstBufferLength);
                    Memory <byte> buffer_2          = actual.AsMemory((int)total + firstBufferLength);

                    current = await RandomAccess.ReadAsync(
                        handle,
                        new Memory <byte>[]
                    {
                        buffer_1,
                        Array.Empty <byte>(),
                        buffer_2
                    },
                        fileOffset : total);

                    Assert.InRange(current, 0, buffer_1.Length + buffer_2.Length);

                    total += current;
                } while (current != 0);

                Assert.Equal(fileSize, total);
                Assert.Equal(expected, actual.Take((int)total).ToArray());
            }
        }
Exemple #13
0
        public async Task ReadAsyncUsingMultipleBuffers(bool asyncOperation, bool asyncHandle)
        {
            const int fileSize = 1_000_000; // 1 MB
            string    filePath = GetTestFilePath();

            byte[] expected = RandomNumberGenerator.GetBytes(fileSize);
            File.WriteAllBytes(filePath, expected);

            using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: GetFileOptions(asyncHandle)))
                using (SectorAlignedMemory <byte> buffer_1 = SectorAlignedMemory <byte> .Allocate(Environment.SystemPageSize))
                    using (SectorAlignedMemory <byte> buffer_2 = SectorAlignedMemory <byte> .Allocate(Environment.SystemPageSize))
                    {
                        long current = 0;
                        long total   = 0;

                        IReadOnlyList <Memory <byte> > buffers = new Memory <byte>[]
                        {
                            buffer_1.Memory,
                            buffer_2.Memory,
                        };

                        do
                        {
                            current = asyncOperation
                        ? await RandomAccess.ReadAsync(handle, buffers, fileOffset : total)
                        : RandomAccess.Read(handle, buffers, fileOffset: total);

                            int takeFromFirst = Math.Min(buffer_1.Memory.Length, (int)current);
                            Assert.True(expected.AsSpan((int)total, takeFromFirst).SequenceEqual(buffer_1.GetSpan().Slice(0, takeFromFirst)));
                            int takeFromSecond = (int)current - takeFromFirst;
                            Assert.True(expected.AsSpan((int)total + takeFromFirst, takeFromSecond).SequenceEqual(buffer_2.GetSpan().Slice(0, takeFromSecond)));

                            total += current;
                        } while (current == buffer_1.Memory.Length + buffer_2.Memory.Length);

                        Assert.Equal(fileSize, total);
                    }
        }
Exemple #14
0
 public async Task ThrowsOnWriteAccess()
 {
     using (SafeFileHandle handle = GetHandleToExistingFile(FileAccess.Write))
     {
         await Assert.ThrowsAsync <UnauthorizedAccessException>(async() => await RandomAccess.ReadAsync(handle, new byte[1], 0));
     }
 }
Exemple #15
0
 protected override ValueTask <int> MethodUnderTest(SafeFileHandle handle, byte[] bytes, long fileOffset)
 => RandomAccess.ReadAsync(handle, bytes, fileOffset);
Exemple #16
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;
                    }
                }
            }
Exemple #17
0
 protected override ValueTask <long> MethodUnderTest(SafeFileHandle handle, byte[] bytes, long fileOffset)
 => RandomAccess.ReadAsync(handle, new Memory <byte>[] { bytes }, fileOffset);