Skip to content

[stdlib] Replace Integer Parsing Code #9729

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

Merged
merged 1 commit into from
May 18, 2017
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
2 changes: 1 addition & 1 deletion stdlib/public/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ set(SWIFTLIB_ESSENTIAL
Index.swift
Indices.swift.gyb
InputStream.swift
IntegerParsing.swift.gyb
IntegerParsing.swift
Integers.swift.gyb
Join.swift
KeyPath.swift
Expand Down
143 changes: 143 additions & 0 deletions stdlib/public/core/IntegerParsing.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

@inline(__always)
internal func _asciiDigit<CodeUnit : UnsignedInteger, Result : BinaryInteger>(
codeUnit u_: CodeUnit, radix: Result
) -> Result? {
let digit = _ascii16("0")..._ascii16("9")
let lower = _ascii16("a")..._ascii16("z")
let upper = _ascii16("A")..._ascii16("Z")

let u = UInt16(extendingOrTruncating: u_)
let d: UInt16
if _fastPath(digit ~= u) { d = u &- digit.lowerBound }
else if _fastPath(upper ~= u) { d = u &- upper.lowerBound &+ 10 }
else if _fastPath(lower ~= u) { d = u &- lower.lowerBound &+ 10 }
else { return nil }
guard _fastPath(d < radix) else { return nil }
return Result(extendingOrTruncating: d)
}

@inline(__always)
internal func _parseUnsignedASCII<
Rest : IteratorProtocol, Result: FixedWidthInteger
>(
first: Rest.Element, rest: inout Rest, radix: Result, positive: Bool
) -> Result?
where Rest.Element : UnsignedInteger {
let r0 = _asciiDigit(codeUnit: first, radix: radix)
guard _fastPath(r0 != nil), var result = r0 else { return nil }
if !positive {
let (result0, overflow0)
= (0 as Result).subtractingReportingOverflow(result)
guard _fastPath(overflow0 == .none) else { return nil }
result = result0
}

while let u = rest.next() {
let d0 = _asciiDigit(codeUnit: u, radix: radix)
guard _fastPath(d0 != nil), let d = d0 else { return nil }
let (result1, overflow1) = result.multipliedReportingOverflow(by: radix)
let (result2, overflow2) = positive
? result1.addingReportingOverflow(d)
: result1.subtractingReportingOverflow(d)
guard _fastPath(overflow1 == .none && overflow2 == .none)
else { return nil }
result = result2
}
return result
}

@inline(__always)
internal func _parseASCII<
CodeUnits : IteratorProtocol, Result: FixedWidthInteger
>(
codeUnits: inout CodeUnits, radix: Result
) -> Result?
where CodeUnits.Element : UnsignedInteger {
let c0_ = codeUnits.next()
guard _fastPath(c0_ != nil), let c0 = c0_ else { return nil }
if _fastPath(c0 != _ascii16("+") && c0 != _ascii16("-")) {
return _parseUnsignedASCII(
first: c0, rest: &codeUnits, radix: radix, positive: true)
}
let c1_ = codeUnits.next()
guard _fastPath(c1_ != nil), let c1 = c1_ else { return nil }
if _fastPath(c0 == _ascii16("-")) {
return _parseUnsignedASCII(
first: c1, rest: &codeUnits, radix: radix, positive: false)
}
else {
return _parseUnsignedASCII(
first: c1, rest: &codeUnits, radix: radix, positive: true)
}
}

extension FixedWidthInteger {
/// Creates a new integer value from the given string and radix.
///
/// The string passed as `text` may begin with a plus or minus sign character
/// (`+` or `-`), followed by one or more numeric digits (`0-9`) or letters
/// (`a-z` or `A-Z`). The string is case insensitive.
///
/// let x = Int("123")
/// // x == 123
///
/// let y = Int("-123", radix: 8)
/// // y == -83
/// let y = Int("+123", radix: 8)
/// // y == +83
///
/// let z = Int("07b", radix: 16)
/// // z == 123
///
/// If `text` is in an invalid format or contains characters that are out of
/// range for the given `radix`, or if the value it denotes in the given
/// `radix` is not representable, the result is `nil`. For example, the
/// following conversions result in `nil`:
///
/// Int(" 100") // Includes whitespace
/// Int("21-50") // Invalid format
/// Int("ff6600") // Characters out of bounds
/// Int("zzzzzzzzzzzzz", radix: 36) // Out of range
///
/// - Parameters:
/// - text: The ASCII representation of a number in the radix passed as
/// `radix`.
/// - radix: The radix, or base, to use for converting `text` to an integer
/// value. `radix` must be in the range `2...36`. The default is 10.
public init?/*<S : StringProtocol>*/(_ text: String, radix: Int = 10) {
_precondition(2...36 ~= radix, "Radix not in range 2...36")
let r = Self(radix)
let s = text// ._ephemeralString
defer { _fixLifetime(s) }

let c = s._core
let result: Self?
if _slowPath(c._baseAddress == nil) {
var i = s.utf16.makeIterator()
result = _parseASCII(codeUnits: &i, radix: r)
}
else if _fastPath(c.elementWidth == 1), let a = c.asciiBuffer {
var i = a.makeIterator()
result = _parseASCII(codeUnits: &i, radix: r)
}
else {
let b = UnsafeBufferPointer(start: c.startUTF16, count: c.count)
var i = b.makeIterator()
result = _parseASCII(codeUnits: &i, radix: r)
}
guard _fastPath(result != nil) else { return nil }
self = result!
}
}
179 changes: 0 additions & 179 deletions stdlib/public/core/IntegerParsing.swift.gyb

This file was deleted.