mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
176 lines
6.2 KiB
Plaintext
176 lines
6.2 KiB
Plaintext
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2016 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
%{
|
|
|
|
from SwiftIntTypes import all_integer_types, int_max_bits
|
|
|
|
# Number of bits in the Builtin.Word type
|
|
word_bits = int(CMAKE_SIZEOF_VOID_P) * 8
|
|
|
|
}%
|
|
|
|
//===--- Parsing helpers --------------------------------------------------===//
|
|
|
|
/// If text is an ASCII representation in the given `radix` of a
|
|
/// 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`.
|
|
// FIXME(integers): support a more general BinaryInteger protocol
|
|
internal func _parseUnsignedAsciiAsUInt64(
|
|
_ u16: String.UTF16View, _ radix: Int, _ maximum: UInt64
|
|
) -> UInt64? {
|
|
if u16.isEmpty { return nil }
|
|
|
|
let digit = _ascii16("0")..._ascii16("9")
|
|
let lower = _ascii16("a")..._ascii16("z")
|
|
let upper = _ascii16("A")..._ascii16("Z")
|
|
|
|
_precondition(radix > 1, "Radix must be greater than 1")
|
|
_precondition(
|
|
radix <= (10 + lower.count),
|
|
"Radix exceeds what can be expressed using the English alphabet")
|
|
|
|
let uRadix = UInt64(bitPattern: Int64(radix))
|
|
var result: UInt64 = 0
|
|
for c in u16 {
|
|
let n: UInt64
|
|
switch c {
|
|
case digit: n = UInt64(extendingOrTruncating: c - digit.lowerBound)
|
|
case lower: n = UInt64(extendingOrTruncating: c - lower.lowerBound) + 10
|
|
case upper: n = UInt64(extendingOrTruncating: c - upper.lowerBound) + 10
|
|
default: return nil
|
|
}
|
|
if n >= uRadix { return nil }
|
|
let (result1, overflow1) = result.multipliedWithOverflow(by: uRadix)
|
|
let (result2, overflow2) = result1.addingWithOverflow(n)
|
|
result = result2
|
|
if overflow1 != .none || overflow2 != .none || result > maximum {
|
|
return nil
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
/// If text is an ASCII representation in the given `radix` of a
|
|
/// non-negative number <= `maximum`, return that number. Otherwise,
|
|
/// return `nil`.
|
|
///
|
|
/// - Note: For text matching the regular expression "-0+", the result
|
|
/// is `0`, not `nil`.
|
|
// FIXME(integers): support a more general BinaryInteger protocol
|
|
internal func _parseAsciiAsUInt64(
|
|
_ utf16: String.UTF16View, _ radix: Int, _ maximum: UInt64
|
|
) -> UInt64? {
|
|
if utf16.isEmpty { return nil }
|
|
// Parse (optional) sign.
|
|
let (digitsUTF16, hasMinus) = _parseOptionalAsciiSign(utf16)
|
|
// Parse digits.
|
|
guard let result = _parseUnsignedAsciiAsUInt64(digitsUTF16, radix, maximum)
|
|
else { return nil }
|
|
// Disallow < 0.
|
|
if hasMinus && result != 0 { return nil }
|
|
|
|
return result
|
|
}
|
|
|
|
/// If text is an ASCII representation in the given `radix` of a
|
|
/// number >= -`maximum` - 1 and <= `maximum`, return that number.
|
|
/// Otherwise, return `nil`.
|
|
///
|
|
/// - Note: For text matching the regular expression "-0+", the result
|
|
/// is `0`, not `nil`.
|
|
// FIXME(integers): support a more general BinaryInteger protocol
|
|
internal func _parseAsciiAsInt64(
|
|
_ utf16: String.UTF16View, _ radix: Int, _ maximum: Int64
|
|
) -> Int64? {
|
|
_sanityCheck(maximum >= 0, "maximum should be non-negative")
|
|
if utf16.isEmpty { return nil }
|
|
// Parse (optional) sign.
|
|
let (digitsUTF16, hasMinus) = _parseOptionalAsciiSign(utf16)
|
|
// Parse digits. +1 for negatives because e.g. Int8's range is -128...127.
|
|
let absValueMax = UInt64(bitPattern: maximum) + (hasMinus ? 1 : 0)
|
|
guard let absValue =
|
|
_parseUnsignedAsciiAsUInt64(digitsUTF16, radix, absValueMax)
|
|
else { return nil }
|
|
// Convert to signed.
|
|
return Int64(bitPattern: hasMinus ? 0 &- absValue : absValue)
|
|
}
|
|
|
|
/// 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)
|
|
}
|
|
}
|
|
|
|
//===--- Loop over all integer types --------------------------------------===//
|
|
% for self_ty in all_integer_types(word_bits):
|
|
% signed = self_ty.is_signed
|
|
% Self = self_ty.stdlib_name
|
|
|
|
extension ${Self} {
|
|
/// 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 = ${Self}("123")
|
|
/// // x == 123
|
|
///
|
|
% if signed:
|
|
/// let y = ${Self}("-123", radix: 8)
|
|
/// // y == -83
|
|
% else:
|
|
/// let y = ${Self}("+123", radix: 8)
|
|
/// // y == +83
|
|
% end
|
|
///
|
|
/// let z = ${Self}("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`:
|
|
///
|
|
/// ${Self}(" 100") // Includes whitespace
|
|
/// ${Self}("21-50") // Invalid format
|
|
/// ${Self}("ff6600") // Characters out of bounds
|
|
/// ${Self}("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?(_ text: String, radix: Int = 10) {
|
|
if let value = _parseAsciiAs${'' if signed else 'U'}Int${int_max_bits}(
|
|
text.utf16, radix, ${'' if signed else 'U'}Int${int_max_bits}(${Self}.max)) {
|
|
self.init(
|
|
${'' if Self in ('Int64', 'UInt64') else 'extendingOrTruncating:'} value)
|
|
}
|
|
else {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
% end
|