Files
swift-mirror/stdlib/public/core/IntegerParsing.swift.gyb

146 lines
5.0 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 http://swift.org/LICENSE.txt for license information
// See http://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 <= numericCast(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(c - digit.lowerBound)
case lower: n = UInt64(c - lower.lowerBound) + 10
case upper: n = UInt64(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} {
/// Construct from an ASCII representation in the given `radix`.
///
/// If `text` does not match the regular expression
/// "[+-]?[0-9a-zA-Z]+", or the value it denotes in the given `radix`
/// is not representable, the result is `nil`.
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 'truncatingBitPattern:'} value)
}
else {
return nil
}
}
}
% end