diff --git a/src/LibHac/Common/StringUtils.cs b/src/LibHac/Common/StringUtils.cs index bfd8e8fe..372b559b 100644 --- a/src/LibHac/Common/StringUtils.cs +++ b/src/LibHac/Common/StringUtils.cs @@ -168,5 +168,15 @@ namespace LibHac.Common { return Utf8ToString(value.Slice(0, GetLength(value))); } + + public static bool IsAlpha(byte c) + { + return (c | 0x20u) - (byte)'A' <= 'Z' - 'A'; + } + + public static bool IsDigit(byte c) + { + return (uint)(c - (byte)'0') <= 9; + } } } diff --git a/src/LibHac/Common/U8StringBuilder.cs b/src/LibHac/Common/U8StringBuilder.cs index e4ae004d..0f78f448 100644 --- a/src/LibHac/Common/U8StringBuilder.cs +++ b/src/LibHac/Common/U8StringBuilder.cs @@ -1,4 +1,6 @@ using System; +using System.Buffers; +using System.Buffers.Text; using System.Diagnostics; namespace LibHac.Common @@ -63,6 +65,36 @@ namespace LibHac.Common return this; } + public U8StringBuilder AppendFormat(byte value, char format = 'G', byte precision = 255) => + AppendFormatUInt64(value, format, precision); + + public U8StringBuilder AppendFormat(sbyte value, char format = 'G', byte precision = 255) => + AppendFormatInt64(value, 0xff, format, precision); + + public U8StringBuilder AppendFormat(ushort value, char format = 'G', byte precision = 255) => + AppendFormatUInt64(value, format, precision); + + public U8StringBuilder AppendFormat(short value, char format = 'G', byte precision = 255) => + AppendFormatInt64(value, 0xffff, format, precision); + + public U8StringBuilder AppendFormat(uint value, char format = 'G', byte precision = 255) => + AppendFormatUInt64(value, format, precision); + + public U8StringBuilder AppendFormat(int value, char format = 'G', byte precision = 255) => + AppendFormatInt64(value, 0xffffff, format, precision); + + public U8StringBuilder AppendFormat(ulong value, char format = 'G', byte precision = 255) => + AppendFormatUInt64(value, format, precision); + + public U8StringBuilder AppendFormat(long value, char format = 'G', byte precision = 255) => + AppendFormatInt64(value, 0xffffffff, format, precision); + + public U8StringBuilder AppendFormat(float value, char format = 'G', byte precision = 255) => + AppendFormatFloat(value, format, precision); + + public U8StringBuilder AppendFormat(double value, char format = 'G', byte precision = 255) => + AppendFormatDouble(value, format, precision); + private readonly bool HasCapacity(int requiredCapacity) { return requiredCapacity <= Capacity; @@ -83,6 +115,100 @@ namespace LibHac.Common if (_buffer.Length == 0) throw new ArgumentException("Buffer length must be greater than 0."); } + private U8StringBuilder AppendFormatInt64(long value, ulong mask, char format, byte precision) + { + if (Overflowed) return this; + + // Remove possible sign extension if needed + if (mask == 'x' | mask == 'X') + { + value &= (long)mask; + } + + // Exclude the null terminator from the buffer because Utf8Formatter doesn't handle it + Span availableBuffer = _buffer.Slice(_length, Capacity - _length); + + bool bufferLargeEnough = Utf8Formatter.TryFormat(value, availableBuffer, out int bytesWritten, + new StandardFormat(format, precision)); + + if (!bufferLargeEnough) + { + Overflowed = true; + return this; + } + + _length += bytesWritten; + AddNullTerminator(); + + return this; + } + + private U8StringBuilder AppendFormatUInt64(ulong value, char format, byte precision) + { + if (Overflowed) return this; + + // Exclude the null terminator from the buffer because Utf8Formatter doesn't handle it + Span availableBuffer = _buffer.Slice(_length, Capacity - _length); + + bool bufferLargeEnough = Utf8Formatter.TryFormat(value, availableBuffer, out int bytesWritten, + new StandardFormat(format, precision)); + + if (!bufferLargeEnough) + { + Overflowed = true; + return this; + } + + _length += bytesWritten; + AddNullTerminator(); + + return this; + } + + private U8StringBuilder AppendFormatFloat(float value, char format, byte precision) + { + if (Overflowed) return this; + + // Exclude the null terminator from the buffer because Utf8Formatter doesn't handle it + Span availableBuffer = _buffer.Slice(_length, Capacity - _length); + + bool bufferLargeEnough = Utf8Formatter.TryFormat(value, availableBuffer, out int bytesWritten, + new StandardFormat(format, precision)); + + if (!bufferLargeEnough) + { + Overflowed = true; + return this; + } + + _length += bytesWritten; + AddNullTerminator(); + + return this; + } + + private U8StringBuilder AppendFormatDouble(double value, char format, byte precision) + { + if (Overflowed) return this; + + // Exclude the null terminator from the buffer because Utf8Formatter doesn't handle it + Span availableBuffer = _buffer.Slice(_length, Capacity - _length); + + bool bufferLargeEnough = Utf8Formatter.TryFormat(value, availableBuffer, out int bytesWritten, + new StandardFormat(format, precision)); + + if (!bufferLargeEnough) + { + Overflowed = true; + return this; + } + + _length += bytesWritten; + AddNullTerminator(); + + return this; + } + public override readonly string ToString() => StringUtils.Utf8ZToString(_buffer); } }