Skip to content

Commit c8d4fd4

Browse files
committed
[stdlib] Remove BinaryInteger._word(at:)
Removes BinaryInteger's _word(at:) requirement and its countOfRepresentedWords property, making the words property the sole way to access an integer's words. This is a better fix for https://bugs.swift.org/browse/SR-5275.
1 parent 7836268 commit c8d4fd4

File tree

3 files changed

+30
-136
lines changed

3 files changed

+30
-136
lines changed

stdlib/public/core/DoubleWidth.swift.gyb

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -157,31 +157,6 @@ public struct DoubleWidth<Base : FixedWidthInteger> :
157157
self.init(exactly: integerPart)
158158
}
159159
}
160-
161-
public func _word(at n: Int) -> UInt {
162-
if Base.bitWidth < UInt.bitWidth {
163-
if UInt.bitWidth % Base.bitWidth != 0 {
164-
fatalError("word(at:) is not supported on this type")
165-
}
166-
if n > 0 {
167-
// Since `Base` is narrower than word, any non-zero word will give us
168-
// the correct value here (0 or ~0).
169-
return _storage.high._word(at: n)
170-
}
171-
return _storage.low._word(at: 0) |
172-
(_storage.high._word(at: 0) << numericCast(Base.bitWidth))
173-
} else {
174-
// multiples of word-size only
175-
if Base.bitWidth % UInt.bitWidth != 0 {
176-
fatalError("word(at:) is not supported on this type")
177-
}
178-
179-
// TODO: move to Int128 just like init(_builtinIntegerLiteral:) ?
180-
return (n < _storage.low._countRepresentedWords) ?
181-
_storage.low._word(at: n) :
182-
_storage.high._word(at: n - _storage.low._countRepresentedWords)
183-
}
184-
}
185160

186161
public struct Words : Collection {
187162
public enum _IndexValue {

stdlib/public/core/Integers.swift.gyb

Lines changed: 29 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1427,27 +1427,22 @@ public protocol BinaryInteger :
14271427
/// - Parameter source: An integer to convert to this type.
14281428
init<T : BinaryInteger>(clamping source: T)
14291429

1430-
/// Returns the n-th word, counting from the least significant to most
1431-
/// significant, of this value's binary representation.
1432-
///
1433-
/// The `_word(at:)` method returns negative values in two's complement
1434-
/// representation, regardless of a type's underlying implementation. If `n`
1435-
/// is greater than the number of words in this value's current
1436-
/// representation, the result is `0` for positive numbers and `~0` for
1437-
/// negative numbers.
1438-
///
1439-
/// - Parameter n: The word to return, counting from the least significant to
1440-
/// most significant. `n` must be greater than or equal to zero.
1441-
/// - Returns: An word-sized, unsigned integer with the bit pattern of the
1442-
/// n-th word of this value.
1443-
func _word(at n: Int) -> UInt
1444-
1445-
// FIXME(integers): add doc comments
1446-
// FIXME: Should be `Words : Collection where Words.Iterator.Element == UInt`
1430+
// FIXME: Should be `Words : Collection where Words.Element == UInt`
14471431
// See <rdar://problem/31798916> for why it isn't.
1448-
associatedtype Words
1432+
/// A type that represents the words of a binary integer.
1433+
/// Must implement the `Collection` protocol with an `UInt` element type.
1434+
associatedtype Words : Sequence where Words.Element == UInt
1435+
1436+
/// Returns a collection containing the words of this value's binary
1437+
/// representation, in order from the least significant to most significant.
1438+
///
1439+
/// Negative values are returned in two's complement representation,
1440+
/// regardless of the type's underlying implementation.
14491441
var words: Words { get }
14501442

1443+
/// The least significant word in this value's binary representation.
1444+
var _lowUWord: UInt { get }
1445+
14511446
/// The number of bits in the current binary representation of this value.
14521447
///
14531448
/// This property is a constant for instances of fixed-width integer
@@ -1543,13 +1538,10 @@ extension BinaryInteger {
15431538
return (self > (0 as Self) ? 1 : 0) - (self < (0 as Self) ? 1 : 0)
15441539
}
15451540

1546-
/// The number of words used for the current binary representation of this
1547-
/// value.
1548-
///
1549-
/// This property is a constant for instances of fixed-width integer types.
15501541
@_transparent
1551-
public var _countRepresentedWords: Int {
1552-
return (self.bitWidth + ${word_bits} - 1) / ${word_bits}
1542+
public var _lowUWord: UInt {
1543+
var it = words.makeIterator()
1544+
return it.next() ?? 0
15531545
}
15541546

15551547
public func quotientAndRemainder(dividingBy rhs: Self)
@@ -1887,26 +1879,6 @@ extension BinaryInteger {
18871879
}
18881880
#endif
18891881

1890-
extension BinaryInteger {
1891-
// FIXME(integers): Should be removed once words get implemented properly.
1892-
// Meanhile it allows to conform to the BinaryInteger without implementing
1893-
// underscored APIs. https://bugs.swift.org/browse/SR-5275
1894-
public func _word(at n: Int) -> UInt {
1895-
fatalError("Should be overridden")
1896-
}
1897-
1898-
// FIXME(integers): inefficient. Should get rid of _word(at:) and
1899-
// _countRepresentedWords, and make `words` the basic operation.
1900-
public var words: [UInt] {
1901-
var result = [UInt]()
1902-
result.reserveCapacity(_countRepresentedWords)
1903-
for i in 0..<self._countRepresentedWords {
1904-
result.append(_word(at: i))
1905-
}
1906-
return result
1907-
}
1908-
}
1909-
19101882
//===----------------------------------------------------------------------===//
19111883
//===--- FixedWidthInteger ------------------------------------------------===//
19121884
//===----------------------------------------------------------------------===//
@@ -2320,24 +2292,24 @@ ${unsafeOperationComment(x.operator)}
23202292
}
23212293
% end
23222294

2323-
@inline(__always)
2295+
@_transparent
23242296
public init<T : BinaryInteger>(extendingOrTruncating source: T) {
2325-
if Self.bitWidth <= ${word_bits} {
2326-
self = Self.init(_truncatingBits: source._word(at: 0))
2297+
if Self.bitWidth <= ${word_bits} || source.bitWidth <= ${word_bits} {
2298+
self = Self.init(_truncatingBits: source._lowUWord)
23272299
}
23282300
else {
2329-
var result: Self = source < (0 as T) ? ~0 : 0
2330-
// start with the most significant word
2331-
var n = source._countRepresentedWords
2332-
while n >= 0 {
2333-
// masking is OK here because this we have already ensured
2334-
// that Self.bitWidth > ${word_bits}. Not masking results in
2301+
let neg = source < (0 as T)
2302+
var result: Self = neg ? ~0 : 0
2303+
var shift: Self = 0
2304+
let width = Self(_truncatingBits: Self.bitWidth._lowUWord)
2305+
for word in source.words {
2306+
guard shift < width else { break }
2307+
// masking shift is OK here because we have already ensured
2308+
// that shift < Self.bitWidth. Not masking results in
23352309
// infinite recursion.
2336-
result &<<= (${word_bits} as Self)
2337-
result |= Self(_truncatingBits: source._word(at: n))
2338-
n -= 1
2310+
result ^= Self(_truncatingBits: neg ? ~word : word) &<< shift
2311+
shift += ${word_bits}
23392312
}
2340-
23412313
self = result
23422314
}
23432315
}
@@ -2798,22 +2770,6 @@ ${assignmentOperatorComment(x.operator, True)}
27982770
)._lowUWord._value)
27992771
}
28002772

2801-
@_transparent
2802-
public func _word(at n: Int) -> UInt {
2803-
_precondition(n >= 0, "Negative word index")
2804-
if _fastPath(n < _countRepresentedWords) {
2805-
let shift = UInt(n._value) &* ${word_bits}
2806-
let bitWidth = UInt(self.bitWidth._value)
2807-
_sanityCheck(shift < bitWidth)
2808-
return (self &>> ${Self}(_truncatingBits: shift))._lowUWord
2809-
}
2810-
% if signed:
2811-
return self < (0 as ${Self}) ? ~0 : 0
2812-
% else:
2813-
return 0
2814-
% end
2815-
}
2816-
28172773
% if Self == 'UInt' or (bits > word_bits and not signed):
28182774
// FIXME should be RandomAccessCollection
28192775
public struct Words : BidirectionalCollection {

test/Prototypes/BigInt.swift

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,7 @@ public struct _BigInt<Word: FixedWidthInteger & UnsignedInteger> :
121121
// FIXME: This is broken on 32-bit arch w/ Word = UInt64
122122
let wordRatio = UInt.bitWidth / Word.bitWidth
123123
_sanityCheck(wordRatio != 0)
124-
for i in 0..<source._countRepresentedWords {
125-
var sourceWord = source._word(at: i)
124+
for var sourceWord in source.words {
126125
for _ in 0..<wordRatio {
127126
_data.append(Word(extendingOrTruncating: sourceWord))
128127
sourceWord >>= Word.bitWidth
@@ -660,38 +659,6 @@ public struct _BigInt<Word: FixedWidthInteger & UnsignedInteger> :
660659
}
661660
}
662661

663-
public func _word(at n: Int) -> UInt {
664-
let ratio = UInt.bitWidth / Word.bitWidth
665-
_sanityCheck(ratio != 0)
666-
667-
var twosComplementData = _dataAsTwosComplement()
668-
669-
// Find beginning of range. If we're beyond the value, return 1s or 0s.
670-
let start = n * ratio
671-
if start >= twosComplementData.count {
672-
return isNegative ? UInt.max : 0
673-
}
674-
675-
// Find end of range. If the range extends beyond the representation,
676-
// add bits to the end.
677-
let end = (n + 1) * ratio
678-
if end > twosComplementData.count {
679-
twosComplementData.append(contentsOf:
680-
repeatElement(isNegative ? Word.max : 0,
681-
count: end - twosComplementData.count))
682-
}
683-
684-
// Build the correct word from the range determined above.
685-
let wordSlice = twosComplementData[start..<end]
686-
var result: UInt = 0
687-
for v in wordSlice.reversed() {
688-
result <<= Word.bitWidth
689-
result |= UInt(extendingOrTruncating: v)
690-
}
691-
692-
return result
693-
}
694-
695662
public var words: [UInt] {
696663
_sanityCheck(UInt.bitWidth % Word.bitWidth == 0)
697664
var words: [UInt] = []
@@ -1316,10 +1283,6 @@ struct Bit : FixedWidthInteger, UnsignedInteger {
13161283
return self
13171284
}
13181285

1319-
func _word(at n: Int) -> UInt {
1320-
return UInt(value)
1321-
}
1322-
13231286
var words: UInt.Words {
13241287
return UInt(value).words
13251288
}

0 commit comments

Comments
 (0)