Skip to content

Impl rand_core::RngCore & support all integers in Rng #284

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- stm32h7b0
- stm32h735
env: # Peripheral Feature flags
FLAGS: rt,xspi,sdmmc,fmc,usb_hs,rtc,ethernet,ltdc,crc
FLAGS: rt,xspi,sdmmc,sdmmc-fatfs,fmc,usb_hs,rtc,ethernet,ltdc,crc,rand

steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- log-semihost
- log-rtt
env: # Peripheral Feature flags
FLAGS: rt,xspi,sdmmc,fmc,usb_hs,rtc,ethernet,ltdc,crc
FLAGS: rt,xspi,sdmmc,sdmmc-fatfs,fmc,usb_hs,rtc,ethernet,ltdc,crc,rand

steps:
- uses: actions/checkout@v2
Expand Down
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ version = "0.4"
default-features = false
optional = true

[dependencies.rand_core]
version = "0.6"
default-features = false
optional = true

[dev-dependencies]
cortex-m-rt = ">=0.6.15,<0.8"
cortex-m-rtic = { version = "0.5.8", default-features = false, features = ["cortex-m-7"] }
Expand Down Expand Up @@ -93,6 +98,7 @@ sdmmc-fatfs = ["embedded-sdmmc", "sdmmc"]
ethernet = ["smoltcp"]
rtc = ["chrono"]
crc = []
rand = ["rand_core"]
rt = ["stm32h7/rt"]
usb_hs = ["synopsys-usb-otg", "synopsys-usb-otg/hs"]
stm32h742 = ["stm32h7/stm32h743", "device-selected", "rm0433"]
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
//! External Memory
//!
//! * [Flexible Memory Controller (FMC)](crate::fmc) Feature gate `fmc`
//! * [SD Card (SDMMC)](crate::sdmmc) Feature gate `sdmmc`
//! * [SD Card (SDMMC)](crate::sdmmc) Feature gate `sdmmc` (FAT16/32 driver is available under the `sdmmc-fatfs` feature gate)
//!
//! Timing functions
//!
Expand All @@ -44,7 +44,7 @@
//! Others
//!
//! * [Cyclic Redundancy Check (CRC)](crate::crc) Feature gate `crc`
//! * [Random Number Generator](crate::rng)
//! * [Random Number Generator](crate::rng) ([rand_core::RngCore](rand_core::RngCore) is implemented under the`rand` feature gate)
//! * [System Window Watchdog](crate::watchdog)

#![cfg_attr(not(test), no_std)]
Expand Down
112 changes: 108 additions & 4 deletions src/rng.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use crate::time::Hertz;

#[derive(Debug)]
pub enum ErrorKind {
ClockError,
SeedError,
ClockError = 0,
SeedError = 1,
}

trait KerClk {
Expand Down Expand Up @@ -131,8 +131,112 @@ macro_rules! rng_core {
};
}

rng_core!(u32, u16, u8);
// Only for types larger than 32 bits
macro_rules! rng_core_large {
($($type:ty),+) => {
$(
impl RngCore<$type> for Rng {
fn gen(&mut self) -> Result<$type, ErrorKind> {
const WORDS: usize = mem::size_of::<$type>() / mem::size_of::<u32>();
let mut res: $type = 0;

for i in 0..WORDS {
res |= (self.value()? as $type) << (i * (mem::size_of::<u32>() * 8))
}

Ok(res)
}

fn fill(&mut self, dest: &mut [$type]) -> Result<(), ErrorKind> {
let len = dest.len() * (mem::size_of::<$type>() / mem::size_of::<u32>());
let ptr = dest.as_mut_ptr() as *mut u32;
let slice_u32 = unsafe { core::slice::from_raw_parts_mut(ptr, len) };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is alignment an issue?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mem::align_of::<u32>() = 4
mem::align_of::<u64>() = 8
mem::align_of::<u128>() = 8

I don't think so. u64/u128 is 8-byte aligned which would be valid for 4-byte aligned values as well. https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right, I hadn't seen the distinction between rng_core and rng_core_large.

self.fill(slice_u32)
}
}
)+
};
}

macro_rules! rng_core_transmute {
($($type:ty = $from:ty),+) => {
$(
impl RngCore<$type> for Rng {
fn gen(&mut self) -> Result<$type, ErrorKind> {
let num = <Self as RngCore<$from>>::gen(self)?;
Ok(unsafe { mem::transmute::<$from, $type>(num) })
}

// Test host may have > 32-bit types, which we don't consider.
fn fill(&mut self, dest: &mut [$type]) -> Result<(), ErrorKind> {
let unsigned_slice = unsafe { mem::transmute::<&mut [$type], &mut [$from]>(dest) };
<Self as RngCore<$from>>::fill(self, unsigned_slice)
}
}
)+
};
}

rng_core!(u8, u16, u32);

// Alignment of these types must be a multiple of mem::align_of::<32>()
rng_core_large!(u64, u128);

// A and B must have the same alignment
// rng_core_transmute!(A = B)
// assert!(mem::align_of::<A>() == mem::align_of::<B>())
rng_core_transmute!(
i8 = u8,
i16 = u16,
i32 = u32,
i64 = u64,
i128 = u128,
isize = usize
);

// If usize is 32 bits, use the rng_core! impl
#[cfg(target_pointer_width = "32")]
rng_core!(usize);

// If usize is 64 bits, use the rng_core_large! impl
#[cfg(target_pointer_width = "64")]
rng_core_large!(usize);

// rand_core
#[cfg(feature = "rand")]
impl rand_core::RngCore for Rng {
/// Generate a random u32
/// Panics if RNG fails.
fn next_u32(&mut self) -> u32 {
self.gen().unwrap()
}

/// Generate a random u64
/// Panics if RNG fails.
fn next_u64(&mut self) -> u64 {
self.gen().unwrap()
}

/// Fill a slice with random data.
/// Panics if RNG fails.
fn fill_bytes(&mut self, dest: &mut [u8]) {
self.fill(dest).unwrap()
}

/// Try to fill a slice with random data. Return an error if RNG fails.
fn try_fill_bytes(
&mut self,
dest: &mut [u8],
) -> Result<(), rand_core::Error> {
self.fill(dest).map_err(|e| {
core::num::NonZeroU32::new(
rand_core::Error::CUSTOM_START + e as u32,
)
// This should never fail as long as no enum variant is equal to 0
.expect("Internal hal error")
.into()
})
}
}

#[cfg(feature = "rand")]
impl rand_core::CryptoRng for Rng {}