| File: Linux\OSFileSystem.cs | Web Access |
| Project: src\src\Libraries\Microsoft.Extensions.Diagnostics.ResourceMonitoring\Microsoft.Extensions.Diagnostics.ResourceMonitoring.csproj (Microsoft.Extensions.Diagnostics.ResourceMonitoring) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Buffers; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using Microsoft.Shared.Pools; namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux; /// <remarks> /// We are reading files from /proc and /cgroup. Those files are dynamically generated by kernel, when the access to them is requested. /// Those files then, are stored entirely in RAM; it is called Virtual File System (VFS). The access to the files is done by syscall that is non-blocking. /// Thus, this API can be synchronous without performance loss. /// </remarks> internal sealed class OSFileSystem : IFileSystem { public bool Exists(FileInfo fileInfo) { return fileInfo.Exists; } public IReadOnlyCollection<string> GetDirectoryNames(string directory, string pattern) { return Directory.GetDirectories(directory, pattern) .ToArray(); } public int Read(FileInfo file, int length, Span<char> destination) { using var stream = file.OpenRead(); using var rentedBuffer = new RentedSpan<byte>(length); var read = stream.Read(rentedBuffer.Span); return Encoding.ASCII.GetChars(rentedBuffer.Span.Slice(0, read), destination); } public void ReadFirstLine(FileInfo file, BufferWriter<char> destination) => ReadUntilTerminatorOrEnd(file, destination, (byte)'\n'); public void ReadAll(FileInfo file, BufferWriter<char> destination) => ReadUntilTerminatorOrEnd(file, destination, null); public IEnumerable<ReadOnlyMemory<char>> ReadAllByLines(FileInfo file, BufferWriter<char> destination) { const int MaxStackalloc = 256; if (!file.Exists) { throw new FileNotFoundException(); } Memory<byte> buffer = ArrayPool<byte>.Shared.Rent(MaxStackalloc); try { using FileStream stream = file.OpenRead(); int read; while ((read = stream.Read(buffer.Span)) > 0) { var start = 0; for (int end = 0; end < read; end++) { if (buffer.Span[end] == (byte)'\n') { var length = end - start; // Decode the slice into chars _ = Encoding.ASCII.GetChars(buffer.Span.Slice(start, length), destination.GetSpan(length)); destination.Advance(length); start = end + 1; // Yield the completed line yield return destination.WrittenMemory; destination.Reset(); } } // Set the comparison in the while loop to end when the file has not been completely read into the buffer. // It will then advance the last character to the destination for the next time yield return is called. if (start < read) { var length = read - start; _ = Encoding.ASCII.GetChars(buffer.Span.Slice(start, length), destination.GetSpan(length)); destination.Advance(length); } } // After reaching EOF, check for any leftover characters if (destination.WrittenMemory.Length > 0) { // Yield the last line (without a trailing newline) yield return destination.WrittenMemory; destination.Reset(); } } finally { // Return the rented buffer to the pool if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> arraySegment) && arraySegment.Array != null) { ArrayPool<byte>.Shared.Return(arraySegment.Array); } } } [SkipLocalsInit] private static void ReadUntilTerminatorOrEnd(FileInfo file, BufferWriter<char> destination, byte? terminator) { const int MaxStackalloc = 256; using var stream = file.OpenRead(); Span<byte> buffer = stackalloc byte[MaxStackalloc]; var read = stream.Read(buffer); while (read != 0) { var end = 0; for (end = 0; end < read; end++) { if (buffer[end] == terminator) { _ = Encoding.ASCII.GetChars(buffer.Slice(0, end), destination.GetSpan(end)); destination.Advance(end); return; } } _ = Encoding.ASCII.GetChars(buffer.Slice(0, end), destination.GetSpan(end)); destination.Advance(end); read = stream.Read(buffer); } } }