Add _stdlib_random for more platforms (#1)

* Remove refs to Countable ranges

* Add `_stdlib_random` for more platforms

* Use `getrandom` (if available) for Android, Cygwin

* Reorder the `_stdlib_random` functions

* Also include <features.h> on Linux

* Add `#error TODO` in `_stdlib_random` for Windows

* Colon after Fatal Error

Performance improvement for Random

gybify ranges

Fix typo in 'basic random numbers'
Add _stdlib_random as a testable method

Switch to generic constraints

Hopefully link against bcrypt

Fix some implementation details

1. Uniform distribution is now uniform
2. Apply Jens' method for uniform floats

Fix a lineable attribute
This commit is contained in:
Ben Rimmington
2018-02-10 00:09:14 +00:00
committed by Azoy
parent d23d219e95
commit a5df0ef83d
11 changed files with 342 additions and 183 deletions

View File

@@ -92,12 +92,24 @@ public struct Bool {
/// - Parameter generator: The random number generator to use when getting a /// - Parameter generator: The random number generator to use when getting a
/// random Boolean. /// random Boolean.
/// - Returns: A random Boolean. /// - Returns: A random Boolean.
@_inlineable @inlinable
public static func random( public static func random<T: RandomNumberGenerator>(
using generator: RandomNumberGenerator = Random.default using generator: T
) -> Bool { ) -> Bool {
return generator.next() % 2 == 0 return generator.next() % 2 == 0
} }
/// Returns a random Boolean
///
/// - Parameter generator: The random number generator to use when getting a
/// random Boolean.
/// - Returns: A random Boolean.
///
/// This uses the standard library's default random number generator.
@inlinable
public static func random() -> Bool {
return Bool.random(using: Random.default)
}
} }
extension Bool : _ExpressibleByBuiltinBooleanLiteral, ExpressibleByBooleanLiteral { extension Bool : _ExpressibleByBuiltinBooleanLiteral, ExpressibleByBooleanLiteral {

View File

@@ -229,6 +229,10 @@ if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
${EXECINFO_LIBRARY}) ${EXECINFO_LIBRARY})
endif() endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
list(APPEND swift_core_link_flags "$ENV{SystemRoot}/system32/bcrypt.dll")
endif()
option(SWIFT_CHECK_ESSENTIAL_STDLIB option(SWIFT_CHECK_ESSENTIAL_STDLIB
"Check core standard library layering by linking its essential subset" "Check core standard library layering by linking its essential subset"
FALSE) FALSE)

View File

@@ -480,57 +480,6 @@ extension ClosedRange where Bound: Strideable, Bound.Stride : SignedInteger {
} }
} }
extension ClosedRange
where Bound : FixedWidthInteger,
Bound.Magnitude : UnsignedInteger {
/// Returns a random element from this collection.
///
/// - Parameter generator: The random number generator to use when getting
/// a random element.
/// - Returns: A random element from this collection.
///
/// A good example of this is getting a random greeting from an array:
///
/// let greetings = ["hi", "hey", "hello", "hola"]
/// let randomGreeting = greetings.random()
///
/// If the collection is empty, the value of this function is `nil`.
///
/// let numbers = [10, 20, 30, 40, 50]
/// if let randomNumber = numbers.random() {
/// print(randomNumber)
/// }
/// // Could print "20", perhaps
@_inlineable
public func random(
using generator: RandomNumberGenerator = Random.default
) -> Element? {
let isLowerNegative = Bound.isSigned && lowerBound < 0
let sameSign = !Bound.isSigned || isLowerNegative == (upperBound < 0)
var delta: Bound.Magnitude
if isLowerNegative {
delta = sameSign
? lowerBound.magnitude - upperBound.magnitude
: lowerBound.magnitude + upperBound.magnitude
} else {
delta = upperBound.magnitude - lowerBound.magnitude
}
if delta == Bound.Magnitude.max {
return Bound(truncatingIfNeeded: generator.next() as Bound.Magnitude)
}
delta += 1
let randomMagnitude = generator.next(upperBound: delta)
if sameSign {
return lowerBound + Bound(randomMagnitude)
} else {
return Bound.isSigned && randomMagnitude <= upperBound.magnitude
? Bound(randomMagnitude)
: 0 - Bound(randomMagnitude - upperBound.magnitude)
}
}
}
extension ClosedRange { extension ClosedRange {
@inlinable @inlinable
public func overlaps(_ other: ClosedRange<Bound>) -> Bool { public func overlaps(_ other: ClosedRange<Bound>) -> Bool {

View File

@@ -1034,12 +1034,12 @@ extension Collection {
/// print(randomNumber) /// print(randomNumber)
/// } /// }
/// // Could print "20", perhaps /// // Could print "20", perhaps
@_inlineable @inlinable
public func random( public func random<T: RandomNumberGenerator>(
using generator: RandomNumberGenerator = Random.default using generator: T
) -> Element? { ) -> Element? {
guard !isEmpty else { return nil } guard !isEmpty else { return nil }
let random = generator.next(upperBound: UInt(self.count)) let random = generator.next(upperBound: UInt(count))
let index = self.index( let index = self.index(
self.startIndex, self.startIndex,
offsetBy: IndexDistance(random), offsetBy: IndexDistance(random),
@@ -1048,6 +1048,31 @@ extension Collection {
return self[index] return self[index]
} }
/// Returns a random element from this collection.
///
/// - Parameter generator: The random number generator to use when getting
/// a random element.
/// - Returns: A random element from this collection.
///
/// A good example of this is getting a random greeting from an array:
///
/// let greetings = ["hi", "hey", "hello", "hola"]
/// let randomGreeting = greetings.random()
///
/// If the collection is empty, the value of this function is `nil`.
///
/// let numbers = [10, 20, 30, 40, 50]
/// if let randomNumber = numbers.random() {
/// print(randomNumber)
/// }
/// // Could print "20", perhaps
///
/// This uses the standard library's default random number generator.
@inlinable
public func random() -> Element? {
return random(using: Random.default)
}
/// Do not use this method directly; call advanced(by: n) instead. /// Do not use this method directly; call advanced(by: n) instead.
@inlinable @inlinable
@inline(__always) @inline(__always)

View File

@@ -368,7 +368,7 @@ extension MutableCollection where Self : BidirectionalCollection {
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// shuffled() // shuffled()/shuffle()
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
extension Sequence { extension Sequence {
@@ -377,14 +377,26 @@ extension Sequence {
/// - Parameter generator: The random number generator to use when shuffling /// - Parameter generator: The random number generator to use when shuffling
/// the sequence. /// the sequence.
/// - Returns: A shuffled array of this sequence's elements. /// - Returns: A shuffled array of this sequence's elements.
@_inlineable @inlinable
public func shuffled( public func shuffled<T: RandomNumberGenerator>(
using generator: RandomNumberGenerator = Random.default using generator: T
) -> [Element] { ) -> [Element] {
var result = ContiguousArray(self) var result = ContiguousArray(self)
result.shuffle(using: generator) result.shuffle(using: generator)
return Array(result) return Array(result)
} }
/// Returns the elements of the sequence, shuffled.
///
/// - Parameter generator: The random number generator to use when shuffling
/// the sequence.
/// - Returns: A shuffled array of this sequence's elements.
///
/// This uses the standard library's default random number generator.
@inlinable
public func shuffled() -> [Element] {
return shuffled(using: Random.default)
}
} }
extension MutableCollection { extension MutableCollection {
@@ -392,9 +404,9 @@ extension MutableCollection {
/// ///
/// - Parameter generator: The random number generator to use when shuffling /// - Parameter generator: The random number generator to use when shuffling
/// the collection. /// the collection.
@_inlineable @inlinable
public mutating func shuffle( public mutating func shuffle<T: RandomNumberGenerator>(
using generator: RandomNumberGenerator = Random.default using generator: T
) { ) {
guard count > 1 else { return } guard count > 1 else { return }
var amount = count var amount = count
@@ -409,6 +421,17 @@ extension MutableCollection {
formIndex(after: &currentIndex) formIndex(after: &currentIndex)
} }
} }
/// Shuffles the collection in place.
///
/// - Parameter generator: The random number generator to use when shuffling
/// the collection.
///
/// This uses the standard library's default random number generator.
@inlinable
public mutating func shuffle() {
shuffle(using: Random.default)
}
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//

View File

@@ -2413,10 +2413,10 @@ where Self.RawSignificand : FixedWidthInteger,
/// - Parameter generator: The random number generator to use when getting /// - Parameter generator: The random number generator to use when getting
/// the random floating point. /// the random floating point.
/// - Returns: A random representation of this floating point. /// - Returns: A random representation of this floating point.
@_inlineable @inlinable
public static func random( public static func random<T: RandomNumberGenerator>(
in range: ${Range}<Self>, in range: ${Range}<Self>,
using generator: RandomNumberGenerator = Random.default using generator: T
) -> Self { ) -> Self {
% if 'Closed' not in Range: % if 'Closed' not in Range:
_precondition( _precondition(
@@ -2425,7 +2425,7 @@ where Self.RawSignificand : FixedWidthInteger,
) )
% end % end
let delta = range.upperBound - range.lowerBound let delta = range.upperBound - range.lowerBound
let maxSignificand: Self.RawSignificand = 1 << Self.significandBitCount let maxSignificand = Self.RawSignificand(1 << (Self.significandBitCount + 1))
% if 'Closed' not in Range: % if 'Closed' not in Range:
let rand: Self.RawSignificand = generator.next(upperBound: maxSignificand) let rand: Self.RawSignificand = generator.next(upperBound: maxSignificand)
% else: % else:
@@ -2434,14 +2434,23 @@ where Self.RawSignificand : FixedWidthInteger,
return range.upperBound return range.upperBound
} }
% end % end
let unitRandom = Self.init( let unitRandom = Self.init(rand) * Self.ulpOfOne / 2
sign: .plus,
exponentBitPattern: (1 as Self).exponentBitPattern,
significandBitPattern: rand
) - 1
return delta * unitRandom + range.lowerBound return delta * unitRandom + range.lowerBound
} }
/// Returns a random representation of this floating point within the range.
///
/// - Parameter range: A ${Range} to determine the bounds to get a random value
/// from.
/// - Parameter generator: The random number generator to use when getting
/// the random floating point.
/// - Returns: A random representation of this floating point.
///
/// This uses the standard library's default random number generator.
@inlinable
public static func random(in range: ${Range}<Self>) -> Self {
return Self.random(in: range, using: Random.default)
}
} }
% end % end

View File

@@ -2532,6 +2532,99 @@ ${assignmentOperatorComment(x.operator, False)}
% for Range in ['Range', 'ClosedRange']: % for Range in ['Range', 'ClosedRange']:
extension ${Range}
where Bound: FixedWidthInteger,
Bound.Magnitude: UnsignedInteger {
/// Returns a random element from this collection.
///
/// - Parameter generator: The random number generator to use when getting
/// a random element.
/// - Returns: A random element from this collection.
///
/// A good example of this is getting a random greeting from an array:
///
/// let greetings = ["hi", "hey", "hello", "hola"]
/// let randomGreeting = greetings.random()
///
/// If the collection is empty, the value of this function is `nil`.
///
/// let numbers = [10, 20, 30, 40, 50]
/// if let randomNumber = numbers.random() {
/// print(randomNumber)
/// }
/// // Could print "20", perhaps
@inlinable
public func random<T: RandomNumberGenerator>(
using generator: T
) -> Element? {
% if 'Closed' not in Range:
guard lowerBound != upperBound else {
return nil
}
% end
let isLowerNegative = Bound.isSigned && lowerBound < 0
let sameSign = !Bound.isSigned || isLowerNegative == (upperBound < 0)
% if 'Closed' not in Range:
let delta: Bound.Magnitude
% else:
var delta: Bound.Magnitude
% end
if isLowerNegative {
delta = sameSign
? lowerBound.magnitude - upperBound.magnitude
: lowerBound.magnitude + upperBound.magnitude
} else {
delta = upperBound.magnitude - lowerBound.magnitude
}
% if 'Closed' in Range:
if delta == Bound.Magnitude.max {
return Bound(truncatingIfNeeded: generator.next() as Bound.Magnitude)
}
delta += 1
% end
let randomMagnitude = generator.next(upperBound: delta)
if sameSign {
return lowerBound + Bound(randomMagnitude)
} else {
% if 'Closed' not in Range:
return randomMagnitude < upperBound.magnitude
? Bound(randomMagnitude)
: -1 - Bound(randomMagnitude - upperBound.magnitude)
% else:
return Bound.isSigned && randomMagnitude <= upperBound.magnitude
? Bound(randomMagnitude)
: 0 - Bound(randomMagnitude - upperBound.magnitude)
% end
}
}
/// Returns a random element from this collection.
///
/// - Parameter generator: The random number generator to use when getting
/// a random element.
/// - Returns: A random element from this collection.
///
/// A good example of this is getting a random greeting from an array:
///
/// let greetings = ["hi", "hey", "hello", "hola"]
/// let randomGreeting = greetings.random()
///
/// If the collection is empty, the value of this function is `nil`.
///
/// let numbers = [10, 20, 30, 40, 50]
/// if let randomNumber = numbers.random() {
/// print(randomNumber)
/// }
/// // Could print "20", perhaps
///
/// This uses the standard library's default random number generator.
@inlinable
public func random() -> Element? {
return self.random(using: Random.default)
}
}
extension FixedWidthInteger extension FixedWidthInteger
where Self.Stride : SignedInteger, where Self.Stride : SignedInteger,
Self.Magnitude : UnsignedInteger { Self.Magnitude : UnsignedInteger {
@@ -2543,10 +2636,10 @@ where Self.Stride : SignedInteger,
/// - Parameter generator: The random number generator to use when getting /// - Parameter generator: The random number generator to use when getting
/// the random integer. /// the random integer.
/// - Returns: A random representation of this integer. /// - Returns: A random representation of this integer.
@_inlineable @inlinable
public static func random( public static func random<T: RandomNumberGenerator>(
in range: ${Range}<Self>, in range: ${Range}<Self>,
using generator: RandomNumberGenerator = Random.default using generator: T
) -> Self { ) -> Self {
% if 'Closed' not in Range: % if 'Closed' not in Range:
_precondition( _precondition(
@@ -2556,6 +2649,20 @@ where Self.Stride : SignedInteger,
% end % end
return range.random(using: generator)! return range.random(using: generator)!
} }
/// Returns a random representation of this integer within the range.
///
/// - Parameter range: A ${Range} to determine the bounds to get a random value
/// from.
/// - Parameter generator: The random number generator to use when getting
/// the random integer.
/// - Returns: A random representation of this integer.
///
/// This uses the standard library's default random number generator.
@inlinable
public static func random(in range: ${Range}<Self>) -> Self {
return Self.random(in: range, using: Random.default)
}
} }
% end % end

View File

@@ -46,10 +46,10 @@ extension RandomNumberGenerator {
/// ///
/// This differs from next() as this function has the ability to transform the /// This differs from next() as this function has the ability to transform the
/// generated number to any unsigned integer. /// generated number to any unsigned integer.
@_inlineable @inlinable
public func next<T: FixedWidthInteger & UnsignedInteger>() -> T { public func next<T: FixedWidthInteger & UnsignedInteger>() -> T {
if T.bitWidth == UInt64.bitWidth { if T.bitWidth <= UInt64.bitWidth {
return T(self.next()) return T(truncatingIfNeeded: self.next())
} }
let (quotient, remainder) = T.bitWidth.quotientAndRemainder( let (quotient, remainder) = T.bitWidth.quotientAndRemainder(
@@ -76,11 +76,12 @@ extension RandomNumberGenerator {
/// - Parameter upperBound: The max number this can generate up to. /// - Parameter upperBound: The max number this can generate up to.
/// - Returns: A number that was randomly generated from 0 to upperBound /// - Returns: A number that was randomly generated from 0 to upperBound
/// ///
/// This uses the uniform distribution to form a random number within the /// By default, this uses the uniform distribution to form a random number
/// upperBound. /// that is less than the upperBound.
@_inlineable @inlinable
public func next<T: FixedWidthInteger & UnsignedInteger>(upperBound: T) -> T { public func next<T: FixedWidthInteger & UnsignedInteger>(upperBound: T) -> T {
let range = T.max % upperBound let tmp = (T.max % upperBound) + 1
let range = tmp == upperBound ? 0 : tmp
var random: T = 0 var random: T = 0
repeat { repeat {
@@ -125,4 +126,23 @@ public struct Random : RandomNumberGenerator {
_stdlib_random(&random, MemoryLayout<UInt64>.size) _stdlib_random(&random, MemoryLayout<UInt64>.size)
return random return random
} }
/// Produces the next randomly generated number
///
/// - Returns: A number that was randomly generated
///
/// This differs from next() as this function has the ability to transform the
/// generated number to any unsigned integer.
public func next<T: FixedWidthInteger & UnsignedInteger>() -> T {
var random: T = 0
_stdlib_random(&random, MemoryLayout<T>.size)
return random
}
}
public // @testable
func _stdlib_random(_ bytes: UnsafeMutableRawBufferPointer) {
if !bytes.isEmpty {
_stdlib_random(bytes.baseAddress!, bytes.count)
}
} }

View File

@@ -303,56 +303,6 @@ extension Range where Bound: Strideable, Bound.Stride : SignedInteger {
} }
} }
extension Range
where Bound : FixedWidthInteger,
Bound.Magnitude : UnsignedInteger {
/// Returns a random element from this collection.
///
/// - Parameter generator: The random number generator to use when getting
/// a random element.
/// - Returns: A random element from this collection.
///
/// A good example of this is getting a random greeting from an array:
///
/// let greetings = ["hi", "hey", "hello", "hola"]
/// let randomGreeting = greetings.random()
///
/// If the collection is empty, the value of this function is `nil`.
///
/// let numbers = [10, 20, 30, 40, 50]
/// if let randomNumber = numbers.random() {
/// print(randomNumber)
/// }
/// // Could print "20", perhaps
@_inlineable
public func random(
using generator: RandomNumberGenerator = Random.default
) -> Element? {
guard lowerBound != upperBound else {
return nil
}
let isLowerNegative = Bound.isSigned && lowerBound < 0
let sameSign = !Bound.isSigned || isLowerNegative == (upperBound < 0)
let delta: Bound.Magnitude
if isLowerNegative {
delta = sameSign
? lowerBound.magnitude - upperBound.magnitude
: lowerBound.magnitude + upperBound.magnitude
} else {
delta = upperBound.magnitude - lowerBound.magnitude
}
let randomMagnitude = generator.next(upperBound: delta)
if sameSign {
return lowerBound + Bound(randomMagnitude)
} else {
return randomMagnitude < upperBound.magnitude
? Bound(randomMagnitude)
: -1 - Bound(randomMagnitude - upperBound.magnitude)
}
}
}
extension Range: RangeExpression { extension Range: RangeExpression {
/// Returns the range of indices described by this range expression within /// Returns the range of indices described by this range expression within
/// the given collection. /// the given collection.

View File

@@ -11,35 +11,67 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#if defined(__APPLE__) #if defined(__APPLE__)
#define _REENTRANT # define _REENTRANT
#include <math.h> # include <math.h>
#endif # include <Security/Security.h>
#include <random>
#include <type_traits>
#include <cmath>
#if defined(_WIN32)
#include <io.h>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#else
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/ioctl.h>
#endif #endif
#include <stdlib.h> #if defined(__CYGWIN__)
#include <stdio.h> # include <cygwin/version.h>
#include <string.h> # if (CYGWIN_VERSION_API_MAJOR > 0) || (CYGWIN_VERSION_API_MINOR >= 306)
#include <fcntl.h> # include <sys/random.h>
# define SWIFT_STDLIB_USING_GETRANDOM
# endif
#endif
#if defined(__Fuchsia__)
# include <sys/random.h>
# define SWIFT_STDLIB_USING_GETENTROPY
#endif
#if defined(__linux__)
# include <linux/version.h>
# if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0))
# include <features.h>
# if defined(__BIONIC__) || (defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2,25))
# include <sys/random.h>
# define SWIFT_STDLIB_USING_GETRANDOM
# endif
# endif
#endif
#if defined(_WIN32) && !defined(__CYGWIN__)
# include <io.h>
# define WIN32_LEAN_AND_MEAN
# define WIN32_NO_STATUS
# include <Windows.h>
# undef WIN32_NO_STATUS
# include <Ntstatus.h>
# include <Ntdef.h>
# include <Bcrypt.h>
#else
# include <pthread.h>
# include <semaphore.h>
# include <sys/ioctl.h>
# include <unistd.h>
#endif
#include <cmath>
#include <errno.h> #include <errno.h>
#include <sys/types.h> #include <fcntl.h>
#include <random>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h>
#include <type_traits>
#include "llvm/Support/DataTypes.h"
#include "swift/Basic/Lazy.h" #include "swift/Basic/Lazy.h"
#include "swift/Runtime/Config.h" #include "swift/Runtime/Config.h"
#include "swift/Runtime/Debug.h" #include "swift/Runtime/Debug.h"
#include "../SwiftShims/LibcShims.h" #include "../SwiftShims/LibcShims.h"
#include "llvm/Support/DataTypes.h"
using namespace swift; using namespace swift;
@@ -311,44 +343,72 @@ swift::_stdlib_cxx11_mt19937_uniform(__swift_uint32_t upper_bound) {
} }
#if defined(__APPLE__) #if defined(__APPLE__)
#include <Security/Security.h>
SWIFT_RUNTIME_STDLIB_INTERNAL SWIFT_RUNTIME_STDLIB_INTERNAL
void swift::_stdlib_random(void *buf, __swift_size_t nbytes) { void swift::_stdlib_random(void *buf, __swift_size_t nbytes) {
if (__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *)) { if (__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *)) {
arc4random_buf(buf, nbytes); arc4random_buf(buf, nbytes);
}else { } else {
OSStatus status = SecRandomCopyBytes(kSecRandomDefault, nbytes, buf); OSStatus status = SecRandomCopyBytes(kSecRandomDefault, nbytes, buf);
if (status != errSecSuccess) { if (status != errSecSuccess) {
fatalError(0, "Fatal error: SecRandomCopyBytes failed with error %d\n", status); fatalError(0, "Fatal error: %d in '%s'\n", status, __func__);
} }
} }
} }
#elif defined(__linux__)
#include <linux/version.h> #elif defined(_WIN32) && !defined(__CYGWIN__)
#include <sys/syscall.h> // TODO: Test on Windows
SWIFT_RUNTIME_STDLIB_INTERNAL SWIFT_RUNTIME_STDLIB_INTERNAL
void swift::_stdlib_random(void *buf, __swift_size_t nbytes) { void swift::_stdlib_random(void *buf, __swift_size_t nbytes) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0) NTSTATUS status = BCryptGenRandom(nullptr,
auto _read = [&]() -> __swift_ssize_t { static_cast<PUCHAR>(buf),
return syscall(SYS_getrandom, buf, bytes, 0); static_cast<ULONG>(nbytes),
}; BCRYPT_USE_SYSTEM_PREFERRED_RNG);
#else if (!NT_SUCCESS(status)) {
auto _read = [&]() -> __swift_ssize_t { fatalError(0, "Fatal error: %#.8x in '%s'\n", status, __func__);
static const int fd = _stdlib_open("/dev/urandom", O_RDONLY);
if (fd < 0) {
fatalError(0, "Unable to open '/dev/urandom'\n");
}
return _stdlib_read(fd, buf, bytes);
};
#endif
while (nbytes > 0) {
auto result = _read();
if (result < 1) {
if (errno == EINTR) { continue; }
fatalError(0, "Unable to read '/dev/urandom'\n");
}
buf = static_cast<uint8_t *>(buf) + result;
nbytes -= result;
} }
} }
#elif defined(SWIFT_STDLIB_USING_GETENTROPY)
SWIFT_RUNTIME_STDLIB_INTERNAL
void swift::_stdlib_random(void *buf, __swift_size_t nbytes) {
while (nbytes > 0) {
constexpr __swift_size_t max_nbytes = 256;
__swift_size_t actual_nbytes = (nbytes < max_nbytes ?
nbytes : max_nbytes);
if (0 != getentropy(buf, actual_nbytes)) {
fatalError(0, "Fatal error: %d in '%s'\n", errno, __func__);
}
buf = static_cast<uint8_t *>(buf) + actual_nbytes;
nbytes -= actual_nbytes;
}
}
#else
SWIFT_RUNTIME_STDLIB_INTERNAL
void swift::_stdlib_random(void *buf, __swift_size_t nbytes) {
#if !defined(SWIFT_STDLIB_USING_GETRANDOM)
static const int fd = _stdlib_open("/dev/urandom", O_RDONLY, 0);
if (fd < 0) {
fatalError(0, "Fatal error: %d in '%s'\n", errno, __func__);
}
#endif
while (nbytes > 0) {
#if !defined(SWIFT_STDLIB_USING_GETRANDOM)
__swift_ssize_t actual_nbytes = _stdlib_read(fd, buf, nbytes);
#else
__swift_ssize_t actual_nbytes = getrandom(buf, nbytes, 0);
#endif
if (actual_nbytes < 1) {
if (errno == EINTR) { continue; }
fatalError(0, "Fatal error: %d in '%s'\n", errno, __func__);
}
buf = static_cast<uint8_t *>(buf) + actual_nbytes;
nbytes -= actual_nbytes;
}
}
#endif #endif

View File

@@ -15,7 +15,7 @@ RandomTests.test("basic random numbers") {
let randomDouble1 = Double.random(in: 0 ..< 1) let randomDouble1 = Double.random(in: 0 ..< 1)
expectTrue(randomDouble1 < 1 && randomDouble1 >= 0) expectTrue(randomDouble1 < 1 && randomDouble1 >= 0)
let randomDouble2 = Double.random(in: 0 ..< 1) let randomDouble2 = Double.random(in: 0 ..< 1)
expectTrue(randomDouble1 < 1 && randomDouble2 >= 0) expectTrue(randomDouble2 < 1 && randomDouble2 >= 0)
expectTrue(randomDouble1 != randomDouble2) expectTrue(randomDouble1 != randomDouble2)
} }