Skip to content

Commit b933827

Browse files
committed
[SE-0368] StaticBigInt: add the Swift wrapper type
1 parent 8fb1ea2 commit b933827

File tree

4 files changed

+395
-2
lines changed

4 files changed

+395
-2
lines changed

stdlib/public/core/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#
33
# This source file is part of the Swift.org open source project
44
#
5-
# Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
5+
# Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
66
# Licensed under Apache License v2.0 with Runtime Library Exception
77
#
88
# See https://swift.org/LICENSE.txt for license information
@@ -226,6 +226,7 @@ set(SWIFTLIB_SOURCES
226226
PlaygroundDisplay.swift
227227
CommandLine.swift
228228
SliceBuffer.swift
229+
StaticBigInt.swift
229230
UnfoldSequence.swift
230231
VarArgs.swift
231232
Zip.swift

stdlib/public/core/GroupInfo.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@
167167
"Integers": [
168168
"Integers.swift",
169169
"IntegerTypes.swift",
170-
"IntegerParsing.swift"],
170+
"IntegerParsing.swift",
171+
"StaticBigInt.swift"],
171172
"Floating": [
172173
"FloatingPoint.swift",
173174
"FloatingPointParsing.swift",

stdlib/public/core/StaticBigInt.swift

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
/// An immutable arbitrary-precision signed integer.
14+
///
15+
/// `StaticBigInt` is primarily intended to be used as the associated type of an
16+
/// `ExpressibleByIntegerLiteral` conformance.
17+
@available(SwiftStdlib 5.8, *)
18+
@frozen
19+
public struct StaticBigInt:
20+
_ExpressibleByBuiltinIntegerLiteral,
21+
ExpressibleByIntegerLiteral,
22+
Sendable
23+
{
24+
@available(SwiftStdlib 5.8, *)
25+
@usableFromInline
26+
internal let _value: Builtin.IntLiteral
27+
28+
@available(SwiftStdlib 5.8, *)
29+
@inlinable
30+
public init(_builtinIntegerLiteral value: Builtin.IntLiteral) {
31+
_value = value
32+
}
33+
34+
/// Returns the given value unchanged.
35+
@_alwaysEmitIntoClient
36+
public static prefix func + (_ rhs: Self) -> Self {
37+
rhs
38+
}
39+
}
40+
41+
//===----------------------------------------------------------------------===//
42+
// MARK: - Binary Representation
43+
//===----------------------------------------------------------------------===//
44+
45+
@available(SwiftStdlib 5.8, *)
46+
extension StaticBigInt {
47+
48+
/// Returns `-1`, `0`, or `+1` to indicate whether this value is less than,
49+
/// equal to, or greater than zero.
50+
///
51+
/// The return type is `Int` rather than `Self`.
52+
@available(SwiftStdlib 5.8, *)
53+
@inlinable
54+
public func signum() -> Int {
55+
_isNegative ? -1 : (bitWidth == 1) ? 0 : +1
56+
}
57+
58+
@available(SwiftStdlib 5.8, *)
59+
@inlinable
60+
internal var _isNegative: Bool {
61+
Bool(Builtin.isNegative_IntLiteral(_value))
62+
}
63+
64+
/// Returns the minimal number of bits in this value's binary representation,
65+
/// including the sign bit, and excluding the sign extension.
66+
///
67+
/// The following examples show the least significant byte of each value's
68+
/// binary representation, separated into excluded and included bits. Negative
69+
/// values are in two's complement.
70+
///
71+
/// * `-4` (`0b11111_100`) is 3 bits.
72+
/// * `-3` (`0b11111_101`) is 3 bits.
73+
/// * `-2` (`0b111111_10`) is 2 bits.
74+
/// * `-1` (`0b1111111_1`) is 1 bit.
75+
/// * `+0` (`0b0000000_0`) is 1 bit.
76+
/// * `+1` (`0b000000_01`) is 2 bits.
77+
/// * `+2` (`0b00000_010`) is 3 bits.
78+
/// * `+3` (`0b00000_011`) is 3 bits.
79+
@available(SwiftStdlib 5.8, *)
80+
@inlinable
81+
public var bitWidth: Int {
82+
Int(Builtin.bitWidth_IntLiteral(_value))
83+
}
84+
85+
/// Returns a 32-bit or 64-bit word of this value's binary representation.
86+
///
87+
/// The words are ordered from least significant to most significant, with
88+
/// an infinite sign extension. Negative values are in two's complement.
89+
///
90+
/// let negative: StaticBigInt = -0x0011223344556677_8899AABBCCDDEEFF
91+
/// negative.signum() //-> -1
92+
/// negative.bitWidth //-> 118
93+
/// negative[0] //-> 0x7766554433221101
94+
/// negative[1] //-> 0xFFEEDDCCBBAA9988
95+
/// negative[2] //-> 0xFFFFFFFFFFFFFFFF
96+
///
97+
/// let positive: StaticBigInt = +0x0011223344556677_8899AABBCCDDEEFF
98+
/// positive.signum() //-> +1
99+
/// positive.bitWidth //-> 118
100+
/// positive[0] //-> 0x8899AABBCCDDEEFF
101+
/// positive[1] //-> 0x0011223344556677
102+
/// positive[2] //-> 0x0000000000000000
103+
///
104+
/// - Parameter wordIndex: A nonnegative zero-based offset.
105+
@available(SwiftStdlib 5.8, *)
106+
@inlinable
107+
public subscript(_ wordIndex: Int) -> UInt {
108+
_precondition(wordIndex >= 0, "Negative word index")
109+
let bitIndex = wordIndex.multipliedReportingOverflow(by: UInt.bitWidth)
110+
guard !bitIndex.overflow, bitIndex.partialValue < bitWidth else {
111+
return _isNegative ? ~0 : 0
112+
}
113+
return UInt(
114+
Builtin.wordAtIndex_IntLiteral(_value, wordIndex._builtinWordValue)
115+
)
116+
}
117+
}
118+
119+
//===----------------------------------------------------------------------===//
120+
// MARK: - Textual Representation
121+
//===----------------------------------------------------------------------===//
122+
123+
@available(SwiftStdlib 5.8, *)
124+
extension StaticBigInt: CustomDebugStringConvertible {
125+
126+
@available(SwiftStdlib 5.8, *)
127+
public var debugDescription: String {
128+
let isNegative = _isNegative
129+
let indicator = isNegative ? "-0x" : "+0x"
130+
131+
// Equivalent to `ceil(bitWidthExcludingSignBit / fourBitsPerHexDigit)`.
132+
// Underestimated for `-(16 ** y)` values (e.g. "-0x1", "-0x10", "-0x100").
133+
let capacity = indicator.utf8.count + (((bitWidth - 1) + 3) / 4)
134+
var result = String(unsafeUninitializedCapacity: capacity) { utf8 in
135+
136+
// Pre-initialize with zeros, ignoring extra capacity.
137+
var utf8 = UnsafeMutableBufferPointer(rebasing: utf8.prefix(capacity))
138+
utf8.initialize(repeating: UInt8(ascii: "0"))
139+
140+
// Use a 32-bit element type, to generate small hexadecimal strings.
141+
typealias Element = UInt32
142+
let hexDigitsPerElement = Element.bitWidth / 4
143+
_internalInvariant(hexDigitsPerElement <= _SmallString.capacity)
144+
_internalInvariant(UInt.bitWidth.isMultiple(of: Element.bitWidth))
145+
146+
// Lazily compute the magnitude, starting with the least significant bits.
147+
var overflow = isNegative
148+
for bitIndex in stride(from: 0, to: bitWidth, by: Element.bitWidth) {
149+
let wordIndex = bitIndex >> UInt.bitWidth.trailingZeroBitCount
150+
var element = Element(_truncatingBits: self[wordIndex] &>> bitIndex)
151+
if isNegative {
152+
element = ~element
153+
if overflow {
154+
(element, overflow) = element.addingReportingOverflow(1)
155+
}
156+
}
157+
158+
// Overwrite trailing zeros with hexadecimal digits.
159+
let hexDigits = String(element, radix: 16, uppercase: true)
160+
_ = UnsafeMutableBufferPointer(
161+
rebasing: utf8.suffix(hexDigits.utf8.count)
162+
).initialize(from: hexDigits.utf8)
163+
utf8 = UnsafeMutableBufferPointer(
164+
rebasing: utf8.dropLast(hexDigitsPerElement)
165+
)
166+
}
167+
return capacity
168+
}
169+
170+
// Overwrite leading zeros with the "±0x" indicator.
171+
if let upToIndex = result.firstIndex(where: { $0 != "0" }) {
172+
result.replaceSubrange(..<upToIndex, with: indicator)
173+
} else {
174+
result = "+0x0"
175+
}
176+
return result
177+
}
178+
}
179+
180+
#if SWIFT_ENABLE_REFLECTION
181+
@available(SwiftStdlib 5.8, *)
182+
extension StaticBigInt: CustomReflectable {
183+
184+
@available(SwiftStdlib 5.8, *)
185+
public var customMirror: Mirror {
186+
Mirror(self, unlabeledChildren: EmptyCollection<Void>())
187+
}
188+
}
189+
#endif

0 commit comments

Comments
 (0)