Skip to content

Commit b1e6bd3

Browse files
jfmontanaroAnton-4
andauthored
add random seed generation (roc-lang#410)
* add random seed generation * add `random_u32` and `random_bytes`, rename `seed` to `random_u64` * misc changes --------- Co-authored-by: Anton-4 <[email protected]>
1 parent df2ea0f commit b1e6bd3

File tree

11 files changed

+315
-100
lines changed

11 files changed

+315
-100
lines changed

Cargo.lock

Lines changed: 165 additions & 98 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ members = [
1111
"crates/roc_stdio",
1212
"crates/roc_env",
1313
"crates/roc_sqlite",
14+
"crates/roc_random",
1415
]
1516

1617
[workspace.package]
@@ -37,6 +38,7 @@ roc_http = { path = "crates/roc_http" }
3738
roc_io_error = { path = "crates/roc_io_error" }
3839
roc_stdio = { path = "crates/roc_stdio" }
3940
roc_env = { path = "crates/roc_env" }
41+
roc_random = { path = "crates/roc_random" }
4042
roc_sqlite = { path = "crates/roc_sqlite" }
4143
memchr = "=2.7.4"
4244
hyper = { version = "=1.6.0", default-features = false, features = [
@@ -61,3 +63,4 @@ libc = "=0.2.172"
6163
backtrace = "=0.3.75"
6264
libsqlite3-sys = { version = "=0.33.0", features = ["bundled"] }
6365
thread_local = "=1.1.8"
66+
getrandom = { version = "0.3.3", features = [ "std" ] }

ci/expect_scripts/random.exp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/usr/bin/expect
2+
3+
# uncomment line below for debugging
4+
# exp_internal 1
5+
6+
set timeout 7
7+
8+
source ./ci/expect_scripts/shared-code.exp
9+
10+
spawn $env(EXAMPLES_DIR)random
11+
12+
set expected_output [normalize_output "
13+
Random U64 seed is: \\\d+
14+
Random U32 seed is: \\\d+
15+
"]
16+
17+
expect -re $expected_output {
18+
expect eof {
19+
check_exit_and_segfault
20+
}
21+
}
22+
23+
puts stderr "\nExpect script failed: output was different from expected value. uncomment `exp_internal 1` to debug."
24+
exit 1

crates/roc_host/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ roc_io_error.workspace = true
2727
roc_http.workspace = true
2828
roc_stdio.workspace = true
2929
roc_env.workspace = true
30+
roc_random.workspace = true
3031
roc_sqlite.workspace = true
3132
hyper.workspace = true
3233
hyper-rustls.workspace = true
3334
tokio.workspace = true
3435
bytes.workspace = true
3536
http-body-util.workspace = true
36-
hyper-util.workspace = true
37+
hyper-util.workspace = true

crates/roc_host/src/lib.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,8 @@ pub fn init() {
349349
roc_fx_temp_dir as _,
350350
roc_fx_get_locale as _,
351351
roc_fx_get_locales as _,
352+
roc_fx_random_u64 as _,
353+
roc_fx_random_u32 as _,
352354
roc_fx_sqlite_bind as _,
353355
roc_fx_sqlite_column_value as _,
354356
roc_fx_sqlite_columns as _,
@@ -658,7 +660,7 @@ async fn async_send_request(request: hyper::Request<http_body_util::Full<Bytes>>
658660

659661
let client: Client<_, http_body_util::Full<Bytes>> =
660662
Client::builder(TokioExecutor::new()).build(https);
661-
663+
662664
let response_res = client.request(request).await;
663665

664666
match response_res {
@@ -809,6 +811,16 @@ pub extern "C" fn roc_fx_get_locales() -> RocList<RocStr> {
809811
roc_env::get_locales()
810812
}
811813

814+
#[no_mangle]
815+
pub extern "C" fn roc_fx_random_u64() -> RocResult<u64, IOErr> {
816+
roc_random::random_u64()
817+
}
818+
819+
#[no_mangle]
820+
pub extern "C" fn roc_fx_random_u32() -> RocResult<u32, IOErr> {
821+
roc_random::random_u32()
822+
}
823+
812824
#[no_mangle]
813825
pub extern "C" fn roc_fx_sqlite_bind(
814826
stmt: RocBox<()>,

crates/roc_random/Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "roc_random"
3+
description = "Common functionality for Roc to interface with getrandom"
4+
authors.workspace = true
5+
edition.workspace = true
6+
license.workspace = true
7+
repository.workspace = true
8+
version.workspace = true
9+
10+
[dependencies]
11+
roc_std.workspace = true
12+
roc_io_error.workspace = true
13+
getrandom.workspace = true

crates/roc_random/src/lib.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
use roc_std::{RocList, RocResult};
2+
use roc_io_error::IOErr;
3+
4+
5+
pub fn random_u64() -> RocResult<u64, IOErr> {
6+
getrandom::u64()
7+
.map_err(|e| std::io::Error::from(e))
8+
.map_err(|e| IOErr::from(e))
9+
.into()
10+
}
11+
12+
pub fn random_u32() -> RocResult<u32, IOErr> {
13+
getrandom::u32()
14+
.map_err(|e| std::io::Error::from(e))
15+
.map_err(|e| IOErr::from(e))
16+
.into()
17+
}

examples/random.roc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
app [main!] { pf: platform "../platform/main.roc" }
2+
3+
# To run this example: check the README.md in this folder
4+
5+
# Demo of basic-cli Random functions
6+
7+
import pf.Stdout
8+
import pf.Random
9+
import pf.Arg exposing [Arg]
10+
11+
main! : List Arg => Result {} _
12+
main! = |_args|
13+
random_u64 = Random.random_seed_u64!({})?
14+
Stdout.line!("Random U64 seed is: ${Inspect.to_str(random_u64)}")?
15+
16+
random_u32 = Random.random_seed_u32!({})?
17+
Stdout.line!("Random U32 seed is: ${Inspect.to_str(random_u32)}")
18+
19+
# See the example linked below on how to generate a sequence of random numbers using a seed
20+
# https://github.com/roc-lang/examples/blob/main/examples/RandomNumbers/main.roc

platform/Host.roc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ hosted [
3333
hard_link!,
3434
path_type!,
3535
posix_time!,
36+
random_u64!,
37+
random_u32!,
3638
send_request!,
3739
set_cwd!,
3840
sleep_millis!,
@@ -146,3 +148,6 @@ env_dict! : {} => List (Str, Str)
146148
env_var! : Str => Result Str {}
147149
exe_path! : {} => Result (List U8) {}
148150
set_cwd! : List U8 => Result {} {}
151+
152+
random_u64! : {} => Result U64 InternalIOErr.IOErrFromHost
153+
random_u32! : {} => Result U32 InternalIOErr.IOErrFromHost

platform/Random.roc

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
module [
2+
IOErr,
3+
random_seed_u64!,
4+
random_seed_u32!,
5+
]
6+
7+
import InternalIOErr
8+
import Host
9+
10+
11+
## Tag union of possible errors when getting a random seed.
12+
##
13+
## > This is the same as [`File.IOErr`](File#IOErr).
14+
IOErr : InternalIOErr.IOErr
15+
16+
## Generate a random `U64` seed using the system's source of randomness.
17+
## A "seed" is a starting value used to deterministically generate a random sequence.
18+
##
19+
## > !! This function is NOT cryptographically secure.
20+
##
21+
## This uses the [`u64()`](https://docs.rs/getrandom/latest/getrandom/fn.u64.html) function
22+
## of the [getrandom crate](https://crates.io/crates/getrandom) to produce
23+
## a single random 64-bit integer.
24+
##
25+
## For hobby purposes, you can just call this function repreatedly to get random numbers.
26+
## In general, we recommend using this seed in combination with a library like
27+
## [roc-random](https://github.com/lukewilliamboswell/roc-random) to generate additional
28+
## random numbers quickly.
29+
##
30+
random_seed_u64! : {} => Result U64 [RandomErr IOErr]
31+
random_seed_u64! = |{}|
32+
Host.random_u64!({})
33+
|> Result.map_err(|err| RandomErr(InternalIOErr.handle_err(err)))
34+
35+
## Generate a random `U32` seed using the system's source of randomness.
36+
## A "seed" is a starting value used to deterministically generate a random sequence.
37+
##
38+
## > !! This function is NOT cryptographically secure.
39+
##
40+
## This uses the [`u32()`](https://docs.rs/getrandom/0.3.3/getrandom/fn.u32.html) function
41+
## of the [getrandom crate](https://crates.io/crates/getrandom) to produce
42+
## a single random 32-bit integer.
43+
##
44+
## For hobby purposes, you can just call this function repreatedly to get random numbers.
45+
## In general, we recommend using this seed in combination with a library like
46+
## [roc-random](https://github.com/lukewilliamboswell/roc-random) to generate additional
47+
## random numbers quickly.
48+
##
49+
random_seed_u32! : {} => Result U32 [RandomErr IOErr]
50+
random_seed_u32! = |{}|
51+
Host.random_u32!({})
52+
|> Result.map_err(|err| RandomErr(InternalIOErr.handle_err(err)))

0 commit comments

Comments
 (0)