Skip to content

Commit 793b14f

Browse files
committed
Support "-nan" in TextFormat.
As odd as it sounds, upstream supports this and there is a unittest that ensure it parses: https://github.com/protocolbuffers/protobuf/blob/3c03e9351c57081d0dffae120ed37497017f105c/src/google/protobuf/compiler/parser_unittest.cc#L464 It seems to have come from: protocolbuffers/protobuf#15017 So make sure Swift is also able to parse it. Also reflow some of the unknown field parsing as inf/nan don't need to be special cased with how the flow now works.
1 parent f265238 commit 793b14f

File tree

3 files changed

+36
-21
lines changed

3 files changed

+36
-21
lines changed

Sources/SwiftProtobuf/TextFormatScanner.swift

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -897,8 +897,18 @@ internal struct TextFormatScanner {
897897

898898
// If the next token is the identifier "nan", return true.
899899
private mutating func skipOptionalNaN() -> Bool {
900-
return skipOptionalKeyword(bytes:
901-
[asciiLowerN, asciiLowerA, asciiLowerN])
900+
let start = p
901+
// "-nan" doesn't mean anything, but upstream handles it, so skip
902+
// over any leading minus when checking for "nan".
903+
if p != end && p[0] == asciiMinus {
904+
p += 1
905+
}
906+
if skipOptionalKeyword(bytes: [asciiLowerN, asciiLowerA, asciiLowerN]) {
907+
return true
908+
} else {
909+
p = start // It wasn't "nan", rewind incase we skipped a minus sign.
910+
return false
911+
}
902912
}
903913

904914
// If the next token is a recognized spelling of "infinity",
@@ -1243,40 +1253,40 @@ internal struct TextFormatScanner {
12431253

12441254
/// Helper to see if this could be the start of a hex or octal number so unknown field
12451255
/// value parsing can decide how to parse/validate.
1246-
private func isNextNumberFloatingPoint() -> Bool {
1247-
// NOTE: If we run out of characters can can't tell, say it isn't and let
1248-
// the other code error handle.
1256+
private func mustParseNumberAsDecimal() -> Bool {
1257+
// NOTE: If we run out of characters/can't tell; then just say it doesn't have
1258+
// to be decimal, and let the other code error handle it.
12491259
var scan = p
12501260
var c = scan[0]
12511261

1252-
// Floats for decimals can have leading '-'
1262+
// Floats or decimals can have leading '-'
12531263
if c == asciiMinus {
12541264
scan += 1
12551265
if scan == end { return false }
12561266
c = scan[0]
12571267
}
12581268

12591269
if c == asciiPeriod {
1260-
return true // "(-)." : clearly a float
1270+
return false // "(-)." : clearly a float
12611271
}
12621272

12631273
if c == asciiZero {
12641274
scan += 1
1265-
if scan == end { return false } // "(-)0[end]" : call it not a float
1275+
if scan == end { return true } // "(-)0[end]" : parse it as decimal
12661276
c = scan[0]
1267-
if c == asciiLowerX || // "(-)0x" : hex - not a float
1268-
(c >= asciiZero && c <= asciiSeven) { // "(-)0[0-7]" : octal - not a float
1269-
return false
1277+
if c == asciiLowerX || // "(-)0x" : hex - must parse as decimal
1278+
(c >= asciiZero && c <= asciiSeven) { // "(-)0[0-7]" : octal - must parse as decimal
1279+
return true
12701280
}
12711281
if c == asciiPeriod {
1272-
return true // "(-)0." : clearly a float
1282+
return false // "(-)0." : clearly a float
12731283
}
12741284
}
12751285

12761286
// At this point, it doesn't realy matter what comes next. We'll call it a floating
12771287
// point value since even if it was a decimal, it might be too large for a UInt64 but
12781288
// would still be valid for a float/double field.
1279-
return true
1289+
return false
12801290
}
12811291

12821292
private mutating func skipUnknownPrimativeFieldValue() throws {
@@ -1311,24 +1321,22 @@ internal struct TextFormatScanner {
13111321
}
13121322
}
13131323

1314-
// This will also cover "true", "false" for booleans, "nan" for floats.
1324+
// NOTE: This will also cover "true", "false" for booleans, "nan"/"inf" for floats.
13151325
if let _ = try nextOptionalEnumName() {
13161326
skipWhitespace() // `nextOptionalEnumName()` doesn't skip trailing whitespace
13171327
return
13181328
}
1319-
// The above will handing "inf", but this is needed for "-inf".
1320-
if let _ = skipOptionalInfinity() {
1321-
return
1322-
}
13231329

1324-
if isNextNumberFloatingPoint() {
1325-
let _ = try nextDouble()
1326-
} else {
1330+
// NOTE: We don't need to special case "-nan"/"-inf", as they won't be forced
1331+
// to parse as decimal, and `nextDouble()` already supports them.
1332+
if mustParseNumberAsDecimal() {
13271333
if c == asciiMinus {
13281334
let _ = try nextSInt()
13291335
} else {
13301336
let _ = try nextUInt()
13311337
}
1338+
} else {
1339+
let _ = try nextDouble()
13321340
}
13331341
}
13341342

Sources/protoc-gen-swift/Descriptor+Extensions.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,13 +352,15 @@ extension FieldDescriptor {
352352
case "inf": return "Double.infinity"
353353
case "-inf": return "-Double.infinity"
354354
case "nan": return "Double.nan"
355+
case "-nan": return "Double.nan"
355356
default: return defaultValue
356357
}
357358
case .float:
358359
switch defaultValue {
359360
case "inf": return "Float.infinity"
360361
case "-inf": return "-Float.infinity"
361362
case "nan": return "Float.nan"
363+
case "-nan": return "Float.nan"
362364
default: return defaultValue
363365
}
364366
case .string:

Tests/SwiftProtobufTests/Test_TextFormatDecodingOptions.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,8 +409,13 @@ final class Test_TextFormatDecodingOptions: XCTestCase {
409409
assertDecodeIgnoringUnknownsSucceeds("double", "-1e-9999")
410410

411411
assertDecodeIgnoringUnknownsSucceeds("float", "nan")
412+
assertDecodeIgnoringUnknownsSucceeds("float", "-nan")
412413
assertDecodeIgnoringUnknownsSucceeds("float", "inf")
413414
assertDecodeIgnoringUnknownsSucceeds("float", "-inf")
415+
assertDecodeIgnoringUnknownsSucceeds("double", "nan")
416+
assertDecodeIgnoringUnknownsSucceeds("double", "-nan")
417+
assertDecodeIgnoringUnknownsSucceeds("double", "inf")
418+
assertDecodeIgnoringUnknownsSucceeds("double", "-inf")
414419
}
415420

416421
func testIgnoreUnknown_Messages() {

0 commit comments

Comments
 (0)