Skip to content

Commit 54b3b8b

Browse files
committed
[stdlib] Random: don't randomize FixedWidthIntegers by overwriting their raw memory
Custom FixedWidthInteger types may not support this. Introduce a new (non-public) FixedWidthInteger requirement for generating random values; implement it using &<</+ in the generic case, and specialize it using RandomNumberGenerator._fill(bytes) for the builtin types.
1 parent 12a2b32 commit 54b3b8b

File tree

2 files changed

+34
-30
lines changed

2 files changed

+34
-30
lines changed

stdlib/public/core/Integers.swift.gyb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2402,6 +2402,9 @@ ${assignmentOperatorComment(x.operator, False)}
24022402
static func ${x.operator}=(_ lhs: inout Self, _ rhs: Self)
24032403
% end
24042404

2405+
static func _random<R: RandomNumberGenerator>(
2406+
using generator: inout R
2407+
) -> Self
24052408
}
24062409

24072410
extension FixedWidthInteger {
@@ -3022,6 +3025,26 @@ ${assignmentOperatorComment('&' + x.operator, True)}
30223025
% end
30233026
}
30243027

3028+
extension FixedWidthInteger {
3029+
public static func _random<R: RandomNumberGenerator>(
3030+
using generator: inout R
3031+
) -> Self {
3032+
if bitWidth <= UInt64.bitWidth {
3033+
return Self(truncatingIfNeeded: generator.next() as UInt64)
3034+
}
3035+
3036+
let (quotient, remainder) = bitWidth.quotientAndRemainder(
3037+
dividingBy: UInt64.bitWidth
3038+
)
3039+
var tmp: Self = 0
3040+
for i in 0 ..< quotient + remainder.signum() {
3041+
let next: UInt64 = generator.next()
3042+
tmp += Self(truncatingIfNeeded: next) &<< (UInt64.bitWidth * i)
3043+
}
3044+
return tmp
3045+
}
3046+
}
3047+
30253048
//===----------------------------------------------------------------------===//
30263049
//===--- UnsignedInteger --------------------------------------------------===//
30273050
//===----------------------------------------------------------------------===//
@@ -4018,6 +4041,16 @@ public func _assumeNonNegative(_ x: ${Self}) -> ${Self} {
40184041
}
40194042
% end
40204043

4044+
extension ${Self} {
4045+
public static func _random<R: RandomNumberGenerator>(
4046+
using generator: inout R
4047+
) -> ${Self} {
4048+
var result: ${Self} = 0
4049+
withUnsafeMutableBytes(of: &result) { generator._fill(bytes: $0) }
4050+
return result
4051+
}
4052+
}
4053+
40214054
//===--- end of FIXME(integers) -------------------------------------------===//
40224055

40234056
% end # end of concrete FixedWidthInteger section

stdlib/public/core/Random.swift

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -83,26 +83,7 @@ extension RandomNumberGenerator {
8383
/// every value of `T` is equally likely to be returned.
8484
@inlinable
8585
public mutating func next<T: FixedWidthInteger & UnsignedInteger>() -> T {
86-
if T.bitWidth <= UInt64.bitWidth {
87-
return T(truncatingIfNeeded: next())
88-
}
89-
90-
let (quotient, remainder) = T.bitWidth.quotientAndRemainder(
91-
dividingBy: UInt64.bitWidth
92-
)
93-
var tmp: T = 0
94-
95-
for i in 0 ..< quotient {
96-
tmp += T(truncatingIfNeeded: next()) &<< (UInt64.bitWidth * i)
97-
}
98-
99-
if remainder != 0 {
100-
let random = next()
101-
let mask = UInt64.max &>> (UInt64.bitWidth - remainder)
102-
tmp += T(truncatingIfNeeded: random & mask) &<< (UInt64.bitWidth * quotient)
103-
}
104-
105-
return tmp
86+
return T._random(using: &self)
10687
}
10788

10889
/// Returns a random value that is less than the given upper bound.
@@ -167,16 +148,6 @@ public struct Random : RandomNumberGenerator {
167148
_stdlib_random(&random, MemoryLayout<UInt64>.size)
168149
return random
169150
}
170-
171-
/// Returns a value from a uniform, independent distribution of binary data.
172-
///
173-
/// - Returns: A random value of `T`. Bits are randomly distributed so that
174-
/// every value of `T` is equally likely to be returned.
175-
public mutating func next<T: FixedWidthInteger & UnsignedInteger>() -> T {
176-
var random: T = 0
177-
_stdlib_random(&random, MemoryLayout<T>.size)
178-
return random
179-
}
180151

181152
public mutating func _fill(bytes buffer: UnsafeMutableRawBufferPointer) {
182153
if !buffer.isEmpty {

0 commit comments

Comments
 (0)