use core::ffi::{c_char, c_void};
use std::ffi::CString;
use crate::casc::{FileIdHint, Storage};
use crate::support::RawCString;
extern "C" {
fn whiteout_casc_shim_openOnline(
product: *const c_char,
region: *const c_char,
build_key: *const c_char,
http: *mut c_void,
cache_dir: *const c_char,
locale_mask: u32,
pool: *mut c_void,
) -> *mut c_void;
fn whiteout_casc_shim_readBatch(
self_: *const c_void,
paths: *const *const c_char,
file_data_ids: *const i32,
hints: *const i32,
count: usize,
) -> *mut c_void;
fn whiteout_casc_shim_readBatch_count(snapshot: *mut c_void) -> usize;
fn whiteout_casc_shim_readBatch_data_at(
snapshot: *mut c_void,
index: usize,
) -> crate::support::RawBytes;
fn whiteout_casc_shim_readBatch_success_at(snapshot: *mut c_void, index: usize) -> i32;
fn whiteout_casc_shim_readBatch_error_at(snapshot: *mut c_void, index: usize) -> RawCString;
fn whiteout_casc_shim_readBatch_free(snapshot: *mut c_void);
}
pub trait AsHttpHandler {
fn as_http_ptr(&self) -> *mut c_void;
}
impl AsHttpHandler for crate::interfaces::HostHttpHandler {
fn as_http_ptr(&self) -> *mut c_void {
self.as_ptr()
}
}
impl AsHttpHandler for crate::host::SimpleHttpHandler {
fn as_http_ptr(&self) -> *mut c_void {
self.raw.as_ptr() as *mut c_void
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum BatchReadRequest {
Path(String),
FileId {
id: i32,
hint: FileIdHint,
},
}
impl BatchReadRequest {
pub fn path(path: impl Into<String>) -> Self {
BatchReadRequest::Path(path.into())
}
pub fn file_id(id: i32) -> Self {
BatchReadRequest::FileId {
id,
hint: FileIdHint::None,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BatchReadResult {
pub data: Option<Vec<u8>>,
pub error: String,
}
impl BatchReadResult {
pub fn is_ok(&self) -> bool {
self.data.is_some()
}
}
impl Storage {
pub fn open_online(
product: &str,
region: &str,
http: &dyn AsHttpHandler,
build_key: Option<&str>,
cache_dir: Option<&str>,
locale_mask: u32,
pool: Option<&crate::interfaces::HostWorkerPool>,
) -> Option<Storage> {
let product_cstr = CString::new(product).unwrap_or_default();
let region_cstr = CString::new(if region.is_empty() { "us" } else { region })
.unwrap_or_default();
let build_key_cstr = CString::new(build_key.unwrap_or("")).unwrap_or_default();
let cache_dir_cstr = CString::new(cache_dir.unwrap_or("")).unwrap_or_default();
unsafe {
Storage::from_raw(whiteout_casc_shim_openOnline(
product_cstr.as_ptr(),
region_cstr.as_ptr(),
build_key_cstr.as_ptr(),
http.as_http_ptr(),
cache_dir_cstr.as_ptr(),
locale_mask,
pool.map_or(core::ptr::null_mut(), |p| p.as_ptr()),
) as *mut _)
}
}
pub fn read_batch(&self, requests: &[BatchReadRequest]) -> Vec<BatchReadResult> {
if requests.is_empty() {
return Vec::new();
}
let mut owned: Vec<Option<CString>> = Vec::with_capacity(requests.len());
let mut ids: Vec<i32> = Vec::with_capacity(requests.len());
let mut hints: Vec<i32> = Vec::with_capacity(requests.len());
for r in requests {
match r {
BatchReadRequest::Path(p) => {
owned.push(Some(CString::new(p.as_str()).unwrap_or_default()));
ids.push(-1);
hints.push(0);
}
BatchReadRequest::FileId { id, hint } => {
owned.push(None);
ids.push(*id);
hints.push(*hint as i32);
}
}
}
let ptrs: Vec<*const c_char> = owned
.iter()
.map(|o| o.as_ref().map_or(core::ptr::null(), |c| c.as_ptr()))
.collect();
unsafe {
let snap = whiteout_casc_shim_readBatch(
self.raw.as_ptr() as *const c_void,
ptrs.as_ptr(),
ids.as_ptr(),
hints.as_ptr(),
requests.len(),
);
if snap.is_null() {
return Vec::new();
}
let n = whiteout_casc_shim_readBatch_count(snap);
let mut out = Vec::with_capacity(n);
for i in 0..n {
let ok = whiteout_casc_shim_readBatch_success_at(snap, i) != 0;
let data = if ok {
let raw = whiteout_casc_shim_readBatch_data_at(snap, i);
if raw.data.is_null() {
Some(Vec::new())
} else {
Some(core::slice::from_raw_parts(raw.data, raw.size).to_vec())
}
} else {
None
};
let raw_err = whiteout_casc_shim_readBatch_error_at(snap, i);
let error = if raw_err.chars.is_null() {
String::new()
} else {
String::from_utf8_lossy(core::slice::from_raw_parts(
raw_err.chars as *const u8,
raw_err.length,
))
.into_owned()
};
out.push(BatchReadResult { data, error });
}
whiteout_casc_shim_readBatch_free(snap);
out
}
}
}