use crate::{strings::convert, update_counts};
use std::{mem::ManuallyDrop, rc::Rc, usize};
#[cfg(not(feature = "fail-support"))]
#[allow(improper_ctypes)]
extern "C" {
fn __quantum__rt__fail(str: *const std::ffi::CString);
}
#[cfg(feature = "fail-support")]
use crate::__quantum__rt__fail;
#[derive(Debug, Clone)]
pub struct QirArray {
pub(crate) elem_size: usize,
pub(crate) data: Vec<u8>,
}
#[no_mangle]
pub extern "C" fn __quantum__rt__array_create_1d(elem_size: u32, count: u64) -> *const QirArray {
let elem_size = elem_size
.try_into()
.expect("The `elem_size` argument should fit in the `usize` type for this platform.");
let count: usize = count
.try_into()
.expect("The `count` argument should fit in the `usize` type for this platform.");
let data = vec![0_u8; elem_size * count];
Rc::into_raw(Rc::new(QirArray { elem_size, data }))
}
#[no_mangle]
pub unsafe extern "C" fn __quantum__rt__array_copy(
arr: *const QirArray,
force: bool,
) -> *const QirArray {
let rc = ManuallyDrop::new(Rc::from_raw(arr));
if force || Rc::weak_count(&rc) > 0 {
let copy = rc.as_ref().clone();
Rc::into_raw(Rc::new(copy))
} else {
Rc::into_raw(Rc::clone(&rc));
arr
}
}
#[no_mangle]
pub unsafe extern "C" fn __quantum__rt__array_concatenate(
arr1: *const QirArray,
arr2: *const QirArray,
) -> *const QirArray {
let array1 = &*arr1;
let array2 = &*arr2;
if array1.elem_size != array2.elem_size {
__quantum__rt__fail(convert(&format!(
"Cannot concatenate arrays with differing element sizes: {} vs {}",
array1.elem_size, array2.elem_size
)));
}
let mut new_array = QirArray {
elem_size: array1.elem_size,
data: Vec::new(),
};
new_array.data.resize(array1.data.len(), 0_u8);
new_array.data.copy_from_slice(array1.data.as_slice());
let mut copy = vec![0; array2.data.len()];
copy.copy_from_slice(array2.data.as_slice());
new_array.data.append(&mut copy);
Rc::into_raw(Rc::new(new_array))
}
#[no_mangle]
pub unsafe extern "C" fn __quantum__rt__array_get_size_1d(arr: *const QirArray) -> u64 {
let array = &*arr;
let len = array.data.len() / array.elem_size;
len.try_into()
.expect("Length of array should always fit in a 64-bit integer.")
}
#[no_mangle]
pub unsafe extern "C" fn __quantum__rt__array_get_element_ptr_1d(
arr: *const QirArray,
index: u64,
) -> *mut i8 {
let array = &*arr;
let index: usize = index
.try_into()
.expect("Indices into an array should fit into the `usize` ");
array.data.as_ptr().add(array.elem_size * index) as *mut i8
}
#[no_mangle]
pub unsafe extern "C" fn __quantum__rt__array_update_reference_count(
arr: *const QirArray,
update: i32,
) {
update_counts(arr, update, false);
}
#[no_mangle]
pub unsafe extern "C" fn __quantum__rt__array_update_alias_count(
arr: *const QirArray,
update: i32,
) {
update_counts(arr, update, true);
}
#[cfg(test)]
mod tests {
use std::{convert::TryInto, mem::size_of};
use super::*;
#[test]
fn test_array_creation_simple() {
let arr = __quantum__rt__array_create_1d(1, 3);
unsafe {
assert_eq!(__quantum__rt__array_get_size_1d(arr), 3);
__quantum__rt__array_update_reference_count(arr, -1);
}
}
#[test]
fn test_array_item_zero_init() {
let arr = __quantum__rt__array_create_1d(1, 3);
unsafe {
assert_eq!(__quantum__rt__array_get_size_1d(arr), 3);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 2), 0);
__quantum__rt__array_update_reference_count(arr, -1);
}
}
#[test]
fn test_array_item_updates() {
let arr = __quantum__rt__array_create_1d(1, 3);
unsafe {
assert_eq!(__quantum__rt__array_get_size_1d(arr), 3);
let first = __quantum__rt__array_get_element_ptr_1d(arr, 0);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0);
*first = 42;
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 42);
let second = __quantum__rt__array_get_element_ptr_1d(arr, 1);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0);
*second = 31;
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 31);
let third = __quantum__rt__array_get_element_ptr_1d(arr, 2);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 2), 0);
*third = 20;
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 2), 20);
__quantum__rt__array_update_reference_count(arr, -1);
}
}
#[test]
fn test_array_copy() {
let arr = __quantum__rt__array_create_1d(1, 3);
unsafe {
assert_eq!(__quantum__rt__array_get_size_1d(arr), 3);
let first = __quantum__rt__array_get_element_ptr_1d(arr, 0);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0);
*first = 42;
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 42);
let second = __quantum__rt__array_get_element_ptr_1d(arr, 1);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0);
*second = 31;
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 31);
let arr2 = __quantum__rt__array_copy(arr, true);
assert_eq!(__quantum__rt__array_get_size_1d(arr2), 3);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 0), 42);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31);
let third = __quantum__rt__array_get_element_ptr_1d(arr, 2);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 2), 0);
*third = 20;
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 2), 20);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 2), 0);
__quantum__rt__array_update_reference_count(arr, -1);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 0), 42);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31);
__quantum__rt__array_update_reference_count(arr2, -1);
}
}
#[test]
fn test_array_concat() {
let arr = __quantum__rt__array_create_1d(1, 3);
unsafe {
assert_eq!(__quantum__rt__array_get_size_1d(arr), 3);
let first = __quantum__rt__array_get_element_ptr_1d(arr, 0);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0);
*first = 42;
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 42);
let second = __quantum__rt__array_get_element_ptr_1d(arr, 1);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0);
*second = 31;
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 31);
let arr2 = __quantum__rt__array_copy(arr, true);
assert_eq!(__quantum__rt__array_get_size_1d(arr2), 3);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 0), 42);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31);
let arr3 = __quantum__rt__array_concatenate(arr, arr2);
assert_eq!(__quantum__rt__array_get_size_1d(arr3), 6);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 1), 31);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 2), 0);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 3), 42);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 4), 31);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 5), 0);
__quantum__rt__array_update_reference_count(arr, -1);
__quantum__rt__array_update_reference_count(arr2, -1);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 1), 31);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 2), 0);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 3), 42);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 4), 31);
assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 5), 0);
__quantum__rt__array_update_reference_count(arr3, -1);
}
}
#[allow(clippy::cast_ptr_alignment)]
#[test]
fn test_array_creation_large_size() {
struct Data {
first: i64,
second: i64,
third: i64,
}
let arr = __quantum__rt__array_create_1d(size_of::<Data>().try_into().unwrap(), 3);
unsafe {
assert_eq!(__quantum__rt__array_get_size_1d(arr), 3);
let first = __quantum__rt__array_get_element_ptr_1d(arr, 0).cast::<Data>();
*first = Data {
first: 1,
second: 2,
third: 3,
};
let second = __quantum__rt__array_get_element_ptr_1d(arr, 1).cast::<Data>();
*second = Data {
first: 10,
second: 20,
third: 30,
};
let third = __quantum__rt__array_get_element_ptr_1d(arr, 2).cast::<Data>();
*third = Data {
first: 100,
second: 200,
third: 300,
};
assert_eq!(
(*(__quantum__rt__array_get_element_ptr_1d(arr, 0).cast::<Data>())).first,
1
);
assert_eq!(
(*(__quantum__rt__array_get_element_ptr_1d(arr, 0).cast::<Data>())).second,
2
);
assert_eq!(
(*(__quantum__rt__array_get_element_ptr_1d(arr, 0).cast::<Data>())).third,
3
);
assert_eq!(
(*(__quantum__rt__array_get_element_ptr_1d(arr, 1).cast::<Data>())).first,
10
);
assert_eq!(
(*(__quantum__rt__array_get_element_ptr_1d(arr, 1).cast::<Data>())).second,
20
);
assert_eq!(
(*(__quantum__rt__array_get_element_ptr_1d(arr, 1).cast::<Data>())).third,
30
);
assert_eq!(
(*(__quantum__rt__array_get_element_ptr_1d(arr, 2).cast::<Data>())).first,
100
);
assert_eq!(
(*(__quantum__rt__array_get_element_ptr_1d(arr, 2).cast::<Data>())).second,
200
);
assert_eq!(
(*(__quantum__rt__array_get_element_ptr_1d(arr, 2).cast::<Data>())).third,
300
);
__quantum__rt__array_update_reference_count(arr, -1);
}
}
}