use crate::records::{HealthRecord, Sampleable};
use rand::SeedableRng;
use rand::prelude::*;
use rand::rngs::SmallRng;
use serde::de;
use serde::{Deserialize, Serialize};
use std::ffi::CStr;
use std::mem;
use std::os::raw::{c_char, c_void};
use tfhe::ServerKey;
use wasip1;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub enum Platform {
IOS,
Android,
JS,
Unknown,
}
#[link(wasm_import_module = "hubro_sdk")]
unsafe extern "C" {
fn get_health_connect_records(record_type: i32, from: i32, to: i32) -> *mut c_char;
fn get_health_connect_number_of_records(record_type: i32, from: i32, to: i32) -> i32;
fn print_line(nf_name: *mut c_char);
fn get_platform() -> *mut c_char;
fn upload_server_key(ptr: *const u8, len: usize) -> i32;
fn host_load_model(path_ptr: *const u8, path_len: usize) -> i32;
fn host_run_inference(
model_id: i32,
input_ptr: *const f32,
input_len: i32,
shape_ptr: *const i32,
shape_len: i32,
output_ptr: *mut f32,
output_len: i32,
) -> i32;
}
#[unsafe(no_mangle)]
pub extern "C" fn allocate(size: usize) -> *mut c_void {
let mut buf = Vec::with_capacity(size);
let ptr = buf.as_mut_ptr();
mem::forget(buf);
return ptr as *mut c_void;
}
#[unsafe(no_mangle)]
pub extern "C" fn dealloc(ptr: *mut c_void, cap: usize) {
unsafe {
let _buf = Vec::from_raw_parts(ptr, 0, cap);
}
}
fn fetch_records_count(record_type: i32, from: i32, to: i32) -> i32 {
let mut s;
loop {
s = unsafe { get_health_connect_number_of_records(record_type, from, to) };
if s != -1 {
break;
}
}
s
}
fn fetch_records<T: de::DeserializeOwned + HealthRecord>(
record_type: i32,
from: i32,
to: i32,
) -> Vec<T> {
if fetch_records_count(record_type, from, to) > 0 {
let s = unsafe { get_health_connect_records(record_type, from, to) };
let json_data = unsafe { CStr::from_ptr(s).to_bytes().to_vec() };
let subject_str = std::str::from_utf8(&json_data).unwrap();
return serde_json::from_str::<Vec<T>>(subject_str).unwrap();
} else {
Vec::<T>::new()
}
}
pub fn get_health_records<T: de::DeserializeOwned + HealthRecord>(from: i32, to: i32) -> Vec<T> {
fetch_records::<T>(T::IDENTIFIER, from, to)
}
pub fn generate_sample_records<T: Serialize + Sampleable>(from: i32, to: i32) -> Vec<T> {
let mut seed = [0u8; 32];
if let Ok(time) = unsafe { wasip1::clock_time_get(wasip1::CLOCKID_REALTIME, 1_000) } {
let b = time.to_le_bytes();
seed[0..8].copy_from_slice(&b);
}
let _ = getrandom::getrandom(&mut seed[8..32]);
let mut rng = SmallRng::from_seed(seed);
let mut records = Vec::new();
let start_time = chrono::DateTime::from_timestamp(from as i64, 0)
.unwrap()
.naive_utc()
.and_utc();
let end_time = chrono::DateTime::from_timestamp(to as i64, 0)
.unwrap()
.naive_utc()
.and_utc();
let duration = end_time.signed_duration_since(start_time);
let days = duration.num_days();
for day in 0..days {
let base_timestamp = start_time + chrono::Duration::days(day);
let num_records = rng.random_range(3..8);
for hour in 0..num_records {
let timestamp = base_timestamp + chrono::Duration::hours(hour * 4);
let record = T::generate_sample(&mut rng, timestamp, chrono::Duration::hours(4));
records.push(record);
}
}
records
}
pub fn debug_print_line(output: &str) {
let size = output.len();
let ptr = allocate(size + 1) as *mut c_char;
unsafe {
std::ptr::copy(output.as_ptr(), ptr as *mut u8, size);
*ptr.add(size) = 0;
print_line(ptr);
}
}
pub fn get_current_platform() -> Platform {
let ptr = unsafe { get_platform() };
if ptr.is_null() {
return Platform::Unknown;
}
let s = unsafe { CStr::from_ptr(ptr).to_string_lossy().to_string() };
let s_lower = s.to_lowercase();
if s_lower.contains("ios") {
Platform::IOS
} else if s_lower.contains("android") {
Platform::Android
} else if s_lower.contains("js") {
Platform::JS
} else {
Platform::Unknown
}
}
pub fn load_model(path: &str) -> i32 {
unsafe { host_load_model(path.as_ptr(), path.len()) }
}
pub fn run_inference(model_id: i32, input: &[f32], shape: &[i32], output: &mut [f32]) -> i32 {
unsafe {
host_run_inference(
model_id,
input.as_ptr(),
input.len() as i32,
shape.as_ptr(),
shape.len() as i32,
output.as_mut_ptr(),
output.len() as i32,
)
}
}
pub fn upload_key(server_key: &ServerKey) -> i32 {
let key = serialize_keys(server_key);
unsafe { upload_server_key(key.as_ptr(), key.len()) }
}
pub fn get_current_time_nanos() -> u64 {
unsafe { wasip1::clock_time_get(wasip1::CLOCKID_REALTIME, 1_000).expect("WASI clock_time_get failed") }
}
pub fn serialize_keys(server_key: &ServerKey) -> Vec<u8> {
let config = bincode::config::standard();
bincode::serde::encode_to_vec(server_key, config)
.expect("Failed to serialize TFHE server key using Serde bridge")
}