Skip to content
This repository was archived by the owner on May 9, 2022. It is now read-only.

Commit 7b17e08

Browse files
longtomjrPiDelport
andcommitted
feat(exec_enclave): implement median sample function
Co-authored-by: Pi Delport <[email protected]>
1 parent 2dbba24 commit 7b17e08

File tree

2 files changed

+61
-3
lines changed

2 files changed

+61
-3
lines changed

rtc_exec_enclave/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![crate_type = "staticlib"]
22
#![no_std]
3+
#![feature(array_chunks)]
34
#![deny(unsafe_op_in_unsafe_fn)]
45
#![deny(clippy::mem_forget)]
56

rtc_exec_enclave/src/sample_functions.rs

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
use crate::types::*;
1+
use core::cmp::Ordering;
2+
use core::mem::size_of;
23
use core::slice;
4+
use std::boxed::Box;
5+
use std::vec::Vec;
6+
7+
use crate::types::*;
38

49
pub(crate) trait SampleExecModule {
510
fn call_safe(&self, dataset: &[u8]) -> ExecResult;
@@ -31,7 +36,59 @@ pub(crate) const MEDIAN_MODULE_ID: [u8; 32] = [2u8; 32];
3136
pub(crate) struct MedianModule;
3237

3338
impl SampleExecModule for MedianModule {
34-
fn call_safe(&self, _dataset: &[u8]) -> ExecResult {
35-
todo!()
39+
fn call_safe(&self, dataset: &[u8]) -> ExecResult {
40+
let mut float_dataset: Vec<f64> = if dataset.len() % size_of::<f64>() == 0 {
41+
// Safety: dataset.len() should be aligned with the size of f64,
42+
// so the array_chucks iterator should have no remainder().
43+
dataset
44+
.array_chunks()
45+
.map(|x| f64::from_ne_bytes(*x))
46+
.collect()
47+
} else {
48+
// Bail out: dataset.len() is not a multiple of the size of f64.
49+
return Err(());
50+
};
51+
52+
let median = median(&mut float_dataset).ok_or(())?;
53+
let median_bytes = median.to_ne_bytes();
54+
55+
Ok(Box::new(median_bytes))
3656
}
3757
}
58+
59+
fn median(data: &mut [f64]) -> Option<f64> {
60+
let len = data.len();
61+
// If len is 0 we cannot calculate a median
62+
if len == 0 {
63+
return None;
64+
};
65+
66+
// No well-defined median if data contains infinities or NaN.
67+
// TODO: Consider something like <https://crates.io/crates/ordered-float>?
68+
if !data.iter().all(|n| n.is_finite()) {
69+
return None;
70+
}
71+
72+
let mid = len / 2;
73+
74+
// Safety: is_finite checked above
75+
let (less, &mut m1, _) = data.select_nth_unstable_by(mid, |a, b| unsafe { finite_cmp(a, b) });
76+
77+
let median = if len % 2 == 1 {
78+
m1
79+
} else {
80+
// Safety: is_finite checked above
81+
let (_, &mut m2, _) =
82+
less.select_nth_unstable_by(mid - 1, |a, b| unsafe { finite_cmp(a, b) });
83+
(m1 + m2) / 2.0
84+
};
85+
Some(median)
86+
}
87+
88+
/// Compare finite floats.
89+
/// # Safety
90+
/// Caller must ensure values are finite (or at least not NaN).
91+
unsafe fn finite_cmp(a: &f64, b: &f64) -> Ordering {
92+
a.partial_cmp(b)
93+
.unwrap_or_else(|| panic!("finite_cmp({:?}, {:?}): not comparable", a, b))
94+
}

0 commit comments

Comments
 (0)