qir_stdlib/
range_support.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4// Functionality in this file can be removed when range support is dropped from the QIR runtime.
5
6use crate::{arrays::QirArray, strings::convert};
7use std::{ffi::CString, rc::Rc};
8
9#[repr(C)]
10pub struct Range {
11    pub start: i64,
12    pub step: i64,
13    pub end: i64,
14}
15
16#[no_mangle]
17pub extern "C" fn quantum__rt__range_to_string(input: Range) -> *const CString {
18    let mut range_str = input.start.to_string() + "..";
19    if input.step != 1 {
20        range_str += &(input.step.to_string() + "..");
21    }
22    range_str += &input.end.to_string();
23
24    convert(&range_str)
25}
26
27#[no_mangle]
28pub unsafe extern "C" fn quantum__rt__array_slice_1d(
29    arr: *const QirArray,
30    range: Range,
31) -> *const QirArray {
32    let array = &*arr;
33    let item_size: i64 = array
34        .elem_size
35        .try_into()
36        .expect("Array element size too large for `usize` type on this platform.");
37    let mut slice = QirArray {
38        elem_size: array.elem_size,
39        data: Vec::new(),
40    };
41    let iter: Box<dyn Iterator<Item = i64>> = if range.step > 0 {
42        Box::new(range.start * item_size..=range.end * item_size)
43    } else {
44        Box::new((range.end * item_size..=range.start * item_size).rev())
45    };
46
47    let step: i64 = range.step.abs();
48    for i in iter.step_by((step * item_size).try_into().expect(
49        "Range step multiplied by item size is too large for `usize` type on this platform",
50    )) {
51        let index = i
52            .try_into()
53            .expect("Item index too large for `usize` type on this platform.");
54        let mut copy = vec![0; array.elem_size];
55        copy.copy_from_slice(&array.data[index..index + array.elem_size]);
56        slice.data.append(&mut copy);
57    }
58
59    Rc::into_raw(Rc::new(slice))
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65    use crate::{
66        arrays::{
67            __quantum__rt__array_concatenate, __quantum__rt__array_copy,
68            __quantum__rt__array_create_1d, __quantum__rt__array_get_element_ptr_1d,
69            __quantum__rt__array_get_size_1d, __quantum__rt__array_update_reference_count,
70        },
71        strings::{__quantum__rt__string_get_data, __quantum__rt__string_update_reference_count},
72    };
73    use std::ffi::CStr;
74
75    #[test]
76    fn test_range_to_string() {
77        let input4 = Range {
78            start: 0,
79            step: 1,
80            end: 9,
81        };
82        let str4 = quantum__rt__range_to_string(input4);
83        unsafe {
84            assert_eq!(
85                CStr::from_ptr(__quantum__rt__string_get_data(str4))
86                    .to_str()
87                    .unwrap(),
88                "0..9"
89            );
90        }
91        let input5 = Range {
92            start: 0,
93            step: 2,
94            end: 12,
95        };
96        let str5 = quantum__rt__range_to_string(input5);
97        unsafe {
98            assert_eq!(
99                CStr::from_ptr(__quantum__rt__string_get_data(str5))
100                    .to_str()
101                    .unwrap(),
102                "0..2..12"
103            );
104        }
105        unsafe {
106            __quantum__rt__string_update_reference_count(str4, -1);
107            __quantum__rt__string_update_reference_count(str5, -1);
108        }
109    }
110
111    #[test]
112    fn test_array_slicing() {
113        let arr = __quantum__rt__array_create_1d(1, 3);
114        unsafe {
115            assert_eq!(__quantum__rt__array_get_size_1d(arr), 3);
116            let first = __quantum__rt__array_get_element_ptr_1d(arr, 0);
117            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0);
118            *first = 42;
119            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 42);
120            let second = __quantum__rt__array_get_element_ptr_1d(arr, 1);
121            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0);
122            *second = 31;
123            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 31);
124            let arr2 = __quantum__rt__array_copy(arr, true);
125            assert_eq!(__quantum__rt__array_get_size_1d(arr2), 3);
126            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 0), 42);
127            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31);
128            let arr3 = __quantum__rt__array_concatenate(arr, arr2);
129            assert_eq!(__quantum__rt__array_get_size_1d(arr3), 6);
130            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42);
131            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 1), 31);
132            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 2), 0);
133            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 3), 42);
134            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 4), 31);
135            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 5), 0);
136            // Third array crated via concatenation has contents [42, 31, 0, 42, 31, 0], create
137            // fourth array via slicing with step size 2, expected contents [42, 0, 31].
138            let arr4 = quantum__rt__array_slice_1d(
139                arr3,
140                Range {
141                    start: 0,
142                    step: 2,
143                    end: 5,
144                },
145            );
146            assert_eq!(__quantum__rt__array_get_size_1d(arr4), 3);
147            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 0), 42);
148            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 1), 0);
149            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 2), 31);
150            // Create fifth array via slicing with reverse iteration, expected contents
151            // [31, 0, 42].
152            let arr5 = quantum__rt__array_slice_1d(
153                arr3,
154                Range {
155                    start: 4,
156                    step: -2,
157                    end: 0,
158                },
159            );
160            assert_eq!(__quantum__rt__array_get_size_1d(arr5), 3);
161            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 0), 31);
162            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 1), 0);
163            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 2), 42);
164            // Create sixth array with range end less than range start, should succeed and create
165            // an empty array.
166            let arr6 = quantum__rt__array_slice_1d(
167                arr5,
168                Range {
169                    start: 0,
170                    step: 1,
171                    end: -1,
172                },
173            );
174            // Confirm each copy, concatenation, and slice is independent of others.
175            assert_eq!(__quantum__rt__array_get_size_1d(arr6), 0);
176            __quantum__rt__array_update_reference_count(arr, -1);
177            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31);
178            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42);
179            __quantum__rt__array_update_reference_count(arr2, -1);
180            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42);
181            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 1), 31);
182            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 2), 0);
183            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 3), 42);
184            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 4), 31);
185            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 5), 0);
186            __quantum__rt__array_update_reference_count(arr3, -1);
187            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 0), 42);
188            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 1), 0);
189            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 2), 31);
190            __quantum__rt__array_update_reference_count(arr4, -1);
191            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 0), 31);
192            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 1), 0);
193            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 2), 42);
194            __quantum__rt__array_update_reference_count(arr5, -1);
195            __quantum__rt__array_update_reference_count(arr6, -1);
196        }
197    }
198}