diff --git a/stdlib/public/core/Integers.swift b/stdlib/public/core/Integers.swift index 89a7ef2e5bdb8..737c5299bb3e2 100644 --- a/stdlib/public/core/Integers.swift +++ b/stdlib/public/core/Integers.swift @@ -1377,64 +1377,341 @@ 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) +/// This internal function does not validate `radix` or the size of `buffer`. +/// It is an unsafe operation. +/// +/// The behavior is undefined if `radix` is not between `2` and `36` (inclusive) +/// or if there are insufficient bytes in `buffer` for the output. +@_specialize(where T == UInt64) +@_specialize(where T == Int64) +@unsafe +internal func _BinaryIntegerToASCII( + negative: Bool, + magnitude: T.Magnitude, + radix: T, + uppercase: Bool, + buffer utf8Buffer: inout MutableSpan +) -> Range { + var value = magnitude + let radix = radix.magnitude + + // We need a `MutableRawSpan` to use wide store/load operations. + var buffer = utf8Buffer.mutableBytes + var offset = buffer.byteCount + + if value == (0 as T.Magnitude) { + unsafe buffer.storeBytes( + of: 0x30 /* "0" */, + toUncheckedByteOffset: 0, + as: UInt8.self) + // Unlike the C++ implementation, we'll never return "-0". + return 0..<1 + } + + if radix == (10 as T.Magnitude) { + // Look up two digits at once. + let lookup: _InlineArray<100, (UInt8, UInt8)> = [ + (0x30, 0x30), (0x30, 0x31), (0x30, 0x32), (0x30, 0x33), (0x30, 0x34), + (0x30, 0x35), (0x30, 0x36), (0x30, 0x37), (0x30, 0x38), (0x30, 0x39), + (0x31, 0x30), (0x31, 0x31), (0x31, 0x32), (0x31, 0x33), (0x31, 0x34), + (0x31, 0x35), (0x31, 0x36), (0x31, 0x37), (0x31, 0x38), (0x31, 0x39), + (0x32, 0x30), (0x32, 0x31), (0x32, 0x32), (0x32, 0x33), (0x32, 0x34), + (0x32, 0x35), (0x32, 0x36), (0x32, 0x37), (0x32, 0x38), (0x32, 0x39), + (0x33, 0x30), (0x33, 0x31), (0x33, 0x32), (0x33, 0x33), (0x33, 0x34), + (0x33, 0x35), (0x33, 0x36), (0x33, 0x37), (0x33, 0x38), (0x33, 0x39), + (0x34, 0x30), (0x34, 0x31), (0x34, 0x32), (0x34, 0x33), (0x34, 0x34), + (0x34, 0x35), (0x34, 0x36), (0x34, 0x37), (0x34, 0x38), (0x34, 0x39), + (0x35, 0x30), (0x35, 0x31), (0x35, 0x32), (0x35, 0x33), (0x35, 0x34), + (0x35, 0x35), (0x35, 0x36), (0x35, 0x37), (0x35, 0x38), (0x35, 0x39), + (0x36, 0x30), (0x36, 0x31), (0x36, 0x32), (0x36, 0x33), (0x36, 0x34), + (0x36, 0x35), (0x36, 0x36), (0x36, 0x37), (0x36, 0x38), (0x36, 0x39), + (0x37, 0x30), (0x37, 0x31), (0x37, 0x32), (0x37, 0x33), (0x37, 0x34), + (0x37, 0x35), (0x37, 0x36), (0x37, 0x37), (0x37, 0x38), (0x37, 0x39), + (0x38, 0x30), (0x38, 0x31), (0x38, 0x32), (0x38, 0x33), (0x38, 0x34), + (0x38, 0x35), (0x38, 0x36), (0x38, 0x37), (0x38, 0x38), (0x38, 0x39), + (0x39, 0x30), (0x39, 0x31), (0x39, 0x32), (0x39, 0x33), (0x39, 0x34), + (0x39, 0x35), (0x39, 0x36), (0x39, 0x37), (0x39, 0x38), (0x39, 0x39) + ] + while value >= (10 as T.Magnitude) { + offset &-= 2 + unsafe buffer.storeBytes( + of: lookup[unchecked: Int(truncatingIfNeeded: value % 100)], + toUncheckedByteOffset: offset, + as: (UInt8, UInt8).self) + value /= 100 + } + if value != (0 as T.Magnitude) { + offset &-= 1 + unsafe buffer.storeBytes( + of: UInt8(truncatingIfNeeded: value) | 0x30, + toUncheckedByteOffset: offset, + as: UInt8.self) + } + } else if radix == (16 as T.Magnitude) { + let lookup: _InlineArray<16, UInt8> = [ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46 + ] + let adjustment: UInt8 = uppercase ? 0 : 0x20 + + while value != (0 as T.Magnitude) { + offset &-= 1 + unsafe buffer.storeBytes( + of: lookup[unchecked: Int(truncatingIfNeeded: value & 0xf)] | adjustment, + toUncheckedByteOffset: offset, + as: UInt8.self) + value >>= 4 + } + } else if radix == (8 as T.Magnitude) { + while value != (0 as T.Magnitude) { + offset &-= 1 + unsafe buffer.storeBytes( + of: UInt8(truncatingIfNeeded: value & 0x7) | 0x30, + toUncheckedByteOffset: offset, + as: UInt8.self) + value >>= 3 + } + } else if radix == (2 as T.Magnitude) { + while value != (0 as T.Magnitude) { + offset &-= 1 + unsafe buffer.storeBytes( + of: UInt8(truncatingIfNeeded: value & 1) | 0x30, + toUncheckedByteOffset: offset, + as: UInt8.self) + value >>= 1 } + } else { + let lookup: _InlineArray<36, UInt8> = [ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, + 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a + ] + let adjustment: UInt8 = uppercase ? 0 : 0x20 + + while value != (0 as T.Magnitude) { + offset &-= 1 + unsafe buffer.storeBytes( + of: lookup[Int(truncatingIfNeeded: value % radix)] | adjustment, + toUncheckedByteOffset: offset, + as: UInt8.self) + value /= radix + } + } - if self == (0 as Self) { return "0" } + if negative { + offset &-= 1 + unsafe buffer.storeBytes( + of: 0x2d /* "-" */, + toUncheckedByteOffset: offset, + as: UInt8.self) + } - // 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_) - } + return offset.. 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 - } +// Support legacy ABI on top of new implementation: +// ================================================ + +// 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 +// to any integer type without checks, however. +@_silgen_name("swift_int64ToString") +@usableFromInline +internal func _int64ToStringImpl( + _ textBuffer: UnsafeMutablePointer, + _ bufferLength: UInt, + _ value: Int64, + _ radix: Int64, + _ uppercase: Bool +) -> UInt64 { + _precondition(radix >= 2 && radix <= 36, "Radix must be between 2 and 36") + _precondition(bufferLength >= (radix >= 10 ? 21 : 65), "Insufficient buffer size") + unsafe textBuffer.initialize(repeating: 0x30, count: Int(bufferLength)) + + var buffer = unsafe MutableSpan( + _unchecked: textBuffer, + count: Int(bufferLength)) + let textRange = unsafe _BinaryIntegerToASCII( + negative: value < 0, + magnitude: value.magnitude, + radix: radix, + uppercase: uppercase, + buffer: &buffer) + _ = consume buffer + let byteCount = textRange.upperBound &- textRange.lowerBound + + // Move text to start of buffer. + if textRange.lowerBound != 0 { + unsafe _memmove( + dest: textBuffer, + src: textBuffer + textRange.lowerBound, + size: UInt(truncatingIfNeeded: byteCount)) + } + return UInt64(truncatingIfNeeded: byteCount) +} - let isNegative = Self.isSigned && self < (0 as Self) - var value = magnitude +// 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 +// to any integer type without checks, however. +@_silgen_name("swift_uint64ToString") +@usableFromInline +internal func _uint64ToStringImpl( + _ textBuffer: UnsafeMutablePointer, + _ bufferLength: UInt, + _ value: UInt64, + _ radix: Int64, + _ uppercase: Bool +) -> UInt64 { + _precondition(radix >= 2 && radix <= 36, "Radix must be between 2 and 36") + _precondition(bufferLength >= (radix >= 10 ? 20 : 64), "Insufficient buffer size") + unsafe textBuffer.initialize(repeating: 0x30, count: Int(bufferLength)) + + var buffer = unsafe MutableSpan( + _unchecked: textBuffer, + count: Int(bufferLength)) + let textRange = unsafe _BinaryIntegerToASCII( + negative: false, + magnitude: value, + radix: radix, + uppercase: uppercase, + buffer: &buffer) + _ = consume buffer + let byteCount = textRange.upperBound &- textRange.lowerBound + + // Move text to start of buffer. + if textRange.lowerBound != 0 { + unsafe _memmove( + dest: textBuffer, + src: textBuffer + textRange.lowerBound, + size: UInt(truncatingIfNeeded: byteCount)) + } + return UInt64(truncatingIfNeeded: byteCount) +} - // TODO(FIXME JIRA): All current stdlib types fit in small. Use a stack - // buffer instead of an array on the heap. +public // @testable +func _uint64ToString( + _ value: UInt64, + radix: Int64 = 10, + uppercase: Bool = false +) -> String { + _precondition(radix >= 2 && radix <= 36, "Radix must be between 2 and 36") + + if radix >= 10 { + var buffer = _InlineArray<20, UTF8.CodeUnit>(repeating: 0x30) + var span = buffer.mutableSpan + let textRange = unsafe _BinaryIntegerToASCII( + negative: false, + magnitude: value, + radix: radix, + uppercase: uppercase, + buffer: &span) + let textStart = + unsafe span._start().assumingMemoryBound(to: UTF8.CodeUnit.self) + + textRange.lowerBound + let byteCount = textRange.upperBound &- textRange.lowerBound + let textBuffer = + unsafe UnsafeBufferPointer( + _uncheckedStart: textStart, count: byteCount) + return unsafe String._fromASCII(textBuffer) + } + + var buffer = _InlineArray<64, UTF8.CodeUnit>(repeating: 0x30) + var span = buffer.mutableSpan + let textRange = unsafe _BinaryIntegerToASCII( + negative: false, + magnitude: value, + radix: radix, + uppercase: false, // When radix < 10, case is irrelevant. + buffer: &span) + let textStart = + unsafe span._start().assumingMemoryBound(to: UTF8.CodeUnit.self) + + textRange.lowerBound + let byteCount = textRange.upperBound &- textRange.lowerBound + let textBuffer = + unsafe UnsafeBufferPointer( + _uncheckedStart: textStart, count: byteCount) + return unsafe String._fromASCII(textBuffer) +} - var result: [UInt8] = [] - while value != 0 { - let (quotient, remainder) = _quotientAndRemainder(value) - result.append(_ascii(UInt8(truncatingIfNeeded: remainder))) - value = quotient - } +extension BinaryInteger { + internal func _description(radix: Int, uppercase: Bool) -> String { + _precondition(radix >= 2 && radix <= 36, "Radix must be between 2 and 36") + + if _fastPath(bitWidth <= 64 || magnitude < UInt64.max) { + if radix >= 10 { + var buffer = _InlineArray<21, UTF8.CodeUnit>(repeating: 0x30) + var span = buffer.mutableSpan + let textRange = unsafe _BinaryIntegerToASCII( + negative: Self.isSigned && self < 0, + magnitude: UInt64(truncatingIfNeeded: magnitude), + radix: UInt64(truncatingIfNeeded: radix), + uppercase: uppercase, + buffer: &span) + let textStart = + unsafe span._start().assumingMemoryBound(to: UTF8.CodeUnit.self) + + textRange.lowerBound + let byteCount = textRange.upperBound &- textRange.lowerBound + let textBuffer = + unsafe UnsafeBufferPointer( + _uncheckedStart: textStart, count: byteCount) + return unsafe String._fromASCII(textBuffer) + } - if isNegative { - result.append(UInt8(("-" as Unicode.Scalar).value)) + var buffer = _InlineArray<65, UTF8.CodeUnit>(repeating: 0x30) + var span = buffer.mutableSpan + let textRange = unsafe _BinaryIntegerToASCII( + negative: Self.isSigned && self < 0, + magnitude: UInt64(truncatingIfNeeded: magnitude), + radix: UInt64(truncatingIfNeeded: radix), + uppercase: false, // When radix < 10, case is irrelevant. + buffer: &span) + let textStart = + unsafe span._start().assumingMemoryBound(to: UTF8.CodeUnit.self) + + textRange.lowerBound + let byteCount = textRange.upperBound &- textRange.lowerBound + let textBuffer = + unsafe UnsafeBufferPointer( + _uncheckedStart: textStart, count: byteCount) + return unsafe String._fromASCII(textBuffer) } - result.reverse() - return unsafe result.withUnsafeBufferPointer { - return unsafe String._fromASCII($0) + // The decimal representation of an unsigned value of bit width `i` requires + // `ceil(log2(10) * i)` bytes. Here, we use 5/16 as a known overestimate, + // with the division computed using a bit shift. Since integer division or + // bit shift is a truncating (flooring) operation, we add 15 to adjust for + // off-by-one results when bit width isn't a multiple of 16. Finally, we add + // 1 to leave room for the '-' sign or, in the case of zero, '0'. + let capacity = radix >= 10 ? (bitWidth * 5 + 15) &>> 4 + 1 : bitWidth + 1 + return unsafe withUnsafeTemporaryAllocation( + of: UTF8.CodeUnit.self, + capacity: capacity + ) { + // It's our responsibility to initialize and deinitialize memory. + // A larger buffer pointer than requested may be allocated, but it's + // undefined behavior to access the excess allocation. + let buffer = + unsafe UnsafeMutableBufferPointer( + start: $0.baseAddress, count: capacity) + unsafe buffer.initialize(repeating: 0x30) + defer { unsafe buffer.deinitialize() } + + var span = unsafe buffer.mutableSpan + let textRange = unsafe _BinaryIntegerToASCII( + negative: Self.isSigned && self < 0, + magnitude: magnitude, + radix: Magnitude(truncatingIfNeeded: radix), + uppercase: uppercase, + buffer: &span) + _ = consume span + let textStart = unsafe buffer.baseAddress! + textRange.lowerBound + let byteCount = textRange.upperBound &- textRange.lowerBound + let textBuffer = + unsafe UnsafeBufferPointer( + _uncheckedStart: textStart, count: byteCount) + return unsafe String._fromASCII(textBuffer) } } diff --git a/stdlib/public/core/Runtime.swift b/stdlib/public/core/Runtime.swift index e92160f9eac79..d311e7a7b4e4d 100644 --- a/stdlib/public/core/Runtime.swift +++ b/stdlib/public/core/Runtime.swift @@ -207,237 +207,6 @@ public func _stdlib_atomicAcquiringLoadARCRef( // Conversion of primitive types to `String` //===----------------------------------------------------------------------===// -/// A 32 byte buffer. -internal struct _Buffer32 { - 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 init() {} - - internal mutating func withBytes( - _ body: (UnsafeMutablePointer) throws -> Result - ) rethrows -> Result { - return try unsafe withUnsafeMutablePointer(to: &self) { - try unsafe body(UnsafeMutableRawPointer($0).assumingMemoryBound(to: UInt8.self)) - } - } -} - -/// A 72 byte buffer. -internal struct _Buffer72 { - 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 init() {} - - internal mutating func withBytes( - _ body: (UnsafeMutablePointer) throws -> Result - ) rethrows -> Result { - return try unsafe withUnsafeMutablePointer(to: &self) { - try unsafe body(UnsafeMutableRawPointer($0).assumingMemoryBound(to: UInt8.self)) - } - } -} - -#if !$Embedded -// 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 -// to any integer type without checks, however. -@_silgen_name("swift_int64ToString") -internal func _int64ToStringImpl( - _ buffer: UnsafeMutablePointer, - _ bufferLength: UInt, - _ value: Int64, - _ radix: Int64, - _ uppercase: Bool -) -> UInt64 -#else -internal func _int64ToStringImpl( - _ buffer: UnsafeMutablePointer, - _ bufferLength: UInt, - _ value: Int64, - _ radix: Int64, - _ uppercase: Bool -) -> UInt64 { - return UInt64(unsafe value._toStringImpl(buffer, bufferLength, Int(radix), uppercase)) -} -#endif - -internal func _int64ToString( - _ value: Int64, - radix: Int64 = 10, - uppercase: Bool = false -) -> String { - if radix >= 10 { - var buffer = _Buffer32() - return unsafe buffer.withBytes { (bufferPtr) in - let actualLength = unsafe _int64ToStringImpl(bufferPtr, 32, value, radix, uppercase) - return unsafe String._fromASCII(UnsafeBufferPointer( - start: bufferPtr, count: Int(truncatingIfNeeded: actualLength) - )) - } - } else { - var buffer = _Buffer72() - return unsafe buffer.withBytes { (bufferPtr) in - let actualLength = unsafe _int64ToStringImpl(bufferPtr, 72, value, radix, uppercase) - return unsafe String._fromASCII(UnsafeBufferPointer( - start: bufferPtr, count: Int(truncatingIfNeeded: actualLength) - )) - } - } -} - -#if !$Embedded -// 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 -// to any integer type without checks, however. -@_silgen_name("swift_uint64ToString") -internal func _uint64ToStringImpl( - _ buffer: UnsafeMutablePointer, - _ bufferLength: UInt, - _ value: UInt64, - _ radix: Int64, - _ uppercase: Bool -) -> UInt64 -#else -internal func _uint64ToStringImpl( - _ buffer: UnsafeMutablePointer, - _ bufferLength: UInt, - _ value: UInt64, - _ radix: Int64, - _ uppercase: Bool -) -> UInt64 { - return unsafe UInt64(value._toStringImpl(buffer, bufferLength, Int(radix), uppercase)) -} -#endif - -public // @testable -func _uint64ToString( - _ value: UInt64, - radix: Int64 = 10, - uppercase: Bool = false -) -> String { - if radix >= 10 { - var buffer = _Buffer32() - return unsafe buffer.withBytes { (bufferPtr) in - let actualLength = unsafe _uint64ToStringImpl(bufferPtr, 32, value, radix, uppercase) - return unsafe String._fromASCII(UnsafeBufferPointer( - start: bufferPtr, count: Int(truncatingIfNeeded: actualLength) - )) - } - } else { - var buffer = _Buffer72() - return unsafe buffer.withBytes { (bufferPtr) in - let actualLength = unsafe _uint64ToStringImpl(bufferPtr, 72, value, radix, uppercase) - return unsafe String._fromASCII(UnsafeBufferPointer( - start: bufferPtr, count: Int(truncatingIfNeeded: actualLength) - )) - } - } -} - @inlinable internal func _rawPointerToString(_ value: Builtin.RawPointer) -> String { var result = _uint64ToString( diff --git a/stdlib/public/stubs/Stubs.cpp b/stdlib/public/stubs/Stubs.cpp index 19c116472b8fc..d156567dc80fc 100644 --- a/stdlib/public/stubs/Stubs.cpp +++ b/stdlib/public/stubs/Stubs.cpp @@ -73,71 +73,6 @@ #include "llvm/ADT/StringExtras.h" -static uint64_t uint64ToStringImpl(char *Buffer, uint64_t Value, - int64_t Radix, bool Uppercase, - bool Negative) { - char *P = Buffer; - uint64_t Y = Value; - - if (Y == 0) { - *P++ = '0'; - } else if (Radix == 10) { - while (Y) { - *P++ = '0' + char(Y % 10); - Y /= 10; - } - } else { - unsigned Radix32 = Radix; - while (Y) { - *P++ = llvm::hexdigit(Y % Radix32, !Uppercase); - Y /= Radix32; - } - } - - if (Negative) - *P++ = '-'; - std::reverse(Buffer, P); - return size_t(P - Buffer); -} - -SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API -uint64_t swift_int64ToString(char *Buffer, size_t BufferLength, - int64_t Value, int64_t Radix, - bool Uppercase) { - if ((Radix >= 10 && BufferLength < 32) || (Radix < 10 && BufferLength < 65)) - swift::crash("swift_int64ToString: insufficient buffer size"); - - if (Radix == 0 || Radix > 36) - swift::crash("swift_int64ToString: invalid radix for string conversion"); - - bool Negative = Value < 0; - - // Compute an absolute value safely, without using unary negation on INT_MIN, - // which is undefined behavior. - uint64_t UnsignedValue = Value; - if (Negative) { - // Assumes two's complement representation. - UnsignedValue = ~UnsignedValue + 1; - } - - return uint64ToStringImpl(Buffer, UnsignedValue, Radix, Uppercase, - Negative); -} - -SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API -uint64_t swift_uint64ToString(char *Buffer, intptr_t BufferLength, - uint64_t Value, int64_t Radix, - bool Uppercase) { - if ((Radix >= 10 && BufferLength < 32) || (Radix < 10 && BufferLength < 64)) - swift::crash("swift_int64ToString: insufficient buffer size"); - - if (Radix == 0 || Radix > 36) - swift::crash("swift_int64ToString: invalid radix for string conversion"); - - return uint64ToStringImpl(Buffer, Value, Radix, Uppercase, - /*Negative=*/false); -} - #if SWIFT_STDLIB_HAS_LOCALE #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__ANDROID__) static inline locale_t getCLocale() { diff --git a/test/api-digester/stability-stdlib-abi-without-asserts.test b/test/api-digester/stability-stdlib-abi-without-asserts.test index 66ac5b441539b..746a95b2fe3bd 100644 --- a/test/api-digester/stability-stdlib-abi-without-asserts.test +++ b/test/api-digester/stability-stdlib-abi-without-asserts.test @@ -874,4 +874,9 @@ Func type(of:) has been removed Func _float32ToStringImpl(_:_:_:_:) is a new API without '@available' Func _float64ToStringImpl(_:_:_:_:) is a new API without '@available' +// Integer-to-String conversions were reimplemented in Swift [SR-9438] +// The legacy C entry points now look like newly introduced Swift functions +Func _int64ToStringImpl(_:_:_:_:_:) is a new API without '@available' +Func _uint64ToStringImpl(_:_:_:_:_:) is a new API without '@available' + // *** DO NOT DISABLE OR XFAIL THIS TEST. *** (See comment above.)