use std::{cmp::Ordering, ffi::c_char};
use crate::ffi::{
any::{AnyObject, CallbackFn, Downcast, wrap_func},
util::{ExtrinsicObject, c_bool},
};
use opendp_derive::bootstrap;
use crate::{
core::{FfiResult, Measure},
domains::ffi::ExtrinsicElement,
error::Fallible,
ffi::{
any::AnyMeasure,
util::{self, into_c_char_p},
},
measures::{Approximate, MaxDivergence, ZeroConcentratedDivergence},
traits::ProductOrd,
};
use super::{PrivacyProfile, RenyiDivergence, SmoothedMaxDivergence};
#[bootstrap(
name = "_measure_free",
arguments(this(do_not_convert = true)),
returns(c_type = "FfiResult<void *>")
)]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_measures___measure_free(this: *mut AnyMeasure) -> FfiResult<*mut ()> {
util::into_owned(this).map(|_| ()).into()
}
#[bootstrap(
name = "_measure_equal",
returns(c_type = "FfiResult<bool *>", hint = "bool")
)]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_measures___measure_equal(
left: *mut AnyMeasure,
right: *const AnyMeasure,
) -> 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 = "measure_debug",
arguments(this(rust_type = b"null")),
returns(c_type = "FfiResult<char *>")
)]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_measures__measure_debug(this: *mut AnyMeasure) -> FfiResult<*mut c_char> {
let this = try_as_ref!(this);
FfiResult::Ok(try_!(into_c_char_p(format!("{:?}", this))))
}
#[bootstrap(
name = "measure_type",
arguments(this(rust_type = b"null")),
returns(c_type = "FfiResult<char *>")
)]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_measures__measure_type(this: *mut AnyMeasure) -> FfiResult<*mut c_char> {
let this = try_as_ref!(this);
FfiResult::Ok(try_!(into_c_char_p(this.type_.descriptor.to_string())))
}
#[bootstrap(
name = "measure_distance_type",
arguments(this(rust_type = b"null")),
returns(c_type = "FfiResult<char *>")
)]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_measures__measure_distance_type(
this: *mut AnyMeasure,
) -> FfiResult<*mut c_char> {
let this = try_as_ref!(this);
FfiResult::Ok(try_!(into_c_char_p(
this.distance_type.descriptor.to_string()
)))
}
#[bootstrap(name = "max_divergence")]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_measures__max_divergence() -> FfiResult<*mut AnyMeasure> {
Ok(AnyMeasure::new(MaxDivergence)).into()
}
#[bootstrap(name = "smoothed_max_divergence")]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_measures__smoothed_max_divergence() -> FfiResult<*mut AnyMeasure> {
Ok(AnyMeasure::new(SmoothedMaxDivergence)).into()
}
#[bootstrap(name = "fixed_smoothed_max_divergence")]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_measures__fixed_smoothed_max_divergence() -> FfiResult<*mut AnyMeasure> {
Ok(AnyMeasure::new(Approximate(MaxDivergence))).into()
}
#[bootstrap(
rust_path = "measures/struct.Approximate",
generics(M(suppress)),
arguments(measure(c_type = "AnyMeasure *", rust_type = b"null")),
returns(c_type = "FfiResult<AnyMeasure *>", hint = "ApproximateDivergence")
)]
fn approximate<M: Measure>(measure: M) -> Approximate<M> {
Approximate(measure)
}
#[unsafe(no_mangle)]
pub extern "C" fn opendp_measures__approximate(
measure: *const AnyMeasure,
) -> FfiResult<*mut AnyMeasure> {
fn monomorphize<MO: 'static + Measure>(measure: &AnyMeasure) -> Fallible<AnyMeasure> {
let measure = measure.downcast_ref::<MO>()?.clone();
Ok(AnyMeasure::new(approximate(measure)))
}
let measure = try_as_ref!(measure);
let MO = measure.type_.clone();
dispatch!(
monomorphize,
[(
MO,
[
MaxDivergence,
SmoothedMaxDivergence,
ZeroConcentratedDivergence,
ExtrinsicDivergence
]
)],
(measure)
)
.into()
}
#[bootstrap(name = "_approximate_divergence_get_inner_measure")]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_measures___approximate_divergence_get_inner_measure(
privacy_measure: *const AnyMeasure,
) -> FfiResult<*mut AnyMeasure> {
let privacy_measure = try_as_ref!(privacy_measure);
let M = privacy_measure.type_.clone();
let T = try_!(M.get_atom());
fn monomorphize<M: 'static + Measure>(privacy_measure: &AnyMeasure) -> Fallible<AnyMeasure> {
let privacy_measure = privacy_measure.downcast_ref::<Approximate<M>>()?.clone();
Ok(AnyMeasure::new(privacy_measure.0.clone()))
}
dispatch!(
monomorphize,
[(
T,
[
MaxDivergence,
SmoothedMaxDivergence,
ZeroConcentratedDivergence,
ExtrinsicDivergence
]
)],
(privacy_measure)
)
.into()
}
#[bootstrap(name = "zero_concentrated_divergence")]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_measures__zero_concentrated_divergence() -> FfiResult<*mut AnyMeasure> {
Ok(AnyMeasure::new(ZeroConcentratedDivergence)).into()
}
#[bootstrap(name = "renyi_divergence")]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_measures__renyi_divergence() -> FfiResult<*mut AnyMeasure> {
Ok(AnyMeasure::new(RenyiDivergence)).into()
}
#[derive(Clone)]
pub struct ExtrinsicDivergence {
pub element: ExtrinsicElement,
}
impl Default for ExtrinsicDivergence {
fn default() -> Self {
Self {
element: ExtrinsicElement::new(
"UserDivergence".to_string(),
ExtrinsicObject(std::ptr::null()),
),
}
}
}
impl std::fmt::Debug for ExtrinsicDivergence {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.element)
}
}
impl PartialEq for ExtrinsicDivergence {
fn eq(&self, other: &Self) -> bool {
self.element
.value
.total_cmp(&other.element.value)
.map(|ordering| ordering == Ordering::Equal)
.unwrap_or(false)
}
}
impl Measure for ExtrinsicDivergence {
type Distance = ExtrinsicObject;
}
#[bootstrap(
name = "user_divergence",
features("honest-but-curious"),
arguments(
identifier(c_type = "char *", rust_type = b"null"),
descriptor(default = b"null", rust_type = "ExtrinsicObject")
)
)]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_measures__user_divergence(
identifier: *mut c_char,
descriptor: *mut ExtrinsicObject,
) -> FfiResult<*mut AnyMeasure> {
let value = try_as_ref!(descriptor).clone();
let identifier = try_!(crate::ffi::util::to_str(identifier)).to_string();
let element = ExtrinsicElement::new(identifier, value);
Ok(AnyMeasure::new(ExtrinsicDivergence { element })).into()
}
#[bootstrap(
name = "_extrinsic_measure_descriptor",
arguments(measure(rust_type = b"null")),
returns(c_type = "FfiResult<ExtrinsicObject *>")
)]
#[unsafe(no_mangle)]
pub extern "C" fn opendp_measures___extrinsic_measure_descriptor(
measure: *mut AnyMeasure,
) -> FfiResult<*mut ExtrinsicObject> {
let measure = try_!(try_as_ref!(measure).downcast_ref::<ExtrinsicDivergence>()).clone();
FfiResult::Ok(util::into_raw(measure.element.value.clone()))
}
#[bootstrap(
name = "new_privacy_profile",
features("contrib", "honest-but-curious"),
arguments(curve(rust_type = "f64")),
returns(rust_type = "PrivacyProfile")
)]
#[allow(dead_code)]
fn new_privacy_profile(curve: *const CallbackFn) -> Fallible<AnyObject> {
let _ = curve;
panic!("this signature only exists for code generation")
}
#[unsafe(no_mangle)]
pub extern "C" fn opendp_measures__new_privacy_profile(
curve: *const CallbackFn,
) -> FfiResult<*mut AnyObject> {
let curve = wrap_func(try_as_ref!(curve).clone());
FfiResult::Ok(AnyObject::new_raw(PrivacyProfile::new(
move |epsilon: f64| curve(&AnyObject::new(epsilon))?.downcast::<f64>(),
)))
}