Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 203 additions & 12 deletions src/Neo.Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;

Expand Down Expand Up @@ -56,15 +57,56 @@ public static bool TryToStrictUtf8String(this ReadOnlySpan<byte> bytes, [NotNull
/// <param name="value">The byte span to convert.</param>
/// <returns>The converted string.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string ToStrictUtf8String(this ReadOnlySpan<byte> value) => StrictUTF8.GetString(value);
public static string ToStrictUtf8String(this ReadOnlySpan<byte> value)
{
try
{
return StrictUTF8.GetString(value);
}
catch (DecoderFallbackException ex)
{
var bytesInfo = value.Length <= 32 ? $"Bytes: [{string.Join(", ", value.ToArray().Select(b => $"0x{b:X2}"))}]" : $"Length: {value.Length} bytes";
throw new DecoderFallbackException($"Failed to decode byte span to UTF-8 string (strict mode): The input contains invalid UTF-8 byte sequences. {bytesInfo}. Ensure all bytes form valid UTF-8 character sequences.", ex);
}
catch (ArgumentException ex)
{
throw new ArgumentException("Invalid byte span provided for UTF-8 decoding. The span may be corrupted or contain invalid data.", nameof(value), ex);
}
catch (Exception ex)
{
throw new InvalidOperationException("An unexpected error occurred while decoding byte span to UTF-8 string in strict mode. This may indicate a system-level encoding issue.", ex);
}
}

/// <summary>
/// Converts a byte array to a strict UTF8 string.
/// </summary>
/// <param name="value">The byte array to convert.</param>
/// <returns>The converted string.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string ToStrictUtf8String(this byte[] value) => StrictUTF8.GetString(value);
public static string ToStrictUtf8String(this byte[] value)
{
if (value == null)
throw new ArgumentNullException(nameof(value), "Cannot decode null byte array to UTF-8 string.");

try
{
return StrictUTF8.GetString(value);
}
catch (DecoderFallbackException ex)
{
var bytesInfo = value.Length <= 32 ? $"Bytes: {BitConverter.ToString(value)}" : $"Length: {value.Length} bytes, First 16: {BitConverter.ToString(value, 0, Math.Min(16, value.Length))}...";
throw new DecoderFallbackException($"Failed to decode byte array to UTF-8 string (strict mode): The input contains invalid UTF-8 byte sequences. {bytesInfo}. Ensure all bytes form valid UTF-8 character sequences.", ex);
}
catch (ArgumentException ex)
{
throw new ArgumentException("Invalid byte array provided for UTF-8 decoding. The array may be corrupted or contain invalid data.", nameof(value), ex);
}
catch (Exception ex)
{
throw new InvalidOperationException("An unexpected error occurred while decoding byte array to UTF-8 string in strict mode. This may indicate a system-level encoding issue.", ex);
}
}

/// <summary>
/// Converts a byte array to a strict UTF8 string.
Expand All @@ -75,23 +117,96 @@ public static bool TryToStrictUtf8String(this ReadOnlySpan<byte> bytes, [NotNull
/// <returns>The converted string.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string ToStrictUtf8String(this byte[] value, int start, int count)
=> StrictUTF8.GetString(value, start, count);
{
if (value == null)
throw new ArgumentNullException(nameof(value), "Cannot decode null byte array to UTF-8 string.");
if (start < 0)
throw new ArgumentOutOfRangeException(nameof(start), start, "Start index cannot be negative.");
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count), count, "Count cannot be negative.");
if (start + count > value.Length)
throw new ArgumentOutOfRangeException(nameof(count), $"The specified range [{start}, {start + count}) exceeds the array bounds (length: {value.Length}). Ensure start + count <= array.Length.");

try
{
return StrictUTF8.GetString(value, start, count);
}
catch (DecoderFallbackException ex)
{
var rangeBytes = new byte[count];
Array.Copy(value, start, rangeBytes, 0, count);
var bytesInfo = count <= 32 ? $"Bytes: {BitConverter.ToString(rangeBytes)}" : $"Length: {count} bytes, First 16: {BitConverter.ToString(rangeBytes, 0, Math.Min(16, count))}...";
throw new DecoderFallbackException($"Failed to decode byte array range [{start}, {start + count}) to UTF-8 string (strict mode): The input contains invalid UTF-8 byte sequences. {bytesInfo}. Ensure all bytes form valid UTF-8 character sequences.", ex);
}
catch (ArgumentException ex)
{
throw new ArgumentException($"Invalid parameters provided for UTF-8 decoding. Array length: {value.Length}, Start: {start}, Count: {count}.", ex);
}
catch (Exception ex)
{
throw new InvalidOperationException($"An unexpected error occurred while decoding byte array range [{start}, {start + count}) to UTF-8 string in strict mode. This may indicate a system-level encoding issue.", ex);
}
}

/// <summary>
/// Converts a string to a strict UTF8 byte array.
/// </summary>
/// <param name="value">The string to convert.</param>
/// <returns>The converted byte array.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte[] ToStrictUtf8Bytes(this string value) => StrictUTF8.GetBytes(value);
public static byte[] ToStrictUtf8Bytes(this string value)
{
if (value == null)
throw new ArgumentNullException(nameof(value), "Cannot encode null string to UTF-8 bytes.");

try
{
return StrictUTF8.GetBytes(value);
}
catch (EncoderFallbackException ex)
{
var valueInfo = value.Length <= 100 ? $"Input: '{value}'" : $"Input length: {value.Length} characters, First 50: '{value[..50]}...'";
throw new EncoderFallbackException($"Failed to encode string to UTF-8 bytes (strict mode): The input contains characters that cannot be encoded in UTF-8. {valueInfo}. Ensure the string contains only valid Unicode characters.", ex);
}
catch (ArgumentException ex)
{
throw new ArgumentException("Invalid string provided for UTF-8 encoding. The string may contain unsupported characters.", nameof(value), ex);
}
catch (Exception ex)
{
throw new InvalidOperationException("An unexpected error occurred while encoding string to UTF-8 bytes in strict mode. This may indicate a system-level encoding issue.", ex);
}
}

/// <summary>
/// Gets the size of the specified <see cref="string"/> encoded in strict UTF8.
/// </summary>
/// <param name="value">The specified <see cref="string"/>.</param>
/// <returns>The size of the <see cref="string"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetStrictUtf8ByteCount(this string value) => StrictUTF8.GetByteCount(value);
public static int GetStrictUtf8ByteCount(this string value)
{
if (value == null)
throw new ArgumentNullException(nameof(value), "Cannot get UTF-8 byte count for null string.");

try
{
return StrictUTF8.GetByteCount(value);
}
catch (EncoderFallbackException ex)
{
var valueInfo = value.Length <= 100 ? $"Input: '{value}'" : $"Input length: {value.Length} characters, First 50: '{value[..50]}...'";
throw new EncoderFallbackException($"Failed to get UTF-8 byte count for string (strict mode): The input contains characters that cannot be encoded in UTF-8. {valueInfo}. Ensure the string contains only valid Unicode characters.", ex);
}
catch (ArgumentException ex)
{
throw new ArgumentException("Invalid string provided for UTF-8 byte count calculation. The string may contain unsupported characters.", nameof(value), ex);
}
catch (Exception ex)
{
throw new InvalidOperationException("An unexpected error occurred while calculating UTF-8 byte count for string in strict mode. This may indicate a system-level encoding issue.", ex);
}
}

/// <summary>
/// Determines if the specified <see cref="string"/> is a valid hex string.
Expand Down Expand Up @@ -119,7 +234,31 @@ public static bool IsHex(this string value)
/// <param name="value">The hex <see cref="string"/> to convert.</param>
/// <returns>The converted byte array.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte[] HexToBytes(this string? value) => HexToBytes(value.AsSpan());
public static byte[] HexToBytes(this string? value)
{
if (value == null)
return [];

try
{
return HexToBytes(value.AsSpan());
}
catch (ArgumentException ex)
{
var valueInfo = value.Length <= 100 ? $"Input: '{value}'" : $"Input length: {value.Length} characters";
throw new ArgumentException($"Failed to convert hex string to bytes: The input has an invalid length (must be even) or contains non-hexadecimal characters. {valueInfo}. Valid hex characters are 0-9, A-F, and a-f.", nameof(value), ex);
}
catch (FormatException ex)
{
var valueInfo = value.Length <= 100 ? $"Input: '{value}'" : $"Input length: {value.Length} characters";
throw new FormatException($"Failed to convert hex string to bytes: The input contains invalid hexadecimal characters. {valueInfo}. Valid hex characters are 0-9, A-F, and a-f.", ex);
}
catch (Exception ex)
{
var valueInfo = value.Length <= 100 ? $"Input: '{value}'" : $"Input length: {value.Length} characters";
throw new InvalidOperationException($"An unexpected error occurred while converting hex string to bytes. {valueInfo}. This may indicate a system-level parsing issue.", ex);
}
}

/// <summary>
/// Converts a hex <see cref="string"/> to byte array then reverses the order of the bytes.
Expand All @@ -129,9 +268,27 @@ public static bool IsHex(this string value)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte[] HexToBytesReversed(this ReadOnlySpan<char> value)
{
var data = HexToBytes(value);
Array.Reverse(data);
return data;
try
{
var data = HexToBytes(value);
Array.Reverse(data);
return data;
}
catch (ArgumentException ex)
{
var valueInfo = value.Length <= 100 ? $"Input: '{value}'" : $"Input length: {value.Length} characters";
throw new ArgumentException($"Failed to convert hex span to reversed bytes: The input has an invalid length (must be even) or contains non-hexadecimal characters. {valueInfo}. Valid hex characters are 0-9, A-F, and a-f.", ex);
}
catch (FormatException ex)
{
var valueInfo = value.Length <= 100 ? $"Input: '{value}'" : $"Input length: {value.Length} characters";
throw new FormatException($"Failed to convert hex span to reversed bytes: The input contains invalid hexadecimal characters. {valueInfo}. Valid hex characters are 0-9, A-F, and a-f.", ex);
}
catch (Exception ex)
{
var valueInfo = value.Length <= 100 ? $"Input: '{value}'" : $"Input length: {value.Length} characters";
throw new InvalidOperationException($"An unexpected error occurred while converting hex span to reversed bytes. {valueInfo}. This may indicate a system-level parsing or array manipulation issue.", ex);
}
}

/// <summary>
Expand All @@ -141,7 +298,25 @@ public static byte[] HexToBytesReversed(this ReadOnlySpan<char> value)
/// <returns>The converted byte array.</returns>
public static byte[] HexToBytes(this ReadOnlySpan<char> value)
{
return Convert.FromHexString(value);
try
{
return Convert.FromHexString(value);
}
catch (ArgumentException ex)
{
var valueInfo = value.Length <= 100 ? $"Input: '{value}'" : $"Input length: {value.Length} characters";
throw new ArgumentException($"Failed to convert hex span to bytes: The input has an invalid length (must be even) or contains non-hexadecimal characters. {valueInfo}. Valid hex characters are 0-9, A-F, and a-f.", ex);
}
catch (FormatException ex)
{
var valueInfo = value.Length <= 100 ? $"Input: '{value}'" : $"Input length: {value.Length} characters";
throw new FormatException($"Failed to convert hex span to bytes: The input contains invalid hexadecimal characters. {valueInfo}. Valid hex characters are 0-9, A-F, and a-f.", ex);
}
catch (Exception ex)
{
var valueInfo = value.Length <= 100 ? $"Input: '{value}'" : $"Input length: {value.Length} characters";
throw new InvalidOperationException($"An unexpected error occurred while converting hex span to bytes. {valueInfo}. This may indicate a system-level parsing issue.", ex);
}
}

/// <summary>
Expand All @@ -151,8 +326,24 @@ public static byte[] HexToBytes(this ReadOnlySpan<char> value)
/// <returns>The size of the <see cref="string"/>.</returns>
public static int GetVarSize(this string value)
{
var size = value.GetStrictUtf8ByteCount();
return size.GetVarSize() + size;
if (value == null)
throw new ArgumentNullException(nameof(value), "Cannot calculate variable size for null string.");

try
{
var size = value.GetStrictUtf8ByteCount();
return size.GetVarSize() + size;
}
catch (EncoderFallbackException ex)
{
var valueInfo = value.Length <= 100 ? $"Input: '{value}'" : $"Input length: {value.Length} characters, First 50: '{value[..50]}...'";
throw new EncoderFallbackException($"Failed to calculate variable size: The string contains characters that cannot be encoded in UTF-8 (strict mode). {valueInfo}. Ensure the string contains only valid Unicode characters.", ex);
}
catch (Exception ex)
{
var valueInfo = value.Length <= 100 ? $"Input: '{value}'" : $"Input length: {value.Length} characters";
throw new InvalidOperationException($"An unexpected error occurred while calculating variable size for string. {valueInfo}. This may indicate an issue with the string encoding or variable size calculation.", ex);
}
}

/// <summary>
Expand Down
Loading
Loading