|
@@ -540,17 +540,17 @@ namespace Google.Protobuf
|
|
|
case FieldType.Int32:
|
|
|
case FieldType.SInt32:
|
|
|
case FieldType.SFixed32:
|
|
|
- return ParseNumericString(keyText, int.Parse, false);
|
|
|
+ return ParseNumericString(keyText, int.Parse);
|
|
|
case FieldType.UInt32:
|
|
|
case FieldType.Fixed32:
|
|
|
- return ParseNumericString(keyText, uint.Parse, false);
|
|
|
+ return ParseNumericString(keyText, uint.Parse);
|
|
|
case FieldType.Int64:
|
|
|
case FieldType.SInt64:
|
|
|
case FieldType.SFixed64:
|
|
|
- return ParseNumericString(keyText, long.Parse, false);
|
|
|
+ return ParseNumericString(keyText, long.Parse);
|
|
|
case FieldType.UInt64:
|
|
|
case FieldType.Fixed64:
|
|
|
- return ParseNumericString(keyText, ulong.Parse, false);
|
|
|
+ return ParseNumericString(keyText, ulong.Parse);
|
|
|
default:
|
|
|
throw new InvalidProtocolBufferException("Invalid field type for map: " + field.FieldType);
|
|
|
}
|
|
@@ -561,7 +561,6 @@ namespace Google.Protobuf
|
|
|
double value = token.NumberValue;
|
|
|
checked
|
|
|
{
|
|
|
- // TODO: Validate that it's actually an integer, possibly in terms of the textual representation?
|
|
|
try
|
|
|
{
|
|
|
switch (field.FieldType)
|
|
@@ -569,16 +568,20 @@ namespace Google.Protobuf
|
|
|
case FieldType.Int32:
|
|
|
case FieldType.SInt32:
|
|
|
case FieldType.SFixed32:
|
|
|
+ CheckInteger(value);
|
|
|
return (int) value;
|
|
|
case FieldType.UInt32:
|
|
|
case FieldType.Fixed32:
|
|
|
+ CheckInteger(value);
|
|
|
return (uint) value;
|
|
|
case FieldType.Int64:
|
|
|
case FieldType.SInt64:
|
|
|
case FieldType.SFixed64:
|
|
|
+ CheckInteger(value);
|
|
|
return (long) value;
|
|
|
case FieldType.UInt64:
|
|
|
case FieldType.Fixed64:
|
|
|
+ CheckInteger(value);
|
|
|
return (ulong) value;
|
|
|
case FieldType.Double:
|
|
|
return value;
|
|
@@ -597,20 +600,32 @@ namespace Google.Protobuf
|
|
|
{
|
|
|
return float.NegativeInfinity;
|
|
|
}
|
|
|
- throw new InvalidProtocolBufferException("Value out of range: " + value);
|
|
|
+ throw new InvalidProtocolBufferException($"Value out of range: {value}");
|
|
|
}
|
|
|
return (float) value;
|
|
|
default:
|
|
|
- throw new InvalidProtocolBufferException("Unsupported conversion from JSON number for field type " + field.FieldType);
|
|
|
+ throw new InvalidProtocolBufferException($"Unsupported conversion from JSON number for field type {field.FieldType}");
|
|
|
}
|
|
|
}
|
|
|
catch (OverflowException)
|
|
|
{
|
|
|
- throw new InvalidProtocolBufferException("Value out of range: " + value);
|
|
|
+ throw new InvalidProtocolBufferException($"Value out of range: {value}");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private static void CheckInteger(double value)
|
|
|
+ {
|
|
|
+ if (double.IsInfinity(value) || double.IsNaN(value))
|
|
|
+ {
|
|
|
+ throw new InvalidProtocolBufferException($"Value not an integer: {value}");
|
|
|
+ }
|
|
|
+ if (value != Math.Floor(value))
|
|
|
+ {
|
|
|
+ throw new InvalidProtocolBufferException($"Value not an integer: {value}");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private static object ParseSingleStringValue(FieldDescriptor field, string text)
|
|
|
{
|
|
|
switch (field.FieldType)
|
|
@@ -622,43 +637,35 @@ namespace Google.Protobuf
|
|
|
case FieldType.Int32:
|
|
|
case FieldType.SInt32:
|
|
|
case FieldType.SFixed32:
|
|
|
- return ParseNumericString(text, int.Parse, false);
|
|
|
+ return ParseNumericString(text, int.Parse);
|
|
|
case FieldType.UInt32:
|
|
|
case FieldType.Fixed32:
|
|
|
- return ParseNumericString(text, uint.Parse, false);
|
|
|
+ return ParseNumericString(text, uint.Parse);
|
|
|
case FieldType.Int64:
|
|
|
case FieldType.SInt64:
|
|
|
case FieldType.SFixed64:
|
|
|
- return ParseNumericString(text, long.Parse, false);
|
|
|
+ return ParseNumericString(text, long.Parse);
|
|
|
case FieldType.UInt64:
|
|
|
case FieldType.Fixed64:
|
|
|
- return ParseNumericString(text, ulong.Parse, false);
|
|
|
+ return ParseNumericString(text, ulong.Parse);
|
|
|
case FieldType.Double:
|
|
|
- double d = ParseNumericString(text, double.Parse, true);
|
|
|
- // double.Parse can return +/- infinity on Mono for non-infinite values which are out of range for double.
|
|
|
- if (double.IsInfinity(d) && !text.Contains("Infinity"))
|
|
|
- {
|
|
|
- throw new InvalidProtocolBufferException("Invalid numeric value: " + text);
|
|
|
- }
|
|
|
+ double d = ParseNumericString(text, double.Parse);
|
|
|
+ ValidateInfinityAndNan(text, double.IsPositiveInfinity(d), double.IsNegativeInfinity(d), double.IsNaN(d));
|
|
|
return d;
|
|
|
case FieldType.Float:
|
|
|
- float f = ParseNumericString(text, float.Parse, true);
|
|
|
- // float.Parse can return +/- infinity on Mono for non-infinite values which are out of range for float.
|
|
|
- if (float.IsInfinity(f) && !text.Contains("Infinity"))
|
|
|
- {
|
|
|
- throw new InvalidProtocolBufferException("Invalid numeric value: " + text);
|
|
|
- }
|
|
|
+ float f = ParseNumericString(text, float.Parse);
|
|
|
+ ValidateInfinityAndNan(text, float.IsPositiveInfinity(f), float.IsNegativeInfinity(f), float.IsNaN(f));
|
|
|
return f;
|
|
|
case FieldType.Enum:
|
|
|
var enumValue = field.EnumType.FindValueByName(text);
|
|
|
if (enumValue == null)
|
|
|
{
|
|
|
- throw new InvalidProtocolBufferException("Invalid enum value: " + text + " for enum type: " + field.EnumType.FullName);
|
|
|
+ throw new InvalidProtocolBufferException($"Invalid enum value: {text} for enum type: {field.EnumType.FullName}");
|
|
|
}
|
|
|
// Just return it as an int, and let the CLR convert it.
|
|
|
return enumValue.Number;
|
|
|
default:
|
|
|
- throw new InvalidProtocolBufferException("Unsupported conversion from JSON string for field type " + field.FieldType);
|
|
|
+ throw new InvalidProtocolBufferException($"Unsupported conversion from JSON string for field type {field.FieldType}");
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -670,43 +677,53 @@ namespace Google.Protobuf
|
|
|
return field.MessageType.Parser.CreateTemplate();
|
|
|
}
|
|
|
|
|
|
- private static T ParseNumericString<T>(string text, Func<string, NumberStyles, IFormatProvider, T> parser, bool floatingPoint)
|
|
|
+ private static T ParseNumericString<T>(string text, Func<string, NumberStyles, IFormatProvider, T> parser)
|
|
|
{
|
|
|
- // TODO: Prohibit leading zeroes (but allow 0!)
|
|
|
- // TODO: Validate handling of "Infinity" etc. (Should be case sensitive, no leading whitespace etc)
|
|
|
// Can't prohibit this with NumberStyles.
|
|
|
if (text.StartsWith("+"))
|
|
|
{
|
|
|
- throw new InvalidProtocolBufferException("Invalid numeric value: " + text);
|
|
|
+ throw new InvalidProtocolBufferException($"Invalid numeric value: {text}");
|
|
|
}
|
|
|
if (text.StartsWith("0") && text.Length > 1)
|
|
|
{
|
|
|
if (text[1] >= '0' && text[1] <= '9')
|
|
|
{
|
|
|
- throw new InvalidProtocolBufferException("Invalid numeric value: " + text);
|
|
|
+ throw new InvalidProtocolBufferException($"Invalid numeric value: {text}");
|
|
|
}
|
|
|
}
|
|
|
else if (text.StartsWith("-0") && text.Length > 2)
|
|
|
{
|
|
|
if (text[2] >= '0' && text[2] <= '9')
|
|
|
{
|
|
|
- throw new InvalidProtocolBufferException("Invalid numeric value: " + text);
|
|
|
+ throw new InvalidProtocolBufferException($"Invalid numeric value: {text}");
|
|
|
}
|
|
|
}
|
|
|
try
|
|
|
{
|
|
|
- var styles = floatingPoint
|
|
|
- ? NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent
|
|
|
- : NumberStyles.AllowLeadingSign;
|
|
|
- return parser(text, styles, CultureInfo.InvariantCulture);
|
|
|
+ return parser(text, NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent, CultureInfo.InvariantCulture);
|
|
|
}
|
|
|
catch (FormatException)
|
|
|
{
|
|
|
- throw new InvalidProtocolBufferException("Invalid numeric value for type: " + text);
|
|
|
+ throw new InvalidProtocolBufferException($"Invalid numeric value for type: {text}");
|
|
|
}
|
|
|
catch (OverflowException)
|
|
|
{
|
|
|
- throw new InvalidProtocolBufferException("Value out of range: " + text);
|
|
|
+ throw new InvalidProtocolBufferException($"Value out of range: {text}");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Checks that any infinite/NaN values originated from the correct text.
|
|
|
+ /// This corrects the lenient whitespace handling of double.Parse/float.Parse, as well as the
|
|
|
+ /// way that Mono parses out-of-range values as infinity.
|
|
|
+ /// </summary>
|
|
|
+ private static void ValidateInfinityAndNan(string text, bool isPositiveInfinity, bool isNegativeInfinity, bool isNaN)
|
|
|
+ {
|
|
|
+ if ((isPositiveInfinity && text != "Infinity") ||
|
|
|
+ (isNegativeInfinity && text != "-Infinity") ||
|
|
|
+ (isNaN && text != "NaN"))
|
|
|
+ {
|
|
|
+ throw new InvalidProtocolBufferException($"Invalid numeric value: {text}");
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -719,7 +736,7 @@ namespace Google.Protobuf
|
|
|
var match = TimestampRegex.Match(token.StringValue);
|
|
|
if (!match.Success)
|
|
|
{
|
|
|
- throw new InvalidProtocolBufferException("Invalid Timestamp value: " + token.StringValue);
|
|
|
+ throw new InvalidProtocolBufferException($"Invalid Timestamp value: {token.StringValue}");
|
|
|
}
|
|
|
var dateTime = match.Groups["datetime"].Value;
|
|
|
var subseconds = match.Groups["subseconds"].Value;
|