use std::{ffi::c_char, fmt::Debug};
use opendp_derive::bootstrap;
use crate::{
core::{Domain, FfiResult, Function},
domains::{AtomDomain, MapDomain, VectorDomain, type_name},
error::Fallible,
ffi::{
any::{AnyDomain, AnyObject, CallbackFn, Downcast, wrap_func},
util::{self, ExtrinsicObject, Type, TypeContents, as_ref, c_bool, into_c_char_p, to_str},
},
traits::{CheckAtom, Float, Hashable, Integer, Primitive},
};
#[cfg(feature = "polars")]
use crate::domains::{ArrayDomain, CategoricalDomain, DatetimeDomain, EnumDomain};
use super::{BitVectorDomain, Bounds, NaN, OptionDomain};
#[bootstrap(
name = "_domain_free",
arguments(this(do_not_convert = true)),
returns(c_type = "FfiResult<void *>")
)]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_domains___domain_free(this: *mut AnyDomain) -> FfiResult<*mut ()> {
util::into_owned(this).map(|_| ()).into()
}
#[bootstrap(
name = "_member",
arguments(this(hint = "Domain"), val(rust_type = "$domain_carrier_type(this)")),
returns(c_type = "FfiResult<bool *>", hint = "bool")
)]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_domains___member(
this: *mut AnyDomain,
val: *const AnyObject,
) -> FfiResult<*mut c_bool> {
let this = try_as_ref!(this);
let val = try_as_ref!(val);
let status = try_!(this.member(val));
FfiResult::Ok(util::into_raw(util::from_bool(status)))
}
#[bootstrap(
name = "_domain_equal",
returns(c_type = "FfiResult<bool *>", hint = "bool")
)]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_domains___domain_equal(
left: *mut AnyDomain,
right: *const AnyDomain,
) -> FfiResult<*mut c_bool> {
let status = try_as_ref!(left) == try_as_ref!(right);
FfiResult::Ok(util::into_raw(util::from_bool(status)))
}
#[bootstrap(
name = "domain_debug",
arguments(this(rust_type = b"null")),
returns(c_type = "FfiResult<char *>")
)]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_domains__domain_debug(this: *mut AnyDomain) -> FfiResult<*mut c_char> {
let this = try_as_ref!(this);
FfiResult::Ok(try_!(into_c_char_p(format!("{:?}", this))))
}
#[bootstrap(
name = "domain_type",
arguments(this(rust_type = b"null")),
returns(c_type = "FfiResult<char *>")
)]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_domains__domain_type(this: *mut AnyDomain) -> FfiResult<*mut c_char> {
let this = try_as_ref!(this);
FfiResult::Ok(try_!(into_c_char_p(this.type_.descriptor.to_string())))
}
#[bootstrap(
name = "domain_carrier_type",
arguments(this(rust_type = b"null")),
returns(c_type = "FfiResult<char *>")
)]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_domains__domain_carrier_type(
this: *mut AnyDomain,
) -> FfiResult<*mut c_char> {
let this = try_as_ref!(this);
FfiResult::Ok(try_!(into_c_char_p(
this.carrier_type.descriptor.to_string()
)))
}
#[bootstrap(
rust_path = "domains/struct.AtomDomain",
arguments(
bounds(
rust_type = "Option<(T, T)>",
c_type = "AnyObject *",
default = b"null"
),
nan(rust_type = "Option<bool>", c_type = "AnyObject *", default = b"null")
),
generics(T(example = "$get_first(bounds)")),
returns(c_type = "FfiResult<AnyDomain *>", hint = "AtomDomain")
)]
fn atom_domain<T: CheckAtom>(bounds: Option<Bounds<T>>, nan: Option<NaN<T>>) -> AtomDomain<T> {
AtomDomain::<T>::new(bounds, nan)
}
#[unsafe(no_mangle)]
pub extern "C" fn opendp_domains__atom_domain(
bounds: *const AnyObject,
nan: *const AnyObject,
T: *const c_char,
) -> FfiResult<*mut AnyDomain> {
fn monomorphize_float<T: 'static + Float>(
bounds: *const AnyObject,
nan: Option<bool>,
) -> Fallible<AnyDomain> {
let bounds = if let Some(bounds) = util::as_ref(bounds) {
let tuple = *bounds.downcast_ref::<(T, T)>()?;
Some(Bounds::new_closed(tuple)?)
} else {
None
};
let nan = nan.unwrap_or(true).then_some(NaN::new());
Ok(AnyDomain::new(atom_domain::<T>(bounds, nan)))
}
fn monomorphize_integer<T: 'static + Integer>(
bounds: *const AnyObject,
nan: Option<bool>,
) -> Fallible<AnyDomain> {
let bounds = if let Some(bounds) = util::as_ref(bounds) {
let tuple = *bounds.downcast_ref::<(T, T)>()?;
Some(Bounds::new_closed(tuple)?)
} else {
None
};
if nan.unwrap_or_default() {
return fallible!(FFI, "integers cannot represent nullity");
}
Ok(AnyDomain::new(atom_domain::<T>(bounds, None)))
}
fn monomorphize_simple<T: 'static + CheckAtom>(
bounds: *const AnyObject,
nan: Option<bool>,
) -> Fallible<AnyDomain> {
if util::as_ref(bounds).is_some() {
return fallible!(FFI, "{} cannot be bounded", type_name!(T));
}
if nan.unwrap_or_default() {
return fallible!(FFI, "{} cannot be NaN", type_name!(T));
}
Ok(AnyDomain::new(atom_domain::<T>(None, None)))
}
let T_ = try_!(Type::try_from(T));
let nan = if let Some(nan) = as_ref(nan) {
Some(*try_!(nan.downcast_ref::<bool>()))
} else {
None
};
fn in_set<T>() -> Option<()> {
Some(())
}
#[cfg(feature = "polars")]
if let Some(_) = dispatch!(in_set, [(T_, [chrono::NaiveDate, chrono::NaiveTime])]) {
return dispatch!(
monomorphize_simple,
[(T_, [chrono::NaiveDate, chrono::NaiveTime])],
(bounds, nan)
)
.into();
};
if let Some(_) = dispatch!(in_set, [(T_, [f32, f64])]) {
dispatch!(monomorphize_float, [(T_, [f32, f64])], (bounds, nan))
} else if let Some(_) = dispatch!(
in_set,
[(
T_,
[
u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize
]
)]
) {
dispatch!(
monomorphize_integer,
[(
T_,
[
u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize
]
)],
(bounds, nan)
)
} else {
dispatch!(monomorphize_simple, [(T_, [bool, String])], (bounds, nan))
}
.into()
}
#[bootstrap(
arguments(domain(rust_type = b"null")),
generics(T(suppress)),
returns(c_type = "FfiResult<AnyObject *>")
)]
fn _atom_domain_get_bounds_closed<T: CheckAtom>(
domain: &AtomDomain<T>,
) -> Fallible<Option<(T, T)>> {
domain.bounds.as_ref().map(|b| b.get_closed()).transpose()
}
#[unsafe(no_mangle)]
pub extern "C" fn opendp_domains___atom_domain_get_bounds_closed(
domain: *const AnyDomain,
) -> FfiResult<*mut AnyObject> {
fn monomorphize<T: 'static + CheckAtom>(domain: &AnyDomain) -> Fallible<AnyObject> {
let domain = domain.downcast_ref::<AtomDomain<T>>()?;
Ok(AnyObject::new(
_atom_domain_get_bounds_closed(domain)?.map(AnyObject::new),
))
}
let domain = try_as_ref!(domain);
let T = try_!(domain.type_.get_atom());
dispatch!(
monomorphize,
[(T, @numbers)],
(domain)
)
.into()
}
#[bootstrap(
arguments(domain(rust_type = b"null")),
generics(T(suppress)),
returns(c_type = "FfiResult<AnyObject *>")
)]
fn _atom_domain_nan<T: CheckAtom>(domain: &AtomDomain<T>) -> Fallible<bool> {
Ok(domain.nan())
}
#[unsafe(no_mangle)]
pub extern "C" fn opendp_domains___atom_domain_nan(
domain: *const AnyDomain,
) -> FfiResult<*mut AnyObject> {
fn monomorphize<T: 'static + CheckAtom>(domain: &AnyDomain) -> Fallible<AnyObject> {
let domain = domain.downcast_ref::<AtomDomain<T>>()?;
_atom_domain_nan(domain).map(AnyObject::new)
}
let domain = try_as_ref!(domain);
let T = try_!(domain.type_.get_atom());
dispatch!(
monomorphize,
[(T, @primitives)],
(domain)
)
.into()
}
#[bootstrap(
rust_path = "domains/struct.OptionDomain",
arguments(element_domain(c_type = "AnyDomain *")),
generics(D(example = "element_domain")),
returns(c_type = "FfiResult<AnyDomain *>", hint = "OptionDomain")
)]
fn option_domain<D: Domain>(element_domain: D) -> OptionDomain<D> {
OptionDomain::<D>::new(element_domain)
}
#[unsafe(no_mangle)]
pub extern "C" fn opendp_domains__option_domain(
element_domain: *const AnyDomain,
D: *const c_char,
) -> FfiResult<*mut AnyDomain> {
fn monomorphize_atom<T: 'static + CheckAtom>(
element_domain: &AnyDomain,
) -> Fallible<AnyDomain> {
let element_domain = element_domain.downcast_ref::<AtomDomain<T>>()?.clone();
Ok(AnyDomain::new(option_domain(element_domain)))
}
let element_domain = try_as_ref!(element_domain);
let D = try_!(Type::try_from(D));
let T = try_!(D.get_atom());
#[cfg(feature = "polars")]
if D == Type::of::<CategoricalDomain>() {
let element_domain = try_!(element_domain.downcast_ref::<CategoricalDomain>()).clone();
return Ok(AnyDomain::new(option_domain(element_domain))).into();
}
#[cfg(feature = "polars")]
if D == Type::of::<EnumDomain>() {
let element_domain = try_!(element_domain.downcast_ref::<EnumDomain>()).clone();
return Ok(AnyDomain::new(option_domain(element_domain))).into();
}
#[cfg(feature = "polars")]
if D == Type::of::<ArrayDomain>() {
let element_domain = try_!(element_domain.downcast_ref::<ArrayDomain>()).clone();
return Ok(AnyDomain::new(option_domain(element_domain))).into();
}
#[cfg(feature = "polars")]
if D == Type::of::<DatetimeDomain>() {
let element_domain = try_!(element_domain.downcast_ref::<DatetimeDomain>()).clone();
return Ok(AnyDomain::new(option_domain(element_domain))).into();
}
#[cfg(feature = "polars")]
if T == Type::of::<chrono::NaiveDate>() {
return monomorphize_atom::<chrono::NaiveDate>(element_domain).into();
}
#[cfg(feature = "polars")]
if T == Type::of::<chrono::NaiveTime>() {
return monomorphize_atom::<chrono::NaiveTime>(element_domain).into();
}
dispatch!(
monomorphize_atom,
[(
T,
[
u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize, f32, f64, bool,
String
]
)],
(element_domain)
)
.into()
}
#[bootstrap(name = "_option_domain_get_element_domain")]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_domains___option_domain_get_element_domain(
option_domain: *const AnyDomain,
) -> FfiResult<*mut AnyDomain> {
fn monomorphize_atom<T: 'static + CheckAtom>(option_domain: &AnyDomain) -> Fallible<AnyDomain> {
let option_domain = option_domain
.downcast_ref::<OptionDomain<AtomDomain<T>>>()?
.clone();
Ok(AnyDomain::new(option_domain.element_domain.clone()))
}
let option_domain = try_as_ref!(option_domain);
let D = option_domain.type_.clone();
let T = try_!(D.get_atom());
#[cfg(feature = "polars")]
if T == Type::of::<CategoricalDomain>() {
let option_domain =
try_!(option_domain.downcast_ref::<OptionDomain<CategoricalDomain>>()).clone();
return Ok(AnyDomain::new(option_domain.element_domain)).into();
}
#[cfg(feature = "polars")]
if T == Type::of::<DatetimeDomain>() {
let option_domain =
try_!(option_domain.downcast_ref::<OptionDomain<DatetimeDomain>>()).clone();
return Ok(AnyDomain::new(option_domain.element_domain)).into();
}
#[cfg(feature = "polars")]
if T == Type::of::<chrono::NaiveDate>() {
return monomorphize_atom::<chrono::NaiveDate>(option_domain).into();
}
#[cfg(feature = "polars")]
if T == Type::of::<chrono::NaiveTime>() {
return monomorphize_atom::<chrono::NaiveTime>(option_domain).into();
}
dispatch!(
monomorphize_atom,
[(
T,
[
u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize, f32, f64, bool,
String
]
)],
(option_domain)
)
.into()
}
#[bootstrap(
name = "vector_domain",
arguments(
atom_domain(c_type = "AnyDomain *", rust_type = b"null"),
size(rust_type = "Option<i32>", default = b"null")
),
returns(c_type = "FfiResult<AnyDomain *>", hint = "VectorDomain")
)]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_domains__vector_domain(
atom_domain: *const AnyDomain,
size: *const AnyObject,
) -> FfiResult<*mut AnyDomain> {
fn monomorphize_all<T: 'static + CheckAtom>(
atom_domain: &AnyDomain,
size: *const AnyObject,
) -> Fallible<AnyDomain> {
let atom_domain = atom_domain.downcast_ref::<AtomDomain<T>>()?.clone();
let mut vector_domain = VectorDomain::new(atom_domain);
if let Some(size) = util::as_ref(size) {
vector_domain = vector_domain.with_size(*try_!(size.downcast_ref::<i32>()) as usize)
};
Ok(AnyDomain::new(vector_domain))
}
fn monomorphize_user_domain(
user_domain: &AnyDomain,
size: *const AnyObject,
) -> Fallible<AnyDomain> {
let user_domain = user_domain.downcast_ref::<ExtrinsicDomain>()?.clone();
let mut vector_domain = VectorDomain::new(user_domain);
if let Some(size) = util::as_ref(size) {
vector_domain = vector_domain.with_size(*try_!(size.downcast_ref::<i32>()) as usize)
};
Ok(AnyDomain::new(vector_domain))
}
let atom_domain = try_as_ref!(atom_domain);
match atom_domain.type_.contents {
TypeContents::GENERIC { name: "AtomDomain", .. } =>
dispatch!(monomorphize_all, [(atom_domain.carrier_type, @primitives)], (atom_domain, size)),
TypeContents::PLAIN("ExtrinsicDomain") => monomorphize_user_domain(atom_domain, size),
_ => fallible!(FFI, "Inner domain of VectorDomain must be AtomDomain or ExtrinsicDomain (created through foreign language bindings)")
}.into()
}
#[bootstrap(name = "_vector_domain_get_element_domain")]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_domains___vector_domain_get_element_domain(
vector_domain: *const AnyDomain,
) -> FfiResult<*mut AnyDomain> {
let vector_domain = try_as_ref!(vector_domain);
let D = vector_domain.type_.clone();
let T = try_!(D.get_atom());
if T == Type::of::<ExtrinsicDomain>() {
let vector_domain =
try_!(vector_domain.downcast_ref::<VectorDomain<ExtrinsicDomain>>()).clone();
return Ok(AnyDomain::new(vector_domain.element_domain)).into();
}
fn monomorphize_atom<T: 'static + CheckAtom>(vector_domain: &AnyDomain) -> Fallible<AnyDomain> {
let option_domain = vector_domain
.downcast_ref::<VectorDomain<AtomDomain<T>>>()?
.clone();
Ok(AnyDomain::new(option_domain.element_domain.clone()))
}
dispatch!(
monomorphize_atom,
[(
T,
[
u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize, f32, f64, bool,
String
]
)],
(vector_domain)
)
.into()
}
#[bootstrap(name = "_vector_domain_get_size")]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_domains___vector_domain_get_size(
vector_domain: *const AnyDomain,
) -> FfiResult<*mut AnyObject> {
let vector_domain = try_as_ref!(vector_domain);
let D = vector_domain.type_.clone();
let T = try_!(D.get_atom());
if T == Type::of::<ExtrinsicDomain>() {
let vector_domain =
try_!(vector_domain.downcast_ref::<VectorDomain<ExtrinsicDomain>>()).clone();
return Ok(AnyObject::new(vector_domain.size.map(AnyObject::new))).into();
}
fn monomorphize_atom<T: 'static + CheckAtom>(vector_domain: &AnyDomain) -> Fallible<AnyObject> {
let option_domain = vector_domain
.downcast_ref::<VectorDomain<AtomDomain<T>>>()?
.clone();
Ok(AnyObject::new(option_domain.size.map(AnyObject::new)))
}
dispatch!(
monomorphize_atom,
[(
T,
[
u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize, f32, f64, bool,
String
]
)],
(vector_domain)
)
.into()
}
#[bootstrap(
name = "bitvector_domain",
arguments(max_weight(rust_type = "Option<u32>", default = b"null")),
returns(c_type = "FfiResult<AnyDomain *>")
)]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_domains__bitvector_domain(
max_weight: *const AnyObject,
) -> FfiResult<*mut AnyDomain> {
let mut bitvector_domain = BitVectorDomain::new();
if let Some(max_weight) = util::as_ref(max_weight) {
let max_weight = *try_!(max_weight.downcast_ref::<u32>()) as usize;
bitvector_domain = bitvector_domain.with_max_weight(max_weight)
};
Ok(AnyDomain::new(bitvector_domain)).into()
}
#[bootstrap(name = "map_domain", returns(c_type = "FfiResult<AnyDomain *>"))]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_domains__map_domain(
key_domain: *const AnyDomain,
value_domain: *const AnyDomain,
) -> FfiResult<*mut AnyDomain> {
fn monomorphize<K: Hashable, V: Primitive>(
key_domain: &AnyDomain,
value_domain: &AnyDomain,
) -> Fallible<AnyDomain> {
let key_domain = key_domain.downcast_ref::<AtomDomain<K>>()?.clone();
let value_domain = value_domain.downcast_ref::<AtomDomain<V>>()?.clone();
let map_domain = MapDomain::new(key_domain, value_domain);
Ok(AnyDomain::new(map_domain))
}
fn monomorphize_extrinsic<K: Hashable>(
key_domain: &AnyDomain,
value_domain: &AnyDomain,
) -> Fallible<AnyDomain> {
let key_domain = key_domain.downcast_ref::<AtomDomain<K>>()?.clone();
let value_domain = value_domain.downcast_ref::<ExtrinsicDomain>()?.clone();
let map_domain = MapDomain::new(key_domain, value_domain);
Ok(AnyDomain::new(map_domain))
}
let key_domain = try_as_ref!(key_domain);
let value_domain = try_as_ref!(value_domain);
match (&key_domain.type_.contents, &value_domain.type_.contents) {
(TypeContents::GENERIC { name: "AtomDomain", .. }, TypeContents::GENERIC { name: "AtomDomain", .. }) =>
dispatch!(monomorphize, [(key_domain.carrier_type, @hashable), (value_domain.carrier_type, @primitives)], (key_domain, value_domain)),
(TypeContents::GENERIC { name: "AtomDomain", .. }, TypeContents::PLAIN("ExtrinsicDomain")) =>
dispatch!(monomorphize_extrinsic, [(key_domain.carrier_type, @hashable)], (key_domain, value_domain)),
_ => fallible!(FFI, "Value domain of MapDomain must be AtomDomain or ExtrinsicDomain (created through foreign language bindings)"),
}.into()
}
#[derive(Clone)]
pub struct ExtrinsicElement {
pub identifier: String,
pub value: ExtrinsicObject,
}
impl ExtrinsicElement {
pub fn new(identifier: String, value: ExtrinsicObject) -> Self {
ExtrinsicElement { value, identifier }
}
}
impl Debug for ExtrinsicElement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.identifier)
}
}
impl PartialEq for ExtrinsicElement {
fn eq(&self, other: &Self) -> bool {
self.identifier == other.identifier
}
}
unsafe impl Send for ExtrinsicElement {}
unsafe impl Sync for ExtrinsicElement {}
#[derive(Clone)]
pub struct ExtrinsicDomain {
pub element: ExtrinsicElement,
pub member: Function<ExtrinsicObject, bool>,
}
impl std::fmt::Debug for ExtrinsicDomain {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.element)
}
}
impl PartialEq for ExtrinsicDomain {
fn eq(&self, other: &Self) -> bool {
self.element == other.element
}
}
impl Domain for ExtrinsicDomain {
type Carrier = ExtrinsicObject;
fn member(&self, val: &Self::Carrier) -> Fallible<bool> {
self.member.eval(val)
}
}
#[bootstrap(
name = "user_domain",
features("honest-but-curious"),
arguments(
identifier(c_type = "char *", rust_type = b"null"),
member(rust_type = "bool"),
descriptor(default = b"null", rust_type = "ExtrinsicObject")
)
)]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_domains__user_domain(
identifier: *mut c_char,
member: *const CallbackFn,
descriptor: *mut ExtrinsicObject,
) -> FfiResult<*mut AnyDomain> {
let identifier = try_!(to_str(identifier)).to_string();
let descriptor = try_as_ref!(descriptor).clone();
let element = ExtrinsicElement::new(identifier, descriptor);
let member_closure = wrap_func(try_as_ref!(member).clone());
let member_function = Function::new_fallible(move |arg: &ExtrinsicObject| -> Fallible<bool> {
member_closure(&AnyObject::new(arg.clone()))?.downcast::<bool>()
});
Ok(AnyDomain::new(ExtrinsicDomain {
element,
member: member_function,
}))
.into()
}
#[bootstrap(
name = "_extrinsic_domain_descriptor",
returns(c_type = "FfiResult<ExtrinsicObject *>")
)]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_domains___extrinsic_domain_descriptor(
domain: *mut AnyDomain,
) -> FfiResult<*mut ExtrinsicObject> {
let domain = try_!(try_as_ref!(domain).downcast_ref::<ExtrinsicDomain>()).clone();
FfiResult::Ok(util::into_raw(domain.element.value.clone()))
}