Skip to content

Commit 22da6dd

Browse files
authored
Merge pull request #14401 from xwu/itoa
[WIP] [stdlib] Consolidate integer-to-string implementations
2 parents 50637d1 + 76e2336 commit 22da6dd

File tree

2 files changed

+79
-280
lines changed

2 files changed

+79
-280
lines changed

stdlib/public/core/Integers.swift.gyb

Lines changed: 77 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1678,7 +1678,82 @@ ${operatorComment(x.nonMaskingOperator, False)}
16781678

16791679
}
16801680

1681-
// Strideable conformance
1681+
1682+
//===----------------------------------------------------------------------===//
1683+
//===--- CustomStringConvertible conformance ------------------------------===//
1684+
//===----------------------------------------------------------------------===//
1685+
1686+
extension BinaryInteger {
1687+
@_inlineable // FIXME(sil-serialize-all)
1688+
@_versioned
1689+
@_transparent
1690+
internal func _description(
1691+
radix: Int = 10, uppercase: Bool = false
1692+
) -> String {
1693+
_precondition(2...36 ~= radix, "Radix must be between 2 and 36")
1694+
1695+
if bitWidth <= 64 {
1696+
let radix_ = Int64(radix)
1697+
return Self.isSigned
1698+
? _int64ToString(
1699+
Int64(truncatingIfNeeded: self), radix: radix_, uppercase: uppercase)
1700+
: _uint64ToString(
1701+
UInt64(truncatingIfNeeded: self), radix: radix_, uppercase: uppercase)
1702+
}
1703+
1704+
if self == (0 as Self) { return "0" }
1705+
1706+
// Bit shifting can be faster than division when `radix` is a power of two
1707+
// (although not necessarily the case for builtin types).
1708+
let isRadixPowerOfTwo = radix.nonzeroBitCount == 1
1709+
let radix_ = Magnitude(radix)
1710+
let quotientAndRemainder: (Magnitude) -> (Magnitude, Magnitude) = { value in
1711+
return isRadixPowerOfTwo
1712+
? (value >> radix.trailingZeroBitCount, value & (radix_ - 1))
1713+
: value.quotientAndRemainder(dividingBy: radix_)
1714+
}
1715+
1716+
let hasLetters = radix > 10
1717+
let ascii: (UInt8) -> UInt8 = { digit in
1718+
let base: UInt8
1719+
if !hasLetters || digit < 10 {
1720+
base = UInt8(("0" as Unicode.Scalar).value)
1721+
} else if uppercase {
1722+
base = UInt8(("A" as Unicode.Scalar).value) &- 10
1723+
} else {
1724+
base = UInt8(("a" as Unicode.Scalar).value) &- 10
1725+
}
1726+
return base &+ digit
1727+
}
1728+
1729+
let isNegative = Self.isSigned && self < (0 as Self)
1730+
var value = magnitude
1731+
var result: [UInt8] = []
1732+
while value != 0 {
1733+
let (quotient, remainder) = quotientAndRemainder(value)
1734+
result.append(ascii(UInt8(truncatingIfNeeded: remainder)))
1735+
value = quotient
1736+
}
1737+
1738+
if isNegative {
1739+
result.append(UInt8(("-" as Unicode.Scalar).value))
1740+
}
1741+
return String._fromWellFormedCodeUnitSequence(
1742+
UTF8.self, input: result.reversed())
1743+
}
1744+
1745+
/// A textual representation of this value.
1746+
@_inlineable // FIXME(sil-serialize-all)
1747+
public var description: String {
1748+
return _description()
1749+
}
1750+
}
1751+
1752+
1753+
//===----------------------------------------------------------------------===//
1754+
//===--- Strideable conformance -------------------------------------------===//
1755+
//===----------------------------------------------------------------------===//
1756+
16821757
extension BinaryInteger {
16831758
// FIXME(ABI): using Int as the return type is wrong.
16841759
/// Returns the distance from this value to the given value, expressed as a
@@ -1968,6 +2043,7 @@ extension BinaryInteger {
19682043

19692044
//===----------------------------------------------------------------------===//
19702045
//===--- Ambiguity breakers -----------------------------------------------===//
2046+
//
19712047
// These two versions of the operators are not ordered with respect to one
19722048
// another, but the compiler choses the second one, and that results in infinite
19732049
// recursion.
@@ -2694,38 +2770,6 @@ extension UnsignedInteger {
26942770
@_inlineable // FIXME(sil-serialize-all)
26952771
@_transparent
26962772
public static var isSigned: Bool { return false }
2697-
2698-
/// A textual representation of this value.
2699-
@_inlineable // FIXME(sil-serialize-all)
2700-
public var description: String {
2701-
if self.bitWidth <= ${word_bits} {
2702-
return _uint64ToString(UInt64(truncatingIfNeeded: self))
2703-
}
2704-
if self == (0 as Self) {
2705-
return "0"
2706-
}
2707-
return renderNonZeroDescription()
2708-
}
2709-
2710-
// FIXME(integers): perhaps a faster implementation is possible
2711-
@_inlineable // FIXME(sil-serialize-all)
2712-
@_versioned // FIXME(sil-serialize-all)
2713-
@_transparent
2714-
internal func renderNonZeroDescription() -> String {
2715-
let ascii0 = 48
2716-
var buf: [Unicode.Scalar] = []
2717-
2718-
var x = self
2719-
repeat {
2720-
let r = x % 10
2721-
x /= 10
2722-
buf.append(
2723-
Unicode.Scalar(
2724-
ascii0 + Int(UInt(truncatingIfNeeded: r)._value))!)
2725-
}
2726-
while x != (0 as Self)
2727-
return String(buf.reversed().lazy.map { Character($0) })
2728-
}
27292773
}
27302774

27312775
extension UnsignedInteger where Self : FixedWidthInteger {
@@ -2828,17 +2872,6 @@ public protocol SignedInteger : BinaryInteger, SignedNumeric {
28282872
}
28292873

28302874
extension SignedInteger {
2831-
/// A textual representation of this value.
2832-
@_inlineable // FIXME(sil-serialize-all)
2833-
public var description: String {
2834-
if self.bitWidth <= ${word_bits} {
2835-
return _int64ToString(Int64(truncatingIfNeeded: self))
2836-
}
2837-
2838-
let base = magnitude.description
2839-
return self < (0 as Self) ? "-" + base : base
2840-
}
2841-
28422875
/// A Boolean value indicating whether this type is a signed integer type.
28432876
///
28442877
/// This property is always `true` for signed integer types.

0 commit comments

Comments
 (0)