//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2022 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 // //===----------------------------------------------------------------------===// /// An immutable arbitrary-precision signed integer. /// /// `StaticBigInt` is primarily intended to be used as the associated type of an /// `ExpressibleByIntegerLiteral` conformance. /// /// extension UInt256: ExpressibleByIntegerLiteral { /// public init(integerLiteral value: StaticBigInt) { /// precondition( /// value.signum() >= 0 && value.bitWidth <= 1 + Self.bitWidth, /// "integer overflow: '\(value)' as '\(Self.self)'" /// ) /// self.words = Words() /// for wordIndex in 0.. Int { _isNegative ? -1 : (bitWidth == 1) ? 0 : +1 } @available(SwiftStdlib 5.8, *) @inlinable internal var _isNegative: Bool { #if compiler(>=5.8) && $BuiltinIntLiteralAccessors Bool(Builtin.isNegative_IntLiteral(_value)) #else fatalError("Swift compiler is incompatible with this SDK version") #endif } /// Returns the minimal number of bits in this value's binary representation, /// including the sign bit, and excluding the sign extension. /// /// The following examples show the least significant byte of each value's /// binary representation, separated (by an underscore) into excluded and /// included bits. Negative values are in two's complement. /// /// * `-4` (`0b11111_100`) is 3 bits. /// * `-3` (`0b11111_101`) is 3 bits. /// * `-2` (`0b111111_10`) is 2 bits. /// * `-1` (`0b1111111_1`) is 1 bit. /// * `+0` (`0b0000000_0`) is 1 bit. /// * `+1` (`0b000000_01`) is 2 bits. /// * `+2` (`0b00000_010`) is 3 bits. /// * `+3` (`0b00000_011`) is 3 bits. @available(SwiftStdlib 5.8, *) @inlinable public var bitWidth: Int { #if compiler(>=5.8) && $BuiltinIntLiteralAccessors Int(Builtin.bitWidth_IntLiteral(_value)) #else fatalError("Swift compiler is incompatible with this SDK version") #endif } /// Returns a 32-bit or 64-bit word of this value's binary representation. /// /// The words are ordered from least significant to most significant, with /// an infinite sign extension. Negative values are in two's complement. /// /// let negative: StaticBigInt = -0x0011223344556677_8899AABBCCDDEEFF /// negative.signum() //-> -1 /// negative.bitWidth //-> 118 /// negative[0] //-> 0x7766554433221101 /// negative[1] //-> 0xFFEEDDCCBBAA9988 /// negative[2] //-> 0xFFFFFFFFFFFFFFFF /// /// let positive: StaticBigInt = 0x0011223344556677_8899AABBCCDDEEFF /// positive.signum() //-> +1 /// positive.bitWidth //-> 118 /// positive[0] //-> 0x8899AABBCCDDEEFF /// positive[1] //-> 0x0011223344556677 /// positive[2] //-> 0x0000000000000000 /// /// - Parameter wordIndex: A nonnegative zero-based offset. @available(SwiftStdlib 5.8, *) @inlinable public subscript(_ wordIndex: Int) -> UInt { _precondition(wordIndex >= 0, "Negative word index") let bitIndex = wordIndex.multipliedReportingOverflow(by: UInt.bitWidth) guard !bitIndex.overflow, bitIndex.partialValue < bitWidth else { return _isNegative ? ~0 : 0 } #if compiler(>=5.8) && $BuiltinIntLiteralAccessors return UInt( Builtin.wordAtIndex_IntLiteral(_value, wordIndex._builtinWordValue) ) #else fatalError("Swift compiler is incompatible with this SDK version") #endif } } //===----------------------------------------------------------------------===// // MARK: - Textual Representation //===----------------------------------------------------------------------===// @available(SwiftStdlib 5.8, *) extension StaticBigInt: CustomDebugStringConvertible { @available(SwiftStdlib 5.8, *) public var debugDescription: String { let isNegative = _isNegative let indicator = isNegative ? "-0x" : "+0x" // Equivalent to `ceil(bitWidthExcludingSignBit / fourBitsPerHexDigit)`. // Underestimated for `-(16 ** y)` values (e.g. "-0x1", "-0x10", "-0x100"). let capacity = indicator.utf8.count + (((bitWidth - 1) + 3) / 4) var result = unsafe String(unsafeUninitializedCapacity: capacity) { utf8 in // Pre-initialize with zeros, ignoring extra capacity. var utf8 = unsafe utf8.prefix(capacity) unsafe utf8.initialize(repeating: UInt8(ascii: "0")) // Use a 32-bit element type, to generate small hexadecimal strings. typealias Element = UInt32 let hexDigitsPerElement = Element.bitWidth / 4 _internalInvariant(hexDigitsPerElement <= _SmallString.capacity) _internalInvariant(UInt.bitWidth.isMultiple(of: Element.bitWidth)) // Lazily compute the magnitude, starting with the least significant bits. var overflow = isNegative for bitIndex in stride(from: 0, to: bitWidth, by: Element.bitWidth) { let wordIndex = bitIndex >> UInt.bitWidth.trailingZeroBitCount var element = Element(_truncatingBits: self[wordIndex] &>> bitIndex) if isNegative { element = ~element if overflow { (element, overflow) = element.addingReportingOverflow(1) } } // Overwrite trailing zeros with hexadecimal digits. let hexDigits = String(element, radix: 16, uppercase: true).utf8 _ = unsafe utf8.suffix(hexDigits.count).update(fromContentsOf: hexDigits) unsafe utf8 = unsafe utf8.dropLast(hexDigitsPerElement) } return capacity } // Overwrite leading zeros with the "±0x" indicator. if let upToIndex = result.firstIndex(where: { $0 != "0" }) { result.replaceSubrange(..()) } } #endif