From 0998e38ef42811f1a41899b47b0c880952416d7c Mon Sep 17 00:00:00 2001 From: Patrick Pijnappel Date: Thu, 17 Dec 2015 09:57:53 +1100 Subject: [PATCH 1/6] [stdlib] Fix internal doc comment --- stdlib/public/core/IntegerParsing.swift.gyb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/public/core/IntegerParsing.swift.gyb b/stdlib/public/core/IntegerParsing.swift.gyb index 66e64f977e264..f2419c2c94b89 100644 --- a/stdlib/public/core/IntegerParsing.swift.gyb +++ b/stdlib/public/core/IntegerParsing.swift.gyb @@ -67,8 +67,8 @@ internal func _parseUnsignedAsciiAsUIntMax( /// non-negative number <= `maximum`, return that number. Otherwise, /// return `nil`. /// -/// - Note: If `text` begins with `"+"` or `"-"`, even if the rest of -/// the characters are `"0"`, the result is `nil`. +/// - Note: For text matching the regular expression "-0+", the result +/// is `0`, not `nil`. internal func _parseAsciiAsUIntMax( u16: String.UTF16View, _ radix: Int, _ maximum: UIntMax ) -> UIntMax? { From 8d1261ed2239b64df5275af3eb4df67145d89c1a Mon Sep 17 00:00:00 2001 From: Patrick Pijnappel Date: Thu, 17 Dec 2015 10:12:36 +1100 Subject: [PATCH 2/6] [stdlib] Fix Int(_:radix:) accepting unintentional cases (SR-187) --- stdlib/public/core/IntegerParsing.swift.gyb | 52 +++++++++++---------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/stdlib/public/core/IntegerParsing.swift.gyb b/stdlib/public/core/IntegerParsing.swift.gyb index f2419c2c94b89..d9b16dc365390 100644 --- a/stdlib/public/core/IntegerParsing.swift.gyb +++ b/stdlib/public/core/IntegerParsing.swift.gyb @@ -70,18 +70,17 @@ internal func _parseUnsignedAsciiAsUIntMax( /// - Note: For text matching the regular expression "-0+", the result /// is `0`, not `nil`. internal func _parseAsciiAsUIntMax( - u16: String.UTF16View, _ radix: Int, _ maximum: UIntMax + utf16: String.UTF16View, _ radix: Int, _ maximum: UIntMax ) -> UIntMax? { - if u16.isEmpty { return nil } - let c = u16.first - if _fastPath(c != _ascii16("-")) { - let unsignedText - = c == _ascii16("+") ? u16.dropFirst() : u16 - return _parseUnsignedAsciiAsUIntMax(unsignedText, radix, maximum) - } - else { - return _parseAsciiAsIntMax(u16, radix, 0) == 0 ? 0 : nil - } + if utf16.isEmpty { return nil } + // Parse (optional) sign + let (digitsUTF16, hasMinus) = _parseOptionalAsciiSign(utf16) + // Parse digits + guard let result = _parseUnsignedAsciiAsUIntMax(digitsUTF16, radix, maximum) else { return nil } + // Disallow < 0 + if hasMinus && result != 0 { return nil } + // Return + return result } /// If text is an ASCII representation in the given `radix` of a @@ -91,23 +90,28 @@ internal func _parseAsciiAsUIntMax( /// - Note: For text matching the regular expression "-0+", the result /// is `0`, not `nil`. internal func _parseAsciiAsIntMax( - u16: String.UTF16View, _ radix: Int, _ maximum: IntMax + utf16: String.UTF16View, _ radix: Int, _ maximum: IntMax ) -> IntMax? { _sanityCheck(maximum >= 0, "maximum should be non-negative") + if utf16.isEmpty { return nil } + // Parse (optional) sign + let (digitsUTF16, hasMinus) = _parseOptionalAsciiSign(utf16) + // Parse digits + let absValueMax = UIntMax(bitPattern: maximum) + (hasMinus ? 1 : 0) + guard let absValue = _parseUnsignedAsciiAsUIntMax(digitsUTF16, radix, absValueMax) else { return nil } + // Return signed result + return IntMax(bitPattern: hasMinus ? 0 &- absValue : absValue) +} - if u16.isEmpty { return nil } - - // Drop any leading "-" - let negative = u16.first == _ascii16("-") - let absResultText = negative ? u16.dropFirst() : u16 - - let absResultMax = UIntMax(bitPattern: maximum) + (negative ? 1 : 0) - - // Parse the result as unsigned - if let absResult = _parseAsciiAsUIntMax(absResultText, radix, absResultMax) { - return IntMax(bitPattern: negative ? 0 &- absResult : absResult) +/// Strip an optional single leading ASCII plus/minus sign from `utf16`. +private func _parseOptionalAsciiSign( + utf16: String.UTF16View +) -> (digitsUTF16: String.UTF16View, isMinus: Bool) { + switch utf16.first { + case _ascii16("-")?: return (utf16.dropFirst(), true) + case _ascii16("+")?: return (utf16.dropFirst(), false) + default: return (utf16, false) } - return nil } //===--- Loop over all integer types --------------------------------------===// From 58bc25bed631f90535a402dd8aea8914fb8383d9 Mon Sep 17 00:00:00 2001 From: Patrick Pijnappel Date: Thu, 17 Dec 2015 10:15:45 +1100 Subject: [PATCH 3/6] [stdlib] Add comment --- stdlib/public/core/IntegerParsing.swift.gyb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/core/IntegerParsing.swift.gyb b/stdlib/public/core/IntegerParsing.swift.gyb index d9b16dc365390..ea267f3daceec 100644 --- a/stdlib/public/core/IntegerParsing.swift.gyb +++ b/stdlib/public/core/IntegerParsing.swift.gyb @@ -97,7 +97,7 @@ internal func _parseAsciiAsIntMax( // Parse (optional) sign let (digitsUTF16, hasMinus) = _parseOptionalAsciiSign(utf16) // Parse digits - let absValueMax = UIntMax(bitPattern: maximum) + (hasMinus ? 1 : 0) + let absValueMax = UIntMax(bitPattern: maximum) + (hasMinus ? 1 : 0) // E.g. Int8's range is -128...127 guard let absValue = _parseUnsignedAsciiAsUIntMax(digitsUTF16, radix, absValueMax) else { return nil } // Return signed result return IntMax(bitPattern: hasMinus ? 0 &- absValue : absValue) From e6b08b9781e9fdcba44052a6c6ebc0471191a3ac Mon Sep 17 00:00:00 2001 From: Patrick Pijnappel Date: Thu, 17 Dec 2015 11:22:07 +1100 Subject: [PATCH 4/6] Add tests for Int(_:radix) accepting unintentional cases (SR-187) --- test/1_stdlib/NumericParsing.swift.gyb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/1_stdlib/NumericParsing.swift.gyb b/test/1_stdlib/NumericParsing.swift.gyb index 35a45f8f2ecd7..fc2f13116990f 100644 --- a/test/1_stdlib/NumericParsing.swift.gyb +++ b/test/1_stdlib/NumericParsing.swift.gyb @@ -92,6 +92,10 @@ tests.test("${Self}/success") { expectEqual(nil, ${Self}("${minValue - 1}")) expectEqual(nil, ${Self}("\u{1D7FF}")) // MATHEMATICAL MONOSPACE DIGIT NINE + // Cases that should fail to parse + expectEqual(nil, ${Self}("--0")) // Zero w/ repeated plus + expectEqual(nil, ${Self}("-+5")) // Non-zero with -+ + // Do more exhaustive testing % for radix in radices_to_test: % for n in required_values + range( From 4faa43aa885eb030db4e76bb90d5f2ea42b04094 Mon Sep 17 00:00:00 2001 From: Patrick Pijnappel Date: Mon, 21 Dec 2015 13:23:12 +1100 Subject: [PATCH 5/6] [stdlib] Adjust formatting to fit 80 colums --- stdlib/public/core/IntegerParsing.swift.gyb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/stdlib/public/core/IntegerParsing.swift.gyb b/stdlib/public/core/IntegerParsing.swift.gyb index ea267f3daceec..68b1d096d8a32 100644 --- a/stdlib/public/core/IntegerParsing.swift.gyb +++ b/stdlib/public/core/IntegerParsing.swift.gyb @@ -76,7 +76,8 @@ internal func _parseAsciiAsUIntMax( // Parse (optional) sign let (digitsUTF16, hasMinus) = _parseOptionalAsciiSign(utf16) // Parse digits - guard let result = _parseUnsignedAsciiAsUIntMax(digitsUTF16, radix, maximum) else { return nil } + guard let result = _parseUnsignedAsciiAsUIntMax(digitsUTF16, radix, maximum) + else { return nil } // Disallow < 0 if hasMinus && result != 0 { return nil } // Return @@ -96,9 +97,11 @@ internal func _parseAsciiAsIntMax( if utf16.isEmpty { return nil } // Parse (optional) sign let (digitsUTF16, hasMinus) = _parseOptionalAsciiSign(utf16) - // Parse digits - let absValueMax = UIntMax(bitPattern: maximum) + (hasMinus ? 1 : 0) // E.g. Int8's range is -128...127 - guard let absValue = _parseUnsignedAsciiAsUIntMax(digitsUTF16, radix, absValueMax) else { return nil } + // Parse digits. +1 for negatives because e.g. Int8's range is -128...127. + let absValueMax = UIntMax(bitPattern: maximum) + (hasMinus ? 1 : 0) + guard let absValue = + _parseUnsignedAsciiAsUIntMax(digitsUTF16, radix, absValueMax) + else { return nil } // Return signed result return IntMax(bitPattern: hasMinus ? 0 &- absValue : absValue) } From 17a5845640023198caa66bdf6944acd2ad21f941 Mon Sep 17 00:00:00 2001 From: Patrick Pijnappel Date: Mon, 21 Dec 2015 13:31:34 +1100 Subject: [PATCH 6/6] [stdlib] Add terminating period to comments --- stdlib/public/core/IntegerParsing.swift.gyb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/stdlib/public/core/IntegerParsing.swift.gyb b/stdlib/public/core/IntegerParsing.swift.gyb index 68b1d096d8a32..042111b792037 100644 --- a/stdlib/public/core/IntegerParsing.swift.gyb +++ b/stdlib/public/core/IntegerParsing.swift.gyb @@ -73,14 +73,14 @@ internal func _parseAsciiAsUIntMax( utf16: String.UTF16View, _ radix: Int, _ maximum: UIntMax ) -> UIntMax? { if utf16.isEmpty { return nil } - // Parse (optional) sign + // Parse (optional) sign. let (digitsUTF16, hasMinus) = _parseOptionalAsciiSign(utf16) - // Parse digits + // Parse digits. guard let result = _parseUnsignedAsciiAsUIntMax(digitsUTF16, radix, maximum) else { return nil } - // Disallow < 0 + // Disallow < 0. if hasMinus && result != 0 { return nil } - // Return + // Return. return result } @@ -95,14 +95,14 @@ internal func _parseAsciiAsIntMax( ) -> IntMax? { _sanityCheck(maximum >= 0, "maximum should be non-negative") if utf16.isEmpty { return nil } - // Parse (optional) sign + // Parse (optional) sign. let (digitsUTF16, hasMinus) = _parseOptionalAsciiSign(utf16) - // Parse digits. +1 for negatives because e.g. Int8's range is -128...127. + // Parse digits. +1 for because e.g. Int8's range is -128...127. let absValueMax = UIntMax(bitPattern: maximum) + (hasMinus ? 1 : 0) guard let absValue = _parseUnsignedAsciiAsUIntMax(digitsUTF16, radix, absValueMax) else { return nil } - // Return signed result + // Return signed result. return IntMax(bitPattern: hasMinus ? 0 &- absValue : absValue) }