//===----------------------------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// %{ 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(extendingOrTruncating: 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.multipliedReportingOverflow(by: uRadix) let (result2, overflow2) = result1.addingReportingOverflow(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 as Int64), "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