1use crate::{
5 conv::FromOCaml,
6 mlvalues::{
7 bigarray::{Array1, BigarrayElt},
8 tag, DynBox, OCamlBytes, OCamlFloat, OCamlInt32, OCamlInt64, OCamlList, RawOCaml,
9 },
10 runtime::OCamlRuntime,
11 value::OCaml,
12};
13use core::{any::Any, cell::UnsafeCell, marker::PhantomData, mem, pin::Pin, ptr};
14pub use ocaml_sys::{caml_alloc, store_field};
15use ocaml_sys::{
16 caml_alloc_string, caml_alloc_tuple, caml_copy_double, caml_copy_int32, caml_copy_int64,
17 custom_operations, string_val, Size,
18};
19
20pub struct OCamlCell<T> {
21 cell: UnsafeCell<RawOCaml>,
22 _marker: PhantomData<T>,
23}
24
25static_assertions::assert_eq_size!(OCamlCell<bool>, OCaml<'static, bool>, RawOCaml);
26
27pub type OCamlRef<'a, T> = &'a OCamlCell<T>;
31
32impl<T> OCamlCell<T> {
33 #[doc(hidden)]
34 pub unsafe fn create_ref<'a>(val: *const RawOCaml) -> OCamlRef<'a, T> {
35 &*(val as *const OCamlCell<T>)
36 }
37
38 pub fn to_rust<RustT>(&self, cr: &OCamlRuntime) -> RustT
40 where
41 RustT: FromOCaml<T>,
42 {
43 RustT::from_ocaml(cr.get(self))
44 }
45
46 pub unsafe fn get_raw(&self) -> RawOCaml {
52 *self.cell.get()
53 }
54}
55
56pub fn alloc_bytes<'a>(cr: &'a mut OCamlRuntime, s: &[u8]) -> OCaml<'a, OCamlBytes> {
57 unsafe {
58 let len = s.len();
59 let value = caml_alloc_string(len);
60 let ptr = string_val(value);
61 core::ptr::copy_nonoverlapping(s.as_ptr(), ptr, len);
62 OCaml::new(cr, value)
63 }
64}
65
66pub fn alloc_string<'a>(cr: &'a mut OCamlRuntime, s: &str) -> OCaml<'a, String> {
67 unsafe {
68 let len = s.len();
69 let value = caml_alloc_string(len);
70 let ptr = string_val(value);
71 core::ptr::copy_nonoverlapping(s.as_ptr(), ptr, len);
72 OCaml::new(cr, value)
73 }
74}
75
76pub fn alloc_int32(cr: &mut OCamlRuntime, i: i32) -> OCaml<OCamlInt32> {
77 unsafe { OCaml::new(cr, caml_copy_int32(i)) }
78}
79
80pub fn alloc_int64(cr: &mut OCamlRuntime, i: i64) -> OCaml<OCamlInt64> {
81 unsafe { OCaml::new(cr, caml_copy_int64(i)) }
82}
83
84pub fn alloc_double(cr: &mut OCamlRuntime, d: f64) -> OCaml<OCamlFloat> {
85 unsafe { OCaml::new(cr, caml_copy_double(d)) }
86}
87
88pub fn alloc_some<'a, A>(cr: &'a mut OCamlRuntime, value: OCamlRef<'_, A>) -> OCaml<'a, Option<A>> {
93 unsafe {
94 let ocaml_some = caml_alloc(1, tag::SOME);
95 store_field(ocaml_some, 0, value.get_raw());
96 OCaml::new(cr, ocaml_some)
97 }
98}
99
100pub fn alloc_ok<'a, A, Err>(
101 cr: &'a mut OCamlRuntime,
102 value: OCamlRef<'_, A>,
103) -> OCaml<'a, Result<A, Err>> {
104 unsafe {
105 let ocaml_ok = caml_alloc(1, tag::TAG_OK);
106 store_field(ocaml_ok, 0, value.get_raw());
107 OCaml::new(cr, ocaml_ok)
108 }
109}
110
111pub fn alloc_error<'a, A, Err>(
112 cr: &'a mut OCamlRuntime,
113 err: OCamlRef<'_, Err>,
114) -> OCaml<'a, Result<A, Err>> {
115 unsafe {
116 let ocaml_err = caml_alloc(1, tag::TAG_ERROR);
117 store_field(ocaml_err, 0, err.get_raw());
118 OCaml::new(cr, ocaml_err)
119 }
120}
121
122#[doc(hidden)]
123pub unsafe fn alloc_tuple<T>(cr: &mut OCamlRuntime, size: usize) -> OCaml<T> {
124 let ocaml_tuple = caml_alloc_tuple(size);
125 OCaml::new(cr, ocaml_tuple)
126}
127
128pub fn alloc_cons<'a, 'b, A>(
132 cr: &'a mut OCamlRuntime,
133 head: OCamlRef<'b, A>,
134 tail: OCamlRef<'b, OCamlList<A>>,
135) -> OCaml<'a, OCamlList<A>> {
136 unsafe {
137 let ocaml_cons = caml_alloc(2, tag::CONS);
138 store_field(ocaml_cons, 0, head.get_raw());
139 store_field(ocaml_cons, 1, tail.get_raw());
140 OCaml::new(cr, ocaml_cons)
141 }
142}
143
144#[inline]
145pub unsafe fn store_raw_field_at<A>(
146 cr: &mut OCamlRuntime,
147 block: OCamlRef<A>,
148 offset: Size,
149 raw_value: RawOCaml,
150) {
151 store_field(cr.get(block).get_raw(), offset, raw_value);
152}
153
154const BOX_OPS_DYN_DROP: custom_operations = custom_operations {
155 identifier: c"_rust_box_dyn_drop".as_ptr() as *const ocaml_sys::Char,
156 finalize: Some(drop_box_dyn),
157 compare: None,
158 hash: None,
159 serialize: None,
160 deserialize: None,
161 compare_ext: None,
162 fixed_length: ptr::null(),
163};
164
165extern "C" fn drop_box_dyn(oval: RawOCaml) {
166 unsafe {
167 let box_ptr = ocaml_sys::field(oval, 1) as *mut Pin<Box<dyn Any>>;
168 ptr::drop_in_place(box_ptr);
169 }
170}
171
172pub fn alloc_box<A: 'static>(cr: &mut OCamlRuntime, data: A) -> OCaml<DynBox<A>> {
195 let oval;
196 type B = Pin<Box<dyn Any>>;
198 unsafe {
199 oval = ocaml_sys::caml_alloc_custom(&BOX_OPS_DYN_DROP, mem::size_of::<B>(), 0, 1);
200 let box_ptr = ocaml_sys::field(oval, 1) as *mut B;
201 std::ptr::write(box_ptr, Box::pin(data));
202 }
203 unsafe { OCaml::new(cr, oval) }
204}
205
206pub fn alloc_bigarray1<'a, A: BigarrayElt>(
212 cr: &'a mut OCamlRuntime,
213 data: &[A],
214) -> OCaml<'a, Array1<A>> {
215 let len = data.len();
216 let ocaml_ba;
217 unsafe {
218 ocaml_ba = ocaml_sys::bigarray::caml_ba_alloc_dims(A::KIND, 1, core::ptr::null_mut(), len);
224 let ba_meta_ptr = ocaml_sys::field(ocaml_ba, 1) as *const ocaml_sys::bigarray::Bigarray;
225 core::ptr::copy_nonoverlapping(data.as_ptr(), (*ba_meta_ptr).data as *mut A, len);
226 }
227 unsafe { OCaml::new(cr, ocaml_ba) }
228}