/
FileOpenHandleRuneBenchmark.cs
60 lines (50 loc) · 1.96 KB
/
FileOpenHandleRuneBenchmark.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
using System.Buffers;
using System.Text;
using BenchmarkData;
namespace FileOpenHandleRuneBenchmark;
public static class FileOpenHandleRuneBenchmark
{
public static Count Count(string path)
{
long wordCount = 0, lineCount = 0, byteCount = 0;
bool wasSpace = true;
byte[] buffer = ArrayPool<byte>.Shared.Rent(BenchmarkValues.Size);
using Microsoft.Win32.SafeHandles.SafeFileHandle handle = File.OpenHandle(path, FileMode.Open, FileAccess.Read, FileShare.Read, FileOptions.SequentialScan);
int index = 0;
// Read content in chunks, in buffer, at count lenght, starting at byteCount
int count = 0;
while ((count = RandomAccess.Read(handle, buffer.AsSpan(index), byteCount)) > 0 || index > 0)
{
byteCount += count;
Span<byte> bytes = buffer.AsSpan(0, count + index);
index = 0;
while (bytes.Length > 0)
{
OperationStatus status = Rune.DecodeFromUtf8(bytes, out Rune rune, out int bytesConsumed);
// bad read due to low buffer length
if (status == OperationStatus.NeedMoreData && count > 0)
{
bytes[..bytesConsumed].CopyTo(buffer); // move the partial Rune to the start of the buffer before next read
index = bytesConsumed;
break;
}
if (Rune.IsWhiteSpace(rune))
{
if (rune.Value is '\n')
{
lineCount++;
}
wasSpace = true;
}
else if (wasSpace)
{
wordCount++;
wasSpace = false;
}
bytes = bytes.Slice(bytesConsumed);
}
}
ArrayPool<byte>.Shared.Return(buffer);
return new(lineCount, wordCount, byteCount, path);
}
}