Skip to content

Commit c67977a

Browse files
author
Dave Abrahams
committed
[stdlib] Replace Integer Parsing Code
1 parent 9939306 commit c67977a

File tree

3 files changed

+144
-180
lines changed

3 files changed

+144
-180
lines changed

stdlib/public/core/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ set(SWIFTLIB_ESSENTIAL
7373
Index.swift
7474
Indices.swift.gyb
7575
InputStream.swift
76-
IntegerParsing.swift.gyb
76+
IntegerParsing.swift
7777
Integers.swift.gyb
7878
Join.swift
7979
KeyPath.swift
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
@inline(__always)
14+
internal func _asciiDigit<CodeUnit : UnsignedInteger, Result : BinaryInteger>(
15+
codeUnit u_: CodeUnit, radix: Result
16+
) -> Result? {
17+
let digit = _ascii16("0")..._ascii16("9")
18+
let lower = _ascii16("a")..._ascii16("z")
19+
let upper = _ascii16("A")..._ascii16("Z")
20+
21+
let u = UInt16(extendingOrTruncating: u_)
22+
let d: UInt16
23+
if _fastPath(digit ~= u) { d = u &- digit.lowerBound }
24+
else if _fastPath(upper ~= u) { d = u &- upper.lowerBound &+ 10 }
25+
else if _fastPath(lower ~= u) { d = u &- lower.lowerBound &+ 10 }
26+
else { return nil }
27+
guard _fastPath(d < radix) else { return nil }
28+
return Result(extendingOrTruncating: d)
29+
}
30+
31+
@inline(__always)
32+
internal func _parseUnsignedASCII<
33+
Rest : IteratorProtocol, Result: FixedWidthInteger
34+
>(
35+
first: Rest.Element, rest: inout Rest, radix: Result, positive: Bool
36+
) -> Result?
37+
where Rest.Element : UnsignedInteger {
38+
let r0 = _asciiDigit(codeUnit: first, radix: radix)
39+
guard _fastPath(r0 != nil), var result = r0 else { return nil }
40+
if !positive {
41+
let (result0, overflow0)
42+
= (0 as Result).subtractingReportingOverflow(result)
43+
guard _fastPath(overflow0 == .none) else { return nil }
44+
result = result0
45+
}
46+
47+
while let u = rest.next() {
48+
let d0 = _asciiDigit(codeUnit: u, radix: radix)
49+
guard _fastPath(d0 != nil), let d = d0 else { return nil }
50+
let (result1, overflow1) = result.multipliedReportingOverflow(by: radix)
51+
let (result2, overflow2) = positive
52+
? result1.addingReportingOverflow(d)
53+
: result1.subtractingReportingOverflow(d)
54+
guard _fastPath(overflow1 == .none && overflow2 == .none)
55+
else { return nil }
56+
result = result2
57+
}
58+
return result
59+
}
60+
61+
@inline(__always)
62+
internal func _parseASCII<
63+
CodeUnits : IteratorProtocol, Result: FixedWidthInteger
64+
>(
65+
codeUnits: inout CodeUnits, radix: Result
66+
) -> Result?
67+
where CodeUnits.Element : UnsignedInteger {
68+
let c0_ = codeUnits.next()
69+
guard _fastPath(c0_ != nil), let c0 = c0_ else { return nil }
70+
if _fastPath(c0 != _ascii16("+") && c0 != _ascii16("-")) {
71+
return _parseUnsignedASCII(
72+
first: c0, rest: &codeUnits, radix: radix, positive: true)
73+
}
74+
let c1_ = codeUnits.next()
75+
guard _fastPath(c1_ != nil), let c1 = c1_ else { return nil }
76+
if _fastPath(c0 == _ascii16("-")) {
77+
return _parseUnsignedASCII(
78+
first: c1, rest: &codeUnits, radix: radix, positive: false)
79+
}
80+
else {
81+
return _parseUnsignedASCII(
82+
first: c1, rest: &codeUnits, radix: radix, positive: true)
83+
}
84+
}
85+
86+
extension FixedWidthInteger {
87+
/// Creates a new integer value from the given string and radix.
88+
///
89+
/// The string passed as `text` may begin with a plus or minus sign character
90+
/// (`+` or `-`), followed by one or more numeric digits (`0-9`) or letters
91+
/// (`a-z` or `A-Z`). The string is case insensitive.
92+
///
93+
/// let x = Int("123")
94+
/// // x == 123
95+
///
96+
/// let y = Int("-123", radix: 8)
97+
/// // y == -83
98+
/// let y = Int("+123", radix: 8)
99+
/// // y == +83
100+
///
101+
/// let z = Int("07b", radix: 16)
102+
/// // z == 123
103+
///
104+
/// If `text` is in an invalid format or contains characters that are out of
105+
/// range for the given `radix`, or if the value it denotes in the given
106+
/// `radix` is not representable, the result is `nil`. For example, the
107+
/// following conversions result in `nil`:
108+
///
109+
/// Int(" 100") // Includes whitespace
110+
/// Int("21-50") // Invalid format
111+
/// Int("ff6600") // Characters out of bounds
112+
/// Int("zzzzzzzzzzzzz", radix: 36) // Out of range
113+
///
114+
/// - Parameters:
115+
/// - text: The ASCII representation of a number in the radix passed as
116+
/// `radix`.
117+
/// - radix: The radix, or base, to use for converting `text` to an integer
118+
/// value. `radix` must be in the range `2...36`. The default is 10.
119+
public init?/*<S : StringProtocol>*/(_ text: String, radix: Int = 10) {
120+
_precondition(2...36 ~= radix, "Radix not in range 2...36")
121+
let r = Self(radix)
122+
let s = text// ._ephemeralString
123+
defer { _fixLifetime(s) }
124+
125+
let c = s._core
126+
let result: Self?
127+
if _slowPath(c._baseAddress == nil) {
128+
var i = s.utf16.makeIterator()
129+
result = _parseASCII(codeUnits: &i, radix: r)
130+
}
131+
else if _fastPath(c.elementWidth == 1), let a = c.asciiBuffer {
132+
var i = a.makeIterator()
133+
result = _parseASCII(codeUnits: &i, radix: r)
134+
}
135+
else {
136+
let b = UnsafeBufferPointer(start: c.startUTF16, count: c.count)
137+
var i = b.makeIterator()
138+
result = _parseASCII(codeUnits: &i, radix: r)
139+
}
140+
guard _fastPath(result != nil) else { return nil }
141+
self = result!
142+
}
143+
}

stdlib/public/core/IntegerParsing.swift.gyb

Lines changed: 0 additions & 179 deletions
This file was deleted.

0 commit comments

Comments
 (0)