Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added localized error message and error parsing for base number type #103

Merged
merged 4 commits into from
Dec 7, 2021
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
26 changes: 26 additions & 0 deletions src/dev/impl/DevToys/LanguageManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,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>
veler marked this conversation as resolved.
Show resolved Hide resolved
<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