Skip to content

Commit b65d0c1

Browse files
benrimmingtonAzoy
authored andcommitted
Consolidate _stdlib_random functions (#2)
* Use the `__has_include` and `GRND_RANDOM` macros * Use `getentropy` instead of `getrandom` * Use `std::min` from the <algorithm> header * Move `#if` out of the `_stdlib_random` function * Use `getrandom` with "/dev/urandom" fallback * Use `#pragma comment` to import "Bcrypt.lib" * <https://docs.microsoft.com/en-us/cpp/preprocessor/comment-c-cpp> * <https://clang.llvm.org/docs/UsersManual.html#microsoft-extensions> * Use "/dev/urandom" instead of `SecRandomCopyBytes` * Use `swift::StaticMutex` for shared "/dev/urandom" * Add `getrandom_available`; use `O_CLOEXEC` flag Add platform impl docs Update copyrights Fix docs Add _stdlib_random test Update _stdlib_random test Add missing & Notice about _stdlib_random Fix docs Guard on upperBound = 0 Test full range of 8 bit integers Remove some gyb Clean up integerRangeTest Remove FixedWidthInteger constraint Use arc4random universally Fix randomElement Constrain shuffle to RandomAccessCollection warning instead of error Move Apple's implementation Fix failing test on 32 bit systems
1 parent f146d17 commit b65d0c1

File tree

13 files changed

+203
-171
lines changed

13 files changed

+203
-171
lines changed

benchmark/single-source/RandomShuffle.swift

Lines changed: 1 addition & 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 - 2018 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2018 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

benchmark/single-source/RandomValues.swift

Lines changed: 1 addition & 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 - 2018 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2018 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

benchmark/utils/main.swift

Lines changed: 1 addition & 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 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2018 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

stdlib/public/SwiftShims/LibcShims.h

Lines changed: 1 addition & 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 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2018 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

stdlib/public/core/Bool.swift

Lines changed: 5 additions & 7 deletions
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 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2018 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
@@ -87,23 +87,21 @@ public struct Bool {
8787
self = value
8888
}
8989

90-
/// Returns a random Boolean
90+
/// Returns a random Boolean value
9191
///
9292
/// - Parameter generator: The random number generator to use when getting a
9393
/// random Boolean.
94-
/// - Returns: A random Boolean.
94+
/// - Returns: A random Boolean value.
9595
@inlinable
9696
public static func random<T: RandomNumberGenerator>(
9797
using generator: inout T
9898
) -> Bool {
9999
return (generator.next() >> 17) & 1 == 0
100100
}
101101

102-
/// Returns a random Boolean
102+
/// Returns a random Boolean value
103103
///
104-
/// - Parameter generator: The random number generator to use when getting a
105-
/// random Boolean.
106-
/// - Returns: A random Boolean.
104+
/// - Returns: A random Boolean value.
107105
///
108106
/// This uses the standard library's default random number generator.
109107
@inlinable

stdlib/public/core/CMakeLists.txt

Lines changed: 1 addition & 6 deletions
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 - 2017 Apple Inc. and the Swift project authors
5+
# Copyright (c) 2014 - 2018 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
@@ -194,7 +194,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
194194
list(APPEND swift_core_link_flags "-all_load")
195195
list(APPEND swift_core_framework_depends Foundation)
196196
list(APPEND swift_core_framework_depends CoreFoundation)
197-
list(APPEND swift_core_framework_depends Security)
198197
list(APPEND swift_core_private_link_libraries icucore)
199198
else()
200199
# With the GNU linker the equivalent of -all_load is to tell the linker
@@ -229,10 +228,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
229228
${EXECINFO_LIBRARY})
230229
endif()
231230

232-
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
233-
list(APPEND swift_core_link_flags "$ENV{SystemRoot}/system32/bcrypt.dll")
234-
endif()
235-
236231
option(SWIFT_CHECK_ESSENTIAL_STDLIB
237232
"Check core standard library layering by linking its essential subset"
238233
FALSE)

stdlib/public/core/Collection.swift

Lines changed: 22 additions & 4 deletions
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 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2018 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
@@ -803,6 +803,25 @@ public protocol Collection: Sequence where SubSequence: Collection {
803803
/// `endIndex`.
804804
func formIndex(after i: inout Index)
805805

806+
/// Returns a random element of the collection, using the given generator as
807+
/// a source for randomness.
808+
///
809+
/// You use this method to select a random element from a collection when you
810+
/// are using a custom random number generator. For example, call
811+
/// `randomElement(using:)` to select a random element from an array of names.
812+
///
813+
/// let names = ["Zoey", "Chloe", "Amani", "Amaia"]
814+
/// let randomName = names.randomElement(using: &myGenerator)!
815+
/// // randomName == "Amani" (maybe)
816+
///
817+
/// - Parameter generator: The random number generator to use when choosing
818+
/// a random element.
819+
/// - Returns: A random element from the collection. If the collection is
820+
/// empty, the method returns `nil`.
821+
func randomElement<T: RandomNumberGenerator>(
822+
using generator: inout T
823+
) -> Element?
824+
806825
@available(*, deprecated, message: "all index distances are now of type Int")
807826
typealias IndexDistance = Int
808827
}
@@ -1038,9 +1057,8 @@ extension Collection {
10381057
guard !isEmpty else { return nil }
10391058
let random = generator.next(upperBound: UInt(count))
10401059
let index = self.index(
1041-
self.startIndex,
1042-
offsetBy: IndexDistance(random),
1043-
limitedBy: self.endIndex
1060+
startIndex,
1061+
offsetBy: numericCast(random)
10441062
)
10451063
return self[index]
10461064
}

stdlib/public/core/CollectionAlgorithms.swift

Lines changed: 3 additions & 4 deletions
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 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2018 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
@@ -411,8 +411,6 @@ extension Sequence {
411411
/// to `numbers.shuffled()` above is equivalent to calling
412412
/// `numbers.shuffled(using: &Random.default)`.
413413
///
414-
/// - Parameter generator: The random number generator to use when shuffling
415-
/// the sequence.
416414
/// - Returns: A shuffled array of this sequence's elements.
417415
///
418416
/// - Complexity: O(*n*)
@@ -422,7 +420,7 @@ extension Sequence {
422420
}
423421
}
424422

425-
extension MutableCollection {
423+
extension MutableCollection where Self : RandomAccessCollection {
426424
/// Shuffles the collection in place, using the given generator as a source
427425
/// for randomness.
428426
///
@@ -442,6 +440,7 @@ extension MutableCollection {
442440
public mutating func shuffle<T: RandomNumberGenerator>(
443441
using generator: inout T
444442
) {
443+
let count = self.count
445444
guard count > 1 else { return }
446445
var amount = count
447446
var currentIndex = startIndex

stdlib/public/core/FloatingPoint.swift.gyb

Lines changed: 7 additions & 8 deletions
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 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2018 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
@@ -2403,7 +2403,7 @@ extension BinaryFloatingPoint {
24032403
% exampleRange = '10.0..<20.0' if Range == 'Range' else '10.0...20.0'
24042404
extension BinaryFloatingPoint
24052405
where Self.RawSignificand : FixedWidthInteger,
2406-
Self.RawSignificand.Stride : SignedInteger & FixedWidthInteger,
2406+
Self.RawSignificand.Stride : SignedInteger,
24072407
Self.RawSignificand.Magnitude : UnsignedInteger {
24082408

24092409
/// Returns a random value within the specified range, using the given
@@ -2420,7 +2420,7 @@ where Self.RawSignificand : FixedWidthInteger,
24202420
/// // Prints "14.2286325689993"
24212421
/// // Prints "13.1485686260762"
24222422
///
2423-
/// The `random(using:)` static method chooses a random value from a
2423+
/// The `random(in:using:)` static method chooses a random value from a
24242424
/// continuous uniform distribution in `range`, and then converts that value
24252425
/// to the nearest representable value in this type. Depending on the size and
24262426
/// span of `range`, some concrete values may be represented more frequently
@@ -2439,12 +2439,10 @@ where Self.RawSignificand : FixedWidthInteger,
24392439
in range: ${Range}<Self>,
24402440
using generator: inout T
24412441
) -> Self {
2442-
% if 'Closed' not in Range:
24432442
_precondition(
2444-
range.lowerBound != range.upperBound,
2445-
"Can't get random value with lowerBound == upperBound"
2443+
!range.isEmpty,
2444+
"Can't get random value with an empty range"
24462445
)
2447-
% end
24482446
let delta = range.upperBound - range.lowerBound
24492447
let rand: Self.RawSignificand
24502448
if Self.RawSignificand.bitWidth == Self.significandBitCount + 1 {
@@ -2456,7 +2454,8 @@ where Self.RawSignificand : FixedWidthInteger,
24562454
}
24572455
% end
24582456
} else {
2459-
let maxSignificand = Self.RawSignificand(1 << (Self.significandBitCount + 1))
2457+
let significandCount = Self.significandBitCount + 1
2458+
let maxSignificand: Self.RawSignificand = 1 << significandCount
24602459
% if 'Closed' not in Range:
24612460
rand = generator.next(upperBound: maxSignificand)
24622461
% else:

stdlib/public/core/Integers.swift.gyb

Lines changed: 4 additions & 8 deletions
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 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2018 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
@@ -2560,11 +2560,9 @@ extension ${Range}
25602560
public func randomElement<T: RandomNumberGenerator>(
25612561
using generator: inout T
25622562
) -> Element? {
2563-
% if 'Closed' not in Range:
2564-
guard lowerBound != upperBound else {
2563+
guard !isEmpty else {
25652564
return nil
25662565
}
2567-
% end
25682566
let isLowerNegative = Bound.isSigned && lowerBound < 0
25692567
let sameSign = !Bound.isSigned || isLowerNegative == (upperBound < 0)
25702568
% if 'Closed' not in Range:
@@ -2658,12 +2656,10 @@ where Self.Stride : SignedInteger,
26582656
in range: ${Range}<Self>,
26592657
using generator: inout T
26602658
) -> Self {
2661-
% if 'Closed' not in Range:
26622659
_precondition(
2663-
range.lowerBound != range.upperBound,
2664-
"Can't get random value with lowerBound == upperBound"
2660+
!range.isEmpty,
2661+
"Can't get random value with an empty range"
26652662
)
2666-
% end
26672663
return range.randomElement(using: &generator)!
26682664
}
26692665

stdlib/public/core/Random.swift

Lines changed: 20 additions & 8 deletions
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 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2018 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
@@ -32,7 +32,7 @@ import SwiftShims
3232
/// }
3333
///
3434
/// static func randomWeekday() -> Weekday {
35-
/// return Weekday.randomWeekday(using: Random.default)
35+
/// return Weekday.randomWeekday(using: &Random.default)
3636
/// }
3737
/// }
3838
///
@@ -64,7 +64,7 @@ extension RandomNumberGenerator {
6464
@inlinable
6565
public mutating func next<T: FixedWidthInteger & UnsignedInteger>() -> T {
6666
if T.bitWidth <= UInt64.bitWidth {
67-
return T(truncatingIfNeeded: self.next())
67+
return T(truncatingIfNeeded: next())
6868
}
6969

7070
let (quotient, remainder) = T.bitWidth.quotientAndRemainder(
@@ -73,11 +73,11 @@ extension RandomNumberGenerator {
7373
var tmp: T = 0
7474

7575
for i in 0 ..< quotient {
76-
tmp += T(truncatingIfNeeded: self.next()) &<< (UInt64.bitWidth * i)
76+
tmp += T(truncatingIfNeeded: next()) &<< (UInt64.bitWidth * i)
7777
}
7878

7979
if remainder != 0 {
80-
let random = self.next()
80+
let random = next()
8181
let mask = UInt64.max &>> (UInt64.bitWidth - remainder)
8282
tmp += T(truncatingIfNeeded: random & mask) &<< (UInt64.bitWidth * quotient)
8383
}
@@ -94,12 +94,13 @@ extension RandomNumberGenerator {
9494
public mutating func next<T: FixedWidthInteger & UnsignedInteger>(
9595
upperBound: T
9696
) -> T {
97+
guard upperBound != 0 else { return 0 }
9798
let tmp = (T.max % upperBound) + 1
9899
let range = tmp == upperBound ? 0 : tmp
99100
var random: T = 0
100101

101102
repeat {
102-
random = self.next()
103+
random = next()
103104
} while random < range
104105

105106
return random % upperBound
@@ -118,8 +119,18 @@ extension RandomNumberGenerator {
118119
///
119120
/// `Random.default` is safe to use in multiple threads, and uses a
120121
/// cryptographically secure algorithm whenever possible.
122+
///
123+
/// Platform Implementation of `Random`
124+
/// ===================================
125+
///
126+
/// - Apple platforms all use `arc4random_buf(3)`.
127+
/// - `Linux`, `Android`, `Cygwin`, `Haiku`, `FreeBSD`, and `PS4` all try to
128+
/// use `getrandom(2)`, but if it doesn't exist then they read from
129+
/// `/dev/urandom`.
130+
/// - `Fuchsia` calls `getentropy(3)`.
131+
/// - `Windows` calls `BCryptGenRandom`.
121132
public struct Random : RandomNumberGenerator {
122-
/// The shared, default instance of the `Range` random number generator.
133+
/// The default instance of the `Random` random number generator.
123134
public static var `default`: Random {
124135
get { return Random() }
125136
set { /* Discard */ }
@@ -130,6 +141,7 @@ public struct Random : RandomNumberGenerator {
130141
/// Returns a value from a uniform, independent distribution of binary data.
131142
///
132143
/// - Returns: An unsigned 64-bit random value.
144+
@effects(releasenone)
133145
public mutating func next() -> UInt64 {
134146
var random: UInt64 = 0
135147
_stdlib_random(&random, MemoryLayout<UInt64>.size)
@@ -147,7 +159,7 @@ public struct Random : RandomNumberGenerator {
147159
}
148160
}
149161

150-
public // @testable
162+
@usableFromInline internal// @testable
151163
func _stdlib_random(_ bytes: UnsafeMutableRawBufferPointer) {
152164
if !bytes.isEmpty {
153165
_stdlib_random(bytes.baseAddress!, bytes.count)

0 commit comments

Comments
 (0)