diff --git a/stdlib/public/core/Integers.swift b/stdlib/public/core/Integers.swift index e9cf86c526851..75fe4e29e4d21 100644 --- a/stdlib/public/core/Integers.swift +++ b/stdlib/public/core/Integers.swift @@ -1495,69 +1495,191 @@ extension BinaryInteger { //===--- CustomStringConvertible conformance ------------------------------===// //===----------------------------------------------------------------------===// -extension BinaryInteger { - internal func _description(radix: Int, uppercase: Bool) -> String { - _precondition(2...36 ~= radix, "Radix must be between 2 and 36") - - if bitWidth <= 64 { - let radix_ = Int64(radix) - return Self.isSigned - ? _int64ToString( - Int64(truncatingIfNeeded: self), radix: radix_, uppercase: uppercase) - : _uint64ToString( - UInt64(truncatingIfNeeded: self), radix: radix_, uppercase: uppercase) - } +@_alwaysEmitIntoClient +@inline(__always) +internal func _overestimatedBufferCapacity( + _ value: T, radix: Int +) -> Int { + assert(radix >= 2 && radix <= 36) + let bitWidth = value.bitWidth + // We have to *overestimate* the buffer capacity required, and we'll do so by + // finding the nearest power-of-two base that's equal to or less than `radix`. + // + // This function could be optimized by looking to the *value* rather than the + // value's bit width, but consider that this may involve multiple + // generic heterogeneous comparisons. + let divisor = Int.bitWidth &- radix.leadingZeroBitCount &- 1 + let result = (bitWidth &- 1) / divisor &+ 1 + return T.isSigned ? result &+ 1 : result +} - if self == (0 as Self) { return "0" } +@_alwaysEmitIntoClient +@inline(__always) +internal func _moveInitializedBytesFromEnd( + _ buffer: UnsafeMutableBufferPointer, + initializedCount: Int +) { + let count = buffer.count + let unusedCapacity = count &- initializedCount + guard unusedCapacity > 0 else { return } + // The last `initializedCount` bytes of the buffer have been initialized, but + // we need the first `initializedCount` bytes of the buffer to be initialized + // instead, so we move the initialized bytes if necessary. + _internalInvariant(buffer.baseAddress != nil) + let ptr1 = buffer.baseAddress! + ptr1.moveInitialize(from: ptr1 + unusedCapacity, count: initializedCount) + // Work around a `_SmallString` bug. + let ptr2 = ptr1 + initializedCount + ptr2.initialize(repeating: 0, count: unusedCapacity) + ptr2.deinitialize(count: unusedCapacity) +} - // Bit shifting can be faster than division when `radix` is a power of two - // (although not necessarily the case for builtin types). - let isRadixPowerOfTwo = radix.nonzeroBitCount == 1 - let radix_ = Magnitude(radix) - func _quotientAndRemainder(_ value: Magnitude) -> (Magnitude, Magnitude) { - return isRadixPowerOfTwo - ? (value >> radix.trailingZeroBitCount, value & (radix_ - 1)) - : value.quotientAndRemainder(dividingBy: radix_) - } +@_alwaysEmitIntoClient +@inline(__always) +internal func _convertInteger( + _ buffer: UnsafeMutableBufferPointer, _ value: T, + radix: Int, uppercase: Bool +) -> /* initializedCount: */ Int { + var initializedCount = _convertUnsignedInteger( + buffer, value.magnitude, radix: radix, uppercase: uppercase) + if T.isSigned && value < (0 as T) { + initializedCount += 1 + buffer[buffer.count &- initializedCount] = 45 /* "-" */ + } + return initializedCount +} - let hasLetters = radix > 10 - func _ascii(_ digit: UInt8) -> UInt8 { - let base: UInt8 - if !hasLetters || digit < 10 { - base = UInt8(("0" as Unicode.Scalar).value) - } else if uppercase { - base = UInt8(("A" as Unicode.Scalar).value) &- 10 - } else { - base = UInt8(("a" as Unicode.Scalar).value) &- 10 - } - return base &+ digit - } +@_alwaysEmitIntoClient +@inline(__always) +internal func _convertUnsignedInteger( + _ buffer: UnsafeMutableBufferPointer, _ value: T, + radix: Int, uppercase: Bool +) -> /* initializedCount: */ Int { + if value == (0 as T) { + buffer[buffer.count &- 1] = 48 /* "0" */ + return 1 + } + if value.bitWidth <= 64 { + return _convertNonzeroUInt64( + buffer, UInt64(truncatingIfNeeded: value), + radix: radix, uppercase: uppercase) + } + return _convertNonzeroUnsignedInteger( + buffer, value, radix: radix, uppercase: uppercase) +} - let isNegative = Self.isSigned && self < (0 as Self) - var value = magnitude +@_alwaysEmitIntoClient +@inline(__always) +internal func _convertNonzeroUInt64( + _ buffer: UnsafeMutableBufferPointer, _ value: UInt64, + radix: Int, uppercase: Bool +) -> /* initializedCount: */ Int { + switch radix { + case 10: + return _convertNonzeroUInt64ToDecimal(buffer, value) + case 16: + return _convertNonzeroUInt64ToHexadecimal( + buffer, value, uppercase: uppercase) + default: + return _convertNonzeroUnsignedInteger( + buffer, value, radix: radix, uppercase: uppercase) + } +} - // TODO(FIXME JIRA): All current stdlib types fit in small. Use a stack - // buffer instead of an array on the heap. +@_alwaysEmitIntoClient +@inline(__always) +internal func _convertNonzeroUInt64ToDecimal( + _ buffer: UnsafeMutableBufferPointer, _ value: UInt64 +) -> /* initializedCount: */ Int { + var value = value + var i = buffer.count + while value > 0 { + i -= 1 + let digit: UInt64 + (value, digit) = value.quotientAndRemainder(dividingBy: 10) + buffer[i] = 48 /* "0" */ &+ UInt8(truncatingIfNeeded: digit) + } + return buffer.count &- i +} - var result: [UInt8] = [] - while value != 0 { - let (quotient, remainder) = _quotientAndRemainder(value) - result.append(_ascii(UInt8(truncatingIfNeeded: remainder))) - value = quotient +@_alwaysEmitIntoClient +@inline(__always) +internal func _convertNonzeroUInt64ToHexadecimal( + _ buffer: UnsafeMutableBufferPointer, _ value: UInt64, + uppercase: Bool +) -> /* initializedCount: */ Int { + var value = value + var i = buffer.count + if uppercase { + while value > 0 { + i -= 1 + let digit = value & 15 + value &>>= 4 + buffer[i] = digit < 10 + ? 48 /* "0" */ &+ UInt8(truncatingIfNeeded: digit) + : 65 /* "A" */ &+ (UInt8(truncatingIfNeeded: digit) &- 10) } + } else { + while value > 0 { + i -= 1 + let digit = value & 15 + value &>>= 4 + buffer[i] = digit < 10 + ? 48 /* "0" */ &+ UInt8(truncatingIfNeeded: digit) + : 97 /* "a" */ &+ (UInt8(truncatingIfNeeded: digit) &- 10) + } + } + return buffer.count &- i +} - if isNegative { - result.append(UInt8(("-" as Unicode.Scalar).value)) +@_alwaysEmitIntoClient +@_specialize(where T == UInt64) +internal func _convertNonzeroUnsignedInteger( + _ buffer: UnsafeMutableBufferPointer, _ value: T, + radix: Int, uppercase: Bool +) -> /* initializedCount: */ Int { + _internalInvariant(radix >= 2 && radix <= 36) + let radix = T(radix) + var value = value + var i = buffer.count + while value > (0 as T) { + i -= 1 + let digit: T + (value, digit) = value.quotientAndRemainder(dividingBy: radix) + if digit < 10 { + buffer[i] = 48 /* "0" */ &+ UInt8(truncatingIfNeeded: digit) + } else if uppercase { + buffer[i] = 65 /* "A" */ &+ (UInt8(truncatingIfNeeded: digit) &- 10) + } else { + buffer[i] = 97 /* "a" */ &+ (UInt8(truncatingIfNeeded: digit) &- 10) } + } + return buffer.count &- i +} - result.reverse() - return result.withUnsafeBufferPointer { - return String._fromASCII($0) +extension BinaryInteger { + @_alwaysEmitIntoClient + @inline(__always) + internal func _description(radix: Int, uppercase: Bool) -> String { + _precondition(2...36 ~= radix, "Radix must be between 2 and 36") + let capacity = _overestimatedBufferCapacity(self, radix: radix) + if #available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) { + return String(unsafeUninitializedCapacity: capacity) { + let i = _convertInteger($0, self, radix: radix, uppercase: uppercase) + _moveInitializedBytesFromEnd($0, initializedCount: i) + return i + } } + let buffer = + UnsafeMutableBufferPointer.allocate(capacity: capacity) + defer { buffer.deallocate() } + let i = _convertInteger(buffer, self, radix: radix, uppercase: uppercase) + return String._fromASCII(UnsafeBufferPointer(rebasing: buffer.suffix(i))) } /// A textual representation of this value. @_semantics("binaryInteger.description") + @inlinable public var description: String { return _description(radix: 10, uppercase: false) } diff --git a/stdlib/public/core/MigrationSupport.swift b/stdlib/public/core/MigrationSupport.swift index 16124058138bc..386497a411dfe 100644 --- a/stdlib/public/core/MigrationSupport.swift +++ b/stdlib/public/core/MigrationSupport.swift @@ -408,7 +408,7 @@ extension UnsafeRawPointer: _CustomPlaygroundQuickLookable { bitPattern: Int64(Int(Builtin.ptrtoint_Word(_rawValue)))) return ptrValue == 0 ? "UnsafeRawPointer(nil)" - : "UnsafeRawPointer(0x\(_uint64ToString(ptrValue, radix:16, uppercase:true)))" + : "UnsafeRawPointer(0x\(ptrValue._description(radix: 16, uppercase: true)))" } @available(swift, deprecated: 4.2/*, obsoleted: 5.0*/, message: "UnsafeRawPointer.customPlaygroundQuickLook will be removed in a future Swift version") @@ -423,7 +423,7 @@ extension UnsafeMutableRawPointer: _CustomPlaygroundQuickLookable { bitPattern: Int64(Int(Builtin.ptrtoint_Word(_rawValue)))) return ptrValue == 0 ? "UnsafeMutableRawPointer(nil)" - : "UnsafeMutableRawPointer(0x\(_uint64ToString(ptrValue, radix:16, uppercase:true)))" + : "UnsafeMutableRawPointer(0x\(ptrValue._description(radix: 16, uppercase: true)))" } @available(swift, deprecated: 4.2/*, obsoleted: 5.0*/, message: "UnsafeMutableRawPointer.customPlaygroundQuickLook will be removed in a future Swift version") @@ -437,7 +437,7 @@ extension UnsafePointer: _CustomPlaygroundQuickLookable { let ptrValue = UInt64(bitPattern: Int64(Int(Builtin.ptrtoint_Word(_rawValue)))) return ptrValue == 0 ? "UnsafePointer(nil)" - : "UnsafePointer(0x\(_uint64ToString(ptrValue, radix:16, uppercase:true)))" + : "UnsafePointer(0x\(ptrValue._description(radix: 16, uppercase: true)))" } @available(swift, deprecated: 4.2/*, obsoleted: 5.0*/, message: "UnsafePointer.customPlaygroundQuickLook will be removed in a future Swift version") @@ -451,7 +451,7 @@ extension UnsafeMutablePointer: _CustomPlaygroundQuickLookable { let ptrValue = UInt64(bitPattern: Int64(Int(Builtin.ptrtoint_Word(_rawValue)))) return ptrValue == 0 ? "UnsafeMutablePointer(nil)" - : "UnsafeMutablePointer(0x\(_uint64ToString(ptrValue, radix:16, uppercase:true)))" + : "UnsafeMutablePointer(0x\(ptrValue._description(radix: 16, uppercase: true)))" } @available(swift, deprecated: 4.2/*, obsoleted: 5.0*/, message: "UnsafeMutablePointer.customPlaygroundQuickLook will be removed in a future Swift version") diff --git a/stdlib/public/core/Runtime.swift b/stdlib/public/core/Runtime.swift index 7df18bd78a232..b3810624457b7 100644 --- a/stdlib/public/core/Runtime.swift +++ b/stdlib/public/core/Runtime.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2021 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 @@ -207,92 +207,6 @@ internal struct _Buffer32 { } } -/// A 72 byte buffer. -internal struct _Buffer72 { - internal init() {} - - internal var _x0: UInt8 = 0 - internal var _x1: UInt8 = 0 - internal var _x2: UInt8 = 0 - internal var _x3: UInt8 = 0 - internal var _x4: UInt8 = 0 - internal var _x5: UInt8 = 0 - internal var _x6: UInt8 = 0 - internal var _x7: UInt8 = 0 - internal var _x8: UInt8 = 0 - internal var _x9: UInt8 = 0 - internal var _x10: UInt8 = 0 - internal var _x11: UInt8 = 0 - internal var _x12: UInt8 = 0 - internal var _x13: UInt8 = 0 - internal var _x14: UInt8 = 0 - internal var _x15: UInt8 = 0 - internal var _x16: UInt8 = 0 - internal var _x17: UInt8 = 0 - internal var _x18: UInt8 = 0 - internal var _x19: UInt8 = 0 - internal var _x20: UInt8 = 0 - internal var _x21: UInt8 = 0 - internal var _x22: UInt8 = 0 - internal var _x23: UInt8 = 0 - internal var _x24: UInt8 = 0 - internal var _x25: UInt8 = 0 - internal var _x26: UInt8 = 0 - internal var _x27: UInt8 = 0 - internal var _x28: UInt8 = 0 - internal var _x29: UInt8 = 0 - internal var _x30: UInt8 = 0 - internal var _x31: UInt8 = 0 - internal var _x32: UInt8 = 0 - internal var _x33: UInt8 = 0 - internal var _x34: UInt8 = 0 - internal var _x35: UInt8 = 0 - internal var _x36: UInt8 = 0 - internal var _x37: UInt8 = 0 - internal var _x38: UInt8 = 0 - internal var _x39: UInt8 = 0 - internal var _x40: UInt8 = 0 - internal var _x41: UInt8 = 0 - internal var _x42: UInt8 = 0 - internal var _x43: UInt8 = 0 - internal var _x44: UInt8 = 0 - internal var _x45: UInt8 = 0 - internal var _x46: UInt8 = 0 - internal var _x47: UInt8 = 0 - internal var _x48: UInt8 = 0 - internal var _x49: UInt8 = 0 - internal var _x50: UInt8 = 0 - internal var _x51: UInt8 = 0 - internal var _x52: UInt8 = 0 - internal var _x53: UInt8 = 0 - internal var _x54: UInt8 = 0 - internal var _x55: UInt8 = 0 - internal var _x56: UInt8 = 0 - internal var _x57: UInt8 = 0 - internal var _x58: UInt8 = 0 - internal var _x59: UInt8 = 0 - internal var _x60: UInt8 = 0 - internal var _x61: UInt8 = 0 - internal var _x62: UInt8 = 0 - internal var _x63: UInt8 = 0 - internal var _x64: UInt8 = 0 - internal var _x65: UInt8 = 0 - internal var _x66: UInt8 = 0 - internal var _x67: UInt8 = 0 - internal var _x68: UInt8 = 0 - internal var _x69: UInt8 = 0 - internal var _x70: UInt8 = 0 - internal var _x71: UInt8 = 0 - - internal mutating func withBytes( - _ body: (UnsafeMutablePointer) throws -> Result - ) rethrows -> Result { - return try withUnsafeMutablePointer(to: &self) { - try body(UnsafeMutableRawPointer($0).assumingMemoryBound(to: UInt8.self)) - } - } -} - #if !((os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64)) // Note that this takes a Float32 argument instead of Float16, because clang // doesn't have _Float16 on all platforms yet. @@ -366,9 +280,7 @@ internal func _float64ToString( return (buffer, length) } - #if !(os(Windows) || os(Android)) && (arch(i386) || arch(x86_64)) - // Returns a UInt64, but that value is the length of the string, so it's // guaranteed to fit into an Int. This is part of the ABI, so we can't // trivially change it to Int. Callers can safely convert the result @@ -394,6 +306,20 @@ internal func _float80ToString( } #endif +@inlinable +internal func _rawPointerToString(_ value: Builtin.RawPointer) -> String { + var result = String( + UInt64(UInt(bitPattern: UnsafeRawPointer(value))), + radix: 16, + uppercase: false) + let count = 2 * MemoryLayout.size - result.utf8.count + return "0x" + String(repeating: "0", count: count) + result +} + +//===----------------------------------------------------------------------===// +// Old entry points preserved for ABI compatibility. +//===----------------------------------------------------------------------===// + // Returns a UInt64, but that value is the length of the string, so it's // guaranteed to fit into an Int. This is part of the ABI, so we can't // trivially change it to Int. Callers can safely convert the result @@ -407,30 +333,6 @@ internal func _int64ToStringImpl( _ uppercase: Bool ) -> UInt64 -internal func _int64ToString( - _ value: Int64, - radix: Int64 = 10, - uppercase: Bool = false -) -> String { - if radix >= 10 { - var buffer = _Buffer32() - return buffer.withBytes { (bufferPtr) in - let actualLength = _int64ToStringImpl(bufferPtr, 32, value, radix, uppercase) - return String._fromASCII(UnsafeBufferPointer( - start: bufferPtr, count: Int(truncatingIfNeeded: actualLength) - )) - } - } else { - var buffer = _Buffer72() - return buffer.withBytes { (bufferPtr) in - let actualLength = _int64ToStringImpl(bufferPtr, 72, value, radix, uppercase) - return String._fromASCII(UnsafeBufferPointer( - start: bufferPtr, count: Int(truncatingIfNeeded: actualLength) - )) - } - } -} - // Returns a UInt64, but that value is the length of the string, so it's // guaranteed to fit into an Int. This is part of the ABI, so we can't // trivially change it to Int. Callers can safely convert the result @@ -444,44 +346,21 @@ internal func _uint64ToStringImpl( _ uppercase: Bool ) -> UInt64 +@available(*, deprecated, message: "Use 'String(_:radix:uppercase:)' instead") public // @testable func _uint64ToString( - _ value: UInt64, - radix: Int64 = 10, - uppercase: Bool = false + _ value: UInt64, + radix: Int64 = 10, + uppercase: Bool = false ) -> String { - if radix >= 10 { - var buffer = _Buffer32() - return buffer.withBytes { (bufferPtr) in - let actualLength = _uint64ToStringImpl(bufferPtr, 32, value, radix, uppercase) - return String._fromASCII(UnsafeBufferPointer( - start: bufferPtr, count: Int(truncatingIfNeeded: actualLength) - )) - } - } else { - var buffer = _Buffer72() - return buffer.withBytes { (bufferPtr) in - let actualLength = _uint64ToStringImpl(bufferPtr, 72, value, radix, uppercase) - return String._fromASCII(UnsafeBufferPointer( - start: bufferPtr, count: Int(truncatingIfNeeded: actualLength) - )) - } - } + return value._description( + radix: Int(truncatingIfNeeded: radix), + uppercase: uppercase) } -@inlinable -internal func _rawPointerToString(_ value: Builtin.RawPointer) -> String { - var result = _uint64ToString( - UInt64( - UInt(bitPattern: UnsafeRawPointer(value))), - radix: 16, - uppercase: false - ) - for _ in 0..<(2 * MemoryLayout.size - result.utf16.count) { - result = "0" + result - } - return "0x" + result -} +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #if _runtime(_ObjC) // At runtime, these classes are derived from `__SwiftNativeNSXXXBase`, diff --git a/test/stdlib/Runtime.swift.gyb b/test/stdlib/Runtime.swift.gyb index b57137c622cf3..5ecb83a227f35 100644 --- a/test/stdlib/Runtime.swift.gyb +++ b/test/stdlib/Runtime.swift.gyb @@ -119,10 +119,10 @@ struct Struct3ConformsToP2 : CustomStringConvertible, Q1 { // Don't rely on string interpolation, it uses the casts that we are trying // to test. var result = "" - result += _uint64ToString(a) + " " - result += _uint64ToString(b) + " " - result += _uint64ToString(c) + " " - result += _uint64ToString(d) + result += a.description + " " + result += b.description + " " + result += c.description + " " + result += d.description return result } } @@ -143,10 +143,10 @@ struct Struct4ConformsToP2 : CustomStringConvertibl // Don't rely on string interpolation, it uses the casts that we are trying // to test. var result = value.description + " " - result += _uint64ToString(e) + " " - result += _uint64ToString(f) + " " - result += _uint64ToString(g) + " " - result += _uint64ToString(h) + result += e.description + " " + result += f.description + " " + result += g.description + " " + result += h.description return result } }