using System; using System.Buffers; using System.Buffers.Text; using System.Text; using PerfectNumbers.Core; namespace EvenPerfectBitScanner; internal static class Utf8CliParser { private const int StackAllocThreshold = 128; public static ulong ParseUInt64(ReadOnlySpan value) { if (!TryParseUInt64(value, out ulong result)) { ThrowFormatException(value); } return result; } public static int ParseInt32(ReadOnlySpan value) { if (!TryParseInt32(value, out int result)) { ThrowFormatException(value); } return result; } public static bool TryParseUInt64(ReadOnlySpan value, out ulong result) { return TryParse(value, static (ReadOnlySpan source, out ulong parsed, out int consumed) => Utf8Parser.TryParse(source, out parsed, out consumed), out result); } public static bool TryParseInt32(ReadOnlySpan value, out int result) { return TryParse(value, static (ReadOnlySpan source, out int parsed, out int consumed) => Utf8Parser.TryParse(source, out parsed, out consumed), out result); } public static bool TryParseDouble(ReadOnlySpan value, out double result) { return TryParse(value, static (ReadOnlySpan source, out double parsed, out int consumed) => Utf8Parser.TryParse(source, out parsed, out consumed), out result); } public static bool TryParseBoolean(ReadOnlySpan value, out bool result) { value = value.Trim(); if (value.Equals("true", StringComparison.OrdinalIgnoreCase)) { result = true; return true; } if (value.Equals("false", StringComparison.OrdinalIgnoreCase)) { result = false; return true; } if (value.Equals("1", StringComparison.Ordinal)) { result = true; return true; } if (value.Equals("0", StringComparison.Ordinal)) { result = false; return true; } return TryParse(value, static (ReadOnlySpan source, out bool parsed, out int consumed) => Utf8Parser.TryParse(source, out parsed, out consumed), out result); } private static bool TryParse(ReadOnlySpan value, Utf8ParserDelegate parser, out T result) where T : struct { value = value.Trim(); if (value.IsEmpty) { result = default; return false; } Span stackBuffer = stackalloc byte[StackAllocThreshold]; if (value.Length <= stackBuffer.Length) { int bytesWritten = Encoding.UTF8.GetBytes(value, stackBuffer); ReadOnlySpan utf8 = stackBuffer[..bytesWritten]; if (!parser(utf8, out result, out int consumed) || consumed != bytesWritten) { result = default; return false; } return true; } ArrayPool pool = ThreadStaticPools.BytePool; byte[] rented = pool.Rent(value.Length); int rentedBytesWritten = Encoding.UTF8.GetBytes(value, rented); ReadOnlySpan rentedUtf8 = rented.AsSpan(0, rentedBytesWritten); bool parsed = parser(rentedUtf8, out result, out int rentedConsumed) && rentedConsumed == rentedBytesWritten; pool.Return(rented); if (!parsed) { result = default; return false; } return true; } private static void ThrowFormatException(ReadOnlySpan value) { throw new FormatException($"Invalid value '{value.ToString()}'."); } private delegate bool Utf8ParserDelegate(ReadOnlySpan source, out T value, out int bytesConsumed) where T : struct; }