use super::*;
use std::collections::VecDeque;
use std::ptr::null_mut;
use std::thread::JoinHandle;
pub type ArbCmdQueue = VecDeque<ArbCmd>;
pub type QubitReferenceSet = VecDeque<QubitRef>;
pub type QubitMeasurementResultSet = HashMap<QubitRef, QubitMeasurementResult>;
pub type PluginJoinHandle = JoinHandle<Result<()>>;
pub type BoxedPluginConfiguration = Box<dyn PluginConfiguration>;
macro_rules! api_object_types_convert {
($($(#[$m:meta])* $i:ident,)+) => (
$(
impl From<$i> for APIObject {
fn from(x: $i) -> APIObject {
APIObject::$i(x)
}
}
)+
)
}
#[derive(Debug)]
#[allow(clippy::large_enum_variant)]
pub enum APIObject {
ArbData(ArbData),
ArbCmd(ArbCmd),
ArbCmdQueue(ArbCmdQueue),
QubitReferenceSet(QubitReferenceSet),
Gate(Gate),
QubitMeasurementResult(QubitMeasurementResult),
QubitMeasurementResultSet(QubitMeasurementResultSet),
Matrix(Matrix),
GateMap(GateMap),
PluginProcessConfiguration(PluginProcessConfiguration),
PluginThreadConfiguration(PluginThreadConfiguration),
SimulatorConfiguration(SimulatorConfiguration),
Simulator(Simulator),
PluginDefinition(PluginDefinition),
PluginJoinHandle(PluginJoinHandle),
}
api_object_types_convert!(
ArbData,
ArbCmd,
ArbCmdQueue,
QubitReferenceSet,
Gate,
QubitMeasurementResult,
QubitMeasurementResultSet,
Matrix,
GateMap,
PluginProcessConfiguration,
PluginThreadConfiguration,
SimulatorConfiguration,
Simulator,
PluginDefinition,
PluginJoinHandle,
);
pub struct APIState {
pub objects: HashMap<dqcs_handle_t, APIObject>,
pub handle_counter: dqcs_handle_t,
pub thread_locals_used_by: Option<dqcs_handle_t>,
pub last_error: Option<CString>,
}
impl APIState {
pub fn push(&mut self, object: APIObject) -> dqcs_handle_t {
let handle = self.handle_counter;
self.objects.insert(handle, object);
self.handle_counter = handle + 1;
handle
}
pub fn thread_locals_claim(&mut self, handle: dqcs_handle_t) -> Result<()> {
self.thread_locals_assert_free()?;
self.thread_locals_used_by.replace(handle);
Ok(())
}
pub fn thread_locals_claimed(&self) -> bool {
if let Some(h) = self.thread_locals_used_by {
self.objects.contains_key(&h)
} else {
false
}
}
pub fn thread_locals_assert_free(&self) -> Result<()> {
if self.thread_locals_claimed() {
inv_op(format!(
"cannot claim DQCsim thread-local storage; already claimed by handle {}",
self.thread_locals_used_by.unwrap()
))
} else {
Ok(())
}
}
}
impl Drop for APIState {
fn drop(&mut self) {
let mut warn = false;
for (_, v) in self.objects.drain() {
if let APIObject::Simulator(_) = v {
warn = true;
std::mem::forget(v);
}
}
if warn {
eprintln!(
"DQCsim API error: you've leaked one or more Simulator objects! \
You should always call dqcs_handle_delete() on simulator objects or call \
dqcs_handle_delete_all() to delete all handles before exiting, otherwise \
things are not destroyed in the right order."
);
}
}
}
thread_local! {
pub static API_STATE: RefCell<APIState> = RefCell::new(APIState {
objects: HashMap::new(),
handle_counter: 1,
thread_locals_used_by: None,
last_error: None,
});
}
pub fn api_return<T>(error_value: T, call: impl FnOnce() -> Result<T>) -> T {
match call() {
Ok(x) => x,
Err(e) => {
API_STATE.with(|state| {
state.borrow_mut().last_error.replace(
CString::new(e.to_string())
.unwrap_or_else(|_| CString::new("<UNKNOWN>").unwrap()),
)
});
error_value
}
}
}
pub fn api_return_none(call: impl FnOnce() -> Result<()>) -> dqcs_return_t {
api_return(dqcs_return_t::DQCS_FAILURE, || {
call().map(|()| dqcs_return_t::DQCS_SUCCESS)
})
}
pub fn api_return_bool(call: impl FnOnce() -> Result<bool>) -> dqcs_bool_return_t {
api_return(dqcs_bool_return_t::DQCS_BOOL_FAILURE, || {
call().map(dqcs_bool_return_t::from)
})
}
pub fn api_return_string(call: impl FnOnce() -> Result<String>) -> *mut c_char {
api_return(null_mut(), || {
call().and_then(|s| {
let s = CString::new(&s[..])?;
let s = unsafe { strdup(s.as_ptr()) as *mut c_char };
if s.is_null() {
err("failed to allocate return value")
} else {
Ok(s)
}
})
})
}
pub fn cb_return<T>(error_value: T, actual_value: T) -> Result<T>
where
T: std::cmp::PartialEq,
{
if actual_value != error_value {
Ok(actual_value)
} else {
API_STATE.with(|state| {
let state = state.borrow();
if let Some(e) = state.last_error.as_ref() {
err(e
.clone()
.into_string()
.unwrap_or_else(|_| "Unknown error".to_string()))
} else {
err("Unknown error")
}
})
}
}
pub fn cb_return_none(actual_value: dqcs_return_t) -> Result<()> {
cb_return(dqcs_return_t::DQCS_FAILURE, actual_value).map(|_| ())
}
pub fn cb_return_bool(actual_value: dqcs_bool_return_t) -> Result<bool> {
cb_return(dqcs_bool_return_t::DQCS_BOOL_FAILURE, actual_value).map(|b| match b {
dqcs_bool_return_t::DQCS_FALSE => false,
dqcs_bool_return_t::DQCS_TRUE => true,
_ => unreachable!(),
})
}
pub struct ResolvedHandle {
ob: Option<APIObject>,
handle: dqcs_handle_t,
}
impl Drop for ResolvedHandle {
fn drop(&mut self) {
if let Some(ob) = self.ob.take() {
API_STATE.with(|state| state.borrow_mut().objects.insert(self.handle, ob));
}
}
}
pub trait UseHandleAs<T> {
fn as_ref(&self) -> Result<&T>;
fn as_mut(&mut self) -> Result<&mut T>;
fn take(&mut self) -> Result<T>;
}
impl UseHandleAs<APIObject> for ResolvedHandle {
fn as_ref(&self) -> Result<&APIObject> {
Ok(self
.ob
.as_ref()
.expect("object ownership was already given away"))
}
fn as_mut(&mut self) -> Result<&mut APIObject> {
Ok(self
.ob
.as_mut()
.expect("object ownership was already given away"))
}
fn take(&mut self) -> Result<APIObject> {
Ok(self
.ob
.take()
.expect("object ownership was already given away"))
}
}
macro_rules! mutate_api_object_as {
{$x:ty, $y:ident: $($p:pat=>$r:expr,$m:expr,$t:expr,)+} => (
impl UseHandleAs<$x> for ResolvedHandle {
#[allow(unreachable_code, unused_variables)]
fn as_ref(&self) -> Result<&$x> {
match self.ob.as_ref().expect("object ownership was already given away") {
$($p => Ok($r),)+
_ => inv_arg(format!("object does not support the {} interface", stringify!($y))),
}
}
#[allow(unreachable_code, unused_variables)]
fn as_mut(&mut self) -> Result<&mut $x> {
match self.ob.as_mut().expect("object ownership was already given away") {
$($p => Ok($m),)+
_ => inv_arg(format!("object does not support the {} interface", stringify!($y))),
}
}
#[allow(unreachable_code, unused_variables)]
fn take(&mut self) -> Result<$x> {
match self.ob.take().expect("object ownership was already given away") {
$($p => Ok($t),)+
x => {
self.ob.replace(x);
inv_arg(format!("object does not support the {} interface", stringify!($y)))
}
}
}
}
)
}
mutate_api_object_as! {ArbData, arb:
APIObject::ArbData(x) => x, x, x,
APIObject::ArbCmd(x) => x.data(), x.data_mut(), x.into(),
APIObject::ArbCmdQueue(x) => {
if let Some(x) = x.front() {
x.data()
} else {
return inv_arg("empty command queue does not support arb interface");
}
}, {
if let Some(x) = x.front_mut() {
x.data_mut()
} else {
return inv_arg("empty command queue does not support arb interface");
}
}, {
let mut x = x;
if let Some(x) = x.pop_front() {
x.into()
} else {
return inv_arg("empty command queue does not support arb interface");
}
},
APIObject::Gate(x) => &x.data, &mut x.data, x.data,
APIObject::QubitMeasurementResult(x) => &x.data, &mut x.data, x.data,
}
mutate_api_object_as! {ArbCmd, cmd:
APIObject::ArbCmd(x) => x, x, x,
APIObject::ArbCmdQueue(x) => {
if let Some(x) = x.front() {
x
} else {
return inv_arg("empty command queue does not support cmd interface");
}
}, {
if let Some(x) = x.front_mut() {
x
} else {
return inv_arg("empty command queue does not support cmd interface");
}
}, {
let mut x = x;
if let Some(x) = x.pop_front() {
x
} else {
return inv_arg("empty command queue does not support cmd interface");
}
},
}
mutate_api_object_as! {ArbCmdQueue, cq:
APIObject::ArbCmdQueue(x) => x, x, x,
}
mutate_api_object_as! {QubitReferenceSet, qbset:
APIObject::QubitReferenceSet(x) => x, x, x,
}
mutate_api_object_as! {Gate, gate:
APIObject::Gate(x) => x, x, x,
}
mutate_api_object_as! {Matrix, mat:
APIObject::Matrix(x) => x, x, x,
}
mutate_api_object_as! {GateMap, gm:
APIObject::GateMap(x) => x, x, x,
}
mutate_api_object_as! {QubitMeasurementResult, meas:
APIObject::QubitMeasurementResult(x) => x, x, x,
}
mutate_api_object_as! {QubitMeasurementResultSet, mset:
APIObject::QubitMeasurementResult(x) => {
return inv_arg("handle does not support the mset interface");
}, {
return inv_arg("handle does not support the mset interface");
}, {
let mut s = QubitMeasurementResultSet::new();
s.insert(x.qubit, x);
s
},
APIObject::QubitMeasurementResultSet(x) => x, x, x,
}
mutate_api_object_as! {PluginProcessConfiguration, pcfg:
APIObject::PluginProcessConfiguration(x) => x, x, x,
}
mutate_api_object_as! {PluginThreadConfiguration, tcfg:
APIObject::PluginThreadConfiguration(x) => x, x, x,
}
mutate_api_object_as! {BoxedPluginConfiguration, xcfg:
APIObject::PluginProcessConfiguration(x) => panic!(), panic!(), Box::new(x),
APIObject::PluginThreadConfiguration(x) => panic!(), panic!(), Box::new(x),
}
mutate_api_object_as! {SimulatorConfiguration, scfg:
APIObject::SimulatorConfiguration(x) => x, x, x,
}
mutate_api_object_as! {Simulator, sim:
APIObject::Simulator(x) => x, x, x,
}
mutate_api_object_as! {PluginDefinition, scfg:
APIObject::PluginDefinition(x) => x, x, x,
}
mutate_api_object_as! {PluginJoinHandle, pjoin:
APIObject::PluginJoinHandle(x) => x, x, x,
}
pub fn resolve(handle: dqcs_handle_t) -> Result<ResolvedHandle> {
if let Some(ob) = API_STATE.with(|state| state.borrow_mut().objects.remove(&handle)) {
Ok(ResolvedHandle {
ob: Some(ob),
handle,
})
} else {
inv_arg(format!("handle {} is invalid", handle))
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! resolve {
($i:ident as &$t:ty) => {
let $i = resolve($i)?;
let $i: &$t = $i.as_ref()?;
};
($i:ident as &mut $t:ty) => {
let mut $i = resolve($i)?;
let $i: &mut $t = $i.as_mut()?;
};
($i:ident as pending $t:ty) => {
let mut $i = resolve($i)?;
let _: &mut $t = $i.as_mut()?;
};
(optional $i:ident as &$t:ty) => {
let $i = resolve($i).ok();
let $i: Option<&$t> = if let Some($i) = $i.as_ref() {
Some($i.as_ref()?)
} else {
None
};
};
(optional $i:ident as &mut $t:ty) => {
let mut $i = resolve($i).ok();
let $i: Option<&$t> = if let Some($i) = $i.as_mut() {
Some($i.as_ref()?)
} else {
None
};
};
(optional $i:ident as pending $t:ty) => {
let mut $i = resolve($i).ok();
if let Some($i) = $i.as_mut() {
let _: &mut $t = $i.as_mut()?;
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! take {
($i:ident as $t:ty) => {
let mut $i = resolve($i)?;
let $i: $t = $i.take()?;
};
($i:ident as mut $t:ty) => {
let mut $i = resolve($i)?;
let mut $i: $t = $i.take()?;
};
(resolved $i:ident as $t:ty) => {
let $i: $t = $i.take().unwrap();
};
(resolved $i:ident as mut $t:ty) => {
let mut $i: $t = $i.take().unwrap();
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! clone {
($clone:ident: $t:ty = resolved $i:ident) => {
let $clone: &$t = $i.as_ref().unwrap();
let $clone = $clone.clone();
};
(mut $clone:ident: $t:ty = resolved $i:ident) => {
let $clone: &$t = $i.as_ref().unwrap();
let mut $clone = $clone.clone();
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! delete {
($i:ident) => {
let $i = resolve($i);
if let Ok(mut $i) = $i {
let _: APIObject = $i.take().unwrap();
}
};
(resolved $i:ident) => {
let _: APIObject = $i.take().unwrap();
};
}
pub fn insert(ob: impl Into<APIObject>) -> dqcs_handle_t {
API_STATE.with(|state| {
let mut state = state.borrow_mut();
state.push(ob.into())
})
}