Skip to content

Commit

Permalink
Added localized error message and error parsing for base number type (#…
Browse files Browse the repository at this point in the history
…103)

* added localized error message and error parsing for base number type

* removed unused using and added comment

* refactoring RemoveFormatting for NumberBaseFormatter.

* handle max value for hexadecimal
  • Loading branch information
btiteux committed Dec 7, 2021
1 parent 51d88a6 commit 865c7b6
Show file tree
Hide file tree
Showing 11 changed files with 250 additions and 76 deletions.
26 changes: 26 additions & 0 deletions src/dev/impl/DevToys/LanguageManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,32 @@ public class NumberBaseConverterStrings : ObservableObject
/// Gets the resource OctalLabel.
/// </summary>
public string OctalLabel => _resources.GetString("OctalLabel");

/// <summary>
/// Gets the resource ValueInvalid.
/// </summary>
public string ValueInvalid => _resources.GetString("ValueInvalid");

/// <summary>
/// Gets the resource ValueInvalid with format.
/// </summary>
public string GetFormattedValueInvalid(string? param0)
{
return string.Format(ValueInvalid, param0);
}

/// <summary>
/// Gets the resource ValueOverflow.
/// </summary>
public string ValueOverflow => _resources.GetString("ValueOverflow");

/// <summary>
/// Gets the resource ValueOverflow with format.
/// </summary>
public string GetFormattedValueOverflow(string? param0)
{
return string.Format(ValueOverflow, param0);
}
}

public class PngJpgCompressorStrings : ObservableObject
Expand Down
14 changes: 7 additions & 7 deletions src/dev/impl/DevToys/Models/NumberBaseFormat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@ public class NumberBaseFormat : IEquatable<NumberBaseFormat>
{
private static NumberBaseConverterStrings Strings => LanguageManager.Instance.NumberBaseConverter;

public static readonly NumberBaseFormat Octal = new(
displayName: Strings.OctalLabel,
value: Radix.Octal,
baseNumber:8,
groupSize: CultureInfo.CurrentCulture.NumberFormat.NumberGroupSizes[0],
groupSeparator: ' ');

public static readonly NumberBaseFormat Binary = new(
displayName: Strings.BinaryLabel,
value: Radix.Binary,
baseNumber: 2,
groupSize: 4,
groupSeparator: ' ');

public static readonly NumberBaseFormat Octal = new(
displayName: Strings.OctalLabel,
value: Radix.Octal,
baseNumber: 8,
groupSize: CultureInfo.CurrentCulture.NumberFormat.NumberGroupSizes[0],
groupSeparator: ' ');

public static readonly NumberBaseFormat Decimal = new(
displayName: Strings.DecimalLabel,
value: Radix.Decimal,
Expand Down
8 changes: 8 additions & 0 deletions src/dev/impl/DevToys/Strings/cs-CZ/NumberBaseConverter.resw
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,12 @@
<data name="OctalLabel" xml:space="preserve">
<value>Octal</value>
</data>
<data name="ValueInvalid" xml:space="preserve">
<value>The current value isn't a valid {0}</value>
<comment>The parameter is the Base Number Type (Decimal, Octal, ...)</comment>
</data>
<data name="ValueOverflow" xml:space="preserve">
<value>Unable to parse the current value exceed max value {0}</value>
<comment>The parameter is the Max value of a long</comment>
</data>
</root>
8 changes: 8 additions & 0 deletions src/dev/impl/DevToys/Strings/en-US/NumberBaseConverter.resw
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,12 @@
<data name="OctalLabel" xml:space="preserve">
<value>Octal</value>
</data>
<data name="ValueInvalid" xml:space="preserve">
<value>The current value isn't a valid {0}</value>
<comment>The parameter is the Base Number Type (Decimal, Octal, ...)</comment>
</data>
<data name="ValueOverflow" xml:space="preserve">
<value>Unable to parse the current value exceed max value {0}</value>
<comment>The parameter is the Max value of a long</comment>
</data>
</root>
8 changes: 8 additions & 0 deletions src/dev/impl/DevToys/Strings/fr-FR/NumberBaseConverter.resw
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,12 @@
<data name="OctalLabel" xml:space="preserve">
<value>Octal</value>
</data>
<data name="ValueInvalid" xml:space="preserve">
<value>La valeur actuelle n'est pas une valeur {0} valide.</value>
<comment>The parameter is the Base Number Type (Decimal, Octal, ...)</comment>
</data>
<data name="ValueOverflow" xml:space="preserve">
<value>Impossible d'analyser la valeur actuelle qui dépasse la valeur maximale {0}</value>
<comment>The parameter is the Max value of a long</comment>
</data>
</root>
8 changes: 8 additions & 0 deletions src/dev/impl/DevToys/Strings/pl-PL/NumberBaseConverter.resw
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,12 @@
<data name="OctalLabel" xml:space="preserve">
<value>Octal</value>
</data>
<data name="ValueInvalid" xml:space="preserve">
<value>The current value isn't a valid {0}</value>
<comment>The parameter is the Base Number Type (Decimal, Octal, ...)</comment>
</data>
<data name="ValueOverflow" xml:space="preserve">
<value>Unable to parse the current value exceed max value {0}</value>
<comment>The parameter is the Max value of a long</comment>
</data>
</root>
8 changes: 8 additions & 0 deletions src/dev/impl/DevToys/Strings/ru-RU/NumberBaseConverter.resw
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,12 @@
<data name="OctalLabel" xml:space="preserve">
<value>Octal</value>
</data>
<data name="ValueInvalid" xml:space="preserve">
<value>The current value isn't a valid {0}</value>
<comment>The parameter is the Base Number Type (Decimal, Octal, ...)</comment>
</data>
<data name="ValueOverflow" xml:space="preserve">
<value>Unable to parse the current value exceed max value {0}</value>
<comment>The parameter is the Max value of a long</comment>
</data>
</root>
8 changes: 8 additions & 0 deletions src/dev/impl/DevToys/Strings/zh-CN/NumberBaseConverter.resw
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,12 @@
<data name="OctalLabel" xml:space="preserve">
<value>Octal</value>
</data>
<data name="ValueInvalid" xml:space="preserve">
<value>The current value isn't a valid {0}</value>
<comment>The parameter is the Base Number Type (Decimal, Octal, ...)</comment>
</data>
<data name="ValueOverflow" xml:space="preserve">
<value>Unable to parse the current value exceed max value {0}</value>
<comment>The parameter is the Max value of a long</comment>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@ private async Task TreatQueueAsync()
isInfoBarOpen = true;
infoBarMessage = exception.Message;
}
catch (InvalidOperationException exception)
{
isInfoBarOpen = true;
infoBarMessage = exception.Message;
}
catch (Exception ex)
{
Logger.LogFault("NumberBaseConverter", ex, $"Input base number: {InputBaseNumber}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ namespace DevToys.Core.Formatter
{
internal static class NumberBaseFormatter
{
private static NumberBaseConverterStrings Strings => LanguageManager.Instance.NumberBaseConverter;

/// <summary>
/// Based on <see cref="System.ParseNumbers"/>
/// https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/ParseNumbers.cs
Expand All @@ -18,19 +20,19 @@ internal static class NumberBaseFormatter
/// <returns>Value converted to <see cref="NumberBaseFormat.Decimal"/></returns>
public static long? StringToBase(string value, NumberBaseFormat baseNumber)
{
if (value.Length <= 0)
if (string.IsNullOrWhiteSpace(value))
{
return null;
}

int length = value.Length;
int index = 0;

Span<char> unformattedValue = RemoveFormatting(value);
Span<char> spanValue = value!.ToCharArray();
int length = RemoveFormatting(spanValue);

// Check for a sign
int sign = 1;
if (unformattedValue[index] == '-')
if (spanValue[index] == '-')
{
if (baseNumber != NumberBaseFormat.Decimal)
{
Expand All @@ -40,65 +42,12 @@ internal static class NumberBaseFormatter
sign = -1;
index++;
}
else if (unformattedValue[index] == '+')
else if (spanValue[index] == '+')
{
index++;
}

long result = 0;
long maxVal;

// Allow all non-decimal numbers to set the sign bit.
if (baseNumber == NumberBaseFormat.Decimal)
{
maxVal = 0x7FFFFFFFFFFFFFFF / 10;

// Read all of the digits and convert to a number
while (index < length && IsDigit(unformattedValue[index], baseNumber.BaseNumber, out int current))
{
// Check for overflows - this is sufficient & correct.
if (result > maxVal || result < 0)
{
throw new OverflowException($"Unable to parse the current value exceded max value ({long.MaxValue})");
}

result = result * baseNumber.BaseNumber + current;
index++;
}

if (result is < 0 and not 0x800000000000000)
{
throw new OverflowException($"Unable to parse the current value exceded max value ({long.MaxValue})");
}
}
else
{
maxVal =
baseNumber.BaseNumber == 10 ? 0xfffffffffffffff / 10 :
baseNumber.BaseNumber == 16 ? 0xfffffffffffffff / 16 :
baseNumber.BaseNumber == 8 ? 0xfffffffffffffff / 8 :
0xfffffffffffffff / 2;

// Read all of the digits and convert to a number
while (index < unformattedValue.Length && IsDigit(unformattedValue[index], baseNumber.BaseNumber, out int current))
{
// Check for overflows - this is sufficient & correct.
if (result > maxVal)
{
throw new OverflowException($"Unable to parse the current value exceded max value ({long.MaxValue})");
}

long temp = result * baseNumber.BaseNumber + current;

if (temp < result) // this means overflow as well
{
throw new OverflowException($"Unable to parse the current value exceded max value ({long.MaxValue})");
}

result = temp;
index++;
}
}
long result = GetLong(baseNumber, spanValue, index, length);

if (baseNumber == NumberBaseFormat.Decimal)
{
Expand Down Expand Up @@ -202,30 +151,125 @@ public static string LongToBase(long number, NumberBaseFormat baseNumber, bool i
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static Span<char> RemoveFormatting(string? value)
public static string RemoveFormatting(string? value)
{
if (string.IsNullOrWhiteSpace(value!))
{
return Span<char>.Empty;
return string.Empty;
}

Span<char> valueSpan = value!.ToCharArray();
int length = RemoveFormatting(valueSpan);
var result = new StringBuilder();
for (int i = 0; i < length; i++)
{
result.Append(valueSpan[i]);
}
return result.ToString();
}

private static int RemoveFormatting(Span<char> values)
{
if (values.Length == 0)
{
return 0;
}

string currentCulture = CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator;
Span<char> values = new char[value!.Length];
int valueIndex = 0;
int maxLength = 0;
for (int i = 0; i < values.Length; i++)
{
if (!char.IsWhiteSpace(value[i]) && value[i] != Convert.ToChar(currentCulture))
if (!char.IsWhiteSpace(values[i]) && values[i] != Convert.ToChar(currentCulture))
{
values[maxLength] = values[i];
maxLength++;
}
}
return maxLength;
}

private static long GetLong(NumberBaseFormat baseNumber, ReadOnlySpan<char> spanValue, int index, int length)
{
ulong result = 0;
ulong maxVal = 0x7FFFFFFFFFFFFFFF / 10;

// Read all of the digits and convert to a number
while (index < length)
{
if (!IsValidChar(spanValue[index], baseNumber))
{
throw new InvalidOperationException(string.Format(Strings.ValueInvalid, baseNumber.DisplayName));
}

if (!IsDigit(spanValue[index], baseNumber.BaseNumber, out int current))
{
break;
}

if (baseNumber == NumberBaseFormat.Decimal)
{
values[valueIndex] = value[i];
valueIndex++;
// Check for overflows - this is sufficient & correct.
if (result > maxVal || result < 0)
{
throw new OverflowException(string.Format(Strings.ValueOverflow, long.MaxValue));
}

result = result * (ulong)baseNumber.BaseNumber + (ulong)current;
index++;
}
else
{
// Check for overflows - this is sufficient & correct.
if (result > maxVal)
{
throw new OverflowException($"Unable to parse the current value exceded max value ({long.MaxValue})");
}

ulong temp = result * (ulong)baseNumber.BaseNumber + (ulong)current;

if (temp < result) // this means overflow as well
{
throw new OverflowException($"Unable to parse the current value exceded max value ({long.MaxValue})");
}

result = temp;
index++;
}
}
return values;

if (baseNumber == NumberBaseFormat.Decimal && (long)result is < 0 and not 0x800000000000000)
{
throw new OverflowException(string.Format(Strings.ValueOverflow, long.MaxValue));
}
return (long)result;
}

private static bool IsValidChar(char c, NumberBaseFormat baseNumber)
{
switch (baseNumber.Value)
{
case Radix.Binary:
if (c is '0' or '1')
{
return true;
}
return false;
case Radix.Decimal:
case Radix.Octal:
return char.IsNumber(c);
case Radix.Hexdecimal:
return (char.IsNumber(c) ||
(c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F'));
default:
return true;
}
}

private static bool IsDigit(char c, int radix, out int result)
{
int tmp;

if ((uint)(c - '0') <= 9)
{
result = tmp = c - '0';
Expand All @@ -243,7 +287,6 @@ private static bool IsDigit(char c, int radix, out int result)
result = -1;
return false;
}

return tmp < radix;
}
}
Expand Down
Loading

0 comments on commit 865c7b6

Please sign in to comment.