mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
- Fix error in `last(where:)` example - Improve MemoryLayout, UnsafePointer, and integer operator discussions - Clean up ranges and random APIs - Revisions to overflow operators and the SignedNumeric requirements - Standardize on 'nonoptional' in remaining uses
178 lines
6.7 KiB
Swift
178 lines
6.7 KiB
Swift
//===--- Random.swift -----------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2018 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import SwiftShims
|
|
|
|
/// A type that provides uniformly distributed random data.
|
|
///
|
|
/// When you call methods that use random data, such as creating new random
|
|
/// values or shuffling a collection, you can pass a `RandomNumberGenerator`
|
|
/// type to be used as the source for randomness. When you don't pass a
|
|
/// generator, the default `SystemRandomNumberGenerator` type is used.
|
|
///
|
|
/// When providing new APIs that use randomness, provide a version that accepts
|
|
/// a generator conforming to the `RandomNumberGenerator` protocol as well as a
|
|
/// version that uses the default system generator. For example, this `Weekday`
|
|
/// enumeration provides static methods that return a random day of the week:
|
|
///
|
|
/// enum Weekday: CaseIterable {
|
|
/// case sunday, monday, tuesday, wednesday, thursday, friday, saturday
|
|
///
|
|
/// static func random<G: RandomNumberGenerator>(using generator: inout G) -> Weekday {
|
|
/// return Weekday.allCases.randomElement(using: &generator)!
|
|
/// }
|
|
///
|
|
/// static func random() -> Weekday {
|
|
/// var g = SystemRandomNumberGenerator()
|
|
/// return Weekday.random(using: &g)
|
|
/// }
|
|
/// }
|
|
///
|
|
/// Conforming to the RandomNumberGenerator Protocol
|
|
/// ================================================
|
|
///
|
|
/// A custom `RandomNumberGenerator` type can have different characteristics
|
|
/// than the default `SystemRandomNumberGenerator` type. For example, a
|
|
/// seedable generator can be used to generate a repeatable sequence of random
|
|
/// values for testing purposes.
|
|
///
|
|
/// To make a custom type conform to the `RandomNumberGenerator` protocol,
|
|
/// implement the required `next()` method. Each call to `next()` must produce
|
|
/// a uniform and independent random value.
|
|
///
|
|
/// Types that conform to `RandomNumberGenerator` should specifically document
|
|
/// the thread safety and quality of the generator.
|
|
public protocol RandomNumberGenerator {
|
|
/// Returns a value from a uniform, independent distribution of binary data.
|
|
///
|
|
/// Use this method when you need random binary data to generate another
|
|
/// value. If you need an integer value within a specific range, use the
|
|
/// static `random(in:using:)` method on that integer type instead of this
|
|
/// method.
|
|
///
|
|
/// - Returns: An unsigned 64-bit random value.
|
|
mutating func next() -> UInt64
|
|
|
|
// FIXME: De-underscore after swift-evolution amendment
|
|
mutating func _fill(bytes buffer: UnsafeMutableRawBufferPointer)
|
|
}
|
|
|
|
extension RandomNumberGenerator {
|
|
@inlinable
|
|
public mutating func _fill(bytes buffer: UnsafeMutableRawBufferPointer) {
|
|
// FIXME: Optimize
|
|
var chunk: UInt64 = 0
|
|
var chunkBytes = 0
|
|
for i in 0..<buffer.count {
|
|
if chunkBytes == 0 {
|
|
chunk = next()
|
|
chunkBytes = UInt64.bitWidth / 8
|
|
}
|
|
buffer[i] = UInt8(truncatingIfNeeded: chunk)
|
|
chunk >>= UInt8.bitWidth
|
|
chunkBytes -= 1
|
|
}
|
|
}
|
|
}
|
|
|
|
extension RandomNumberGenerator {
|
|
/// Returns a value from a uniform, independent distribution of binary data.
|
|
///
|
|
/// Use this method when you need random binary data to generate another
|
|
/// value. If you need an integer value within a specific range, use the
|
|
/// static `random(in:using:)` method on that integer type instead of this
|
|
/// method.
|
|
///
|
|
/// - Returns: A random value of `T`. Bits are randomly distributed so that
|
|
/// every value of `T` is equally likely to be returned.
|
|
@inlinable
|
|
public mutating func next<T: FixedWidthInteger & UnsignedInteger>() -> T {
|
|
return T._random(using: &self)
|
|
}
|
|
|
|
/// Returns a random value that is less than the given upper bound.
|
|
///
|
|
/// Use this method when you need random binary data to generate another
|
|
/// value. If you need an integer value within a specific range, use the
|
|
/// static `random(in:using:)` method on that integer type instead of this
|
|
/// method.
|
|
///
|
|
/// - Parameter upperBound: The upper bound for the randomly generated value.
|
|
/// Must be non-zero.
|
|
/// - Returns: A random value of `T` in the range `0..<upperBound`. Every
|
|
/// value in the range `0..<upperBound` is equally likely to be returned.
|
|
@inlinable
|
|
public mutating func next<T: FixedWidthInteger & UnsignedInteger>(
|
|
upperBound: T
|
|
) -> T {
|
|
_precondition(upperBound != 0, "upperBound cannot be zero.")
|
|
let tmp = (T.max % upperBound) + 1
|
|
let range = tmp == upperBound ? 0 : tmp
|
|
var random: T = 0
|
|
|
|
repeat {
|
|
random = next()
|
|
} while random < range
|
|
|
|
return random % upperBound
|
|
}
|
|
}
|
|
|
|
/// The system's default source of random data.
|
|
///
|
|
/// When you generate random values, shuffle a collection, or perform another
|
|
/// operation that depends on random data, this type is the generator used by
|
|
/// default. For example, the two method calls in this example are equivalent:
|
|
///
|
|
/// let x = Int.random(in: 1...100)
|
|
/// var g = SystemRandomNumberGenerator()
|
|
/// let y = Int.random(in: 1...100, using: &g)
|
|
///
|
|
/// `SystemRandomNumberGenerator` is automatically seeded, is safe to use in
|
|
/// multiple threads, and uses a cryptographically secure algorithm whenever
|
|
/// possible.
|
|
///
|
|
/// Platform Implementation of `SystemRandomNumberGenerator`
|
|
/// ========================================================
|
|
///
|
|
/// While the system generator is automatically seeded and thread-safe on every
|
|
/// platform, the cryptographic quality of the stream of random data produced by
|
|
/// the generator may vary. For more detail, see the documentation for the APIs
|
|
/// used by each platform.
|
|
///
|
|
/// - Apple platforms use `arc4random_buf(3)`.
|
|
/// - Linux platforms use `getrandom(2)` when available; otherwise, they read
|
|
/// from `/dev/urandom`.
|
|
@_fixed_layout
|
|
public struct SystemRandomNumberGenerator : RandomNumberGenerator {
|
|
/// Creates a new instance of the system's default random number generator.
|
|
@inlinable
|
|
public init() { }
|
|
|
|
/// Returns a value from a uniform, independent distribution of binary data.
|
|
///
|
|
/// - Returns: An unsigned 64-bit random value.
|
|
@inlinable
|
|
public mutating func next() -> UInt64 {
|
|
var random: UInt64 = 0
|
|
_stdlib_random(&random, MemoryLayout<UInt64>.size)
|
|
return random
|
|
}
|
|
|
|
@inlinable
|
|
public mutating func _fill(bytes buffer: UnsafeMutableRawBufferPointer) {
|
|
if !buffer.isEmpty {
|
|
_stdlib_random(buffer.baseAddress!, buffer.count)
|
|
}
|
|
}
|
|
}
|