Skip to content

Commit

Permalink
Merge pull request #61 from mt-caret/uniform-and-float-arrays
Browse files Browse the repository at this point in the history
Float and Uniform array support
  • Loading branch information
tizoc authored Jan 28, 2024
2 parents 7359290 + a1195e5 commit 0588ba3
Show file tree
Hide file tree
Showing 11 changed files with 188 additions and 25 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: test
- run: opam install dune alcotest
- run: opam install dune alcotest base
- name: Rust caller test
run: cd testing/rust-caller; cargo test
- name: Build OCaml caller
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ exclude = [
features = [ "without-ocamlopt" ]

[dependencies]
ocaml-sys = "0.22"
ocaml-sys = "0.23"
ocaml-boxroot-sys = "0.2"
static_assertions = "1.1.0"

Expand Down
43 changes: 42 additions & 1 deletion src/conv/from_ocaml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
// SPDX-License-Identifier: MIT

use crate::{
mlvalues::{field_val, OCamlBytes, OCamlFloat, OCamlInt, OCamlInt32, OCamlInt64, OCamlList},
mlvalues::{
field_val, tag, OCamlBytes, OCamlFloat, OCamlFloatArray, OCamlInt, OCamlInt32, OCamlInt64,
OCamlList, OCamlUniformArray,
},
value::OCaml,
};
use ocaml_sys::{caml_alloc, caml_sys_double_field};

Check warning on line 11 in src/conv/from_ocaml.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `caml_alloc`

warning: unused import: `caml_alloc` --> src/conv/from_ocaml.rs:11:17 | 11 | use ocaml_sys::{caml_alloc, caml_sys_double_field}; | ^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default

/// Implements conversion from OCaml values into Rust values.
pub unsafe trait FromOCaml<T> {

Check warning on line 14 in src/conv/from_ocaml.rs

View workflow job for this annotation

GitHub Actions / clippy

docs for unsafe trait missing `# Safety` section

warning: docs for unsafe trait missing `# Safety` section --> src/conv/from_ocaml.rs:14:1 | 14 | pub unsafe trait FromOCaml<T> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc = note: `#[warn(clippy::missing_safety_doc)]` on by default
Expand Down Expand Up @@ -137,6 +141,43 @@ where
}
}

unsafe impl<A, OCamlA> FromOCaml<OCamlUniformArray<OCamlA>> for Vec<A>
where
A: FromOCaml<OCamlA>,
{
fn from_ocaml(v: OCaml<OCamlUniformArray<OCamlA>>) -> Self {
assert!(
v.tag_value() != tag::DOUBLE_ARRAY,
"unboxed float arrays are not supported"
);

let size = unsafe { v.size() };
let mut vec = Vec::with_capacity(size);
for i in 0..size {
vec.push(A::from_ocaml(unsafe { v.field(i) }));
}
vec
}
}

unsafe impl FromOCaml<OCamlFloatArray> for Vec<f64> {
fn from_ocaml(v: OCaml<OCamlFloatArray>) -> Self {
let size = unsafe { v.size() };

// an empty floatarray doesn't have the double array tag, but otherwise
// we always expect an unboxed float array.
if size > 0 {
assert_eq!(v.tag_value(), tag::DOUBLE_ARRAY)
};

let mut vec = Vec::with_capacity(size);
for i in 0..size {
vec.push(unsafe { caml_sys_double_field(v.raw(), i) });
}
vec
}
}

// Tuples

macro_rules! tuple_from_ocaml {
Expand Down
33 changes: 32 additions & 1 deletion src/conv/to_ocaml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

use core::{borrow::Borrow, str};

use ocaml_sys::{caml_alloc_float_array, caml_sys_store_double_field};

use crate::{
internal::{caml_alloc, store_field},
memory::{
alloc_bigarray1, alloc_bytes, alloc_cons, alloc_double, alloc_error, alloc_int32,
alloc_int64, alloc_ok, alloc_some, alloc_string, alloc_tuple, store_raw_field_at, OCamlRef,
Expand All @@ -15,7 +18,7 @@ use crate::{
},
runtime::OCamlRuntime,
value::OCaml,
BoxRoot,
BoxRoot, OCamlFloatArray, OCamlUniformArray,
};

/// Implements conversion from Rust values into OCaml values.
Expand Down Expand Up @@ -208,6 +211,34 @@ where
}
}

unsafe impl<A, OCamlA: 'static> ToOCaml<OCamlUniformArray<OCamlA>> for Vec<A>
where
A: ToOCaml<OCamlA>,
{
fn to_ocaml<'a>(&self, cr: &'a mut OCamlRuntime) -> OCaml<'a, OCamlUniformArray<OCamlA>> {
let result = BoxRoot::new(unsafe { OCaml::new(cr, caml_alloc(self.len(), 0)) });

for (i, elt) in self.iter().enumerate() {
let ov = elt.to_ocaml(cr);
unsafe { store_field(result.get_raw(), i, ov.raw()) };
}

result.get(cr)
}
}

unsafe impl ToOCaml<OCamlFloatArray> for Vec<f64> {
fn to_ocaml<'a>(&self, cr: &'a mut OCamlRuntime) -> OCaml<'a, OCamlFloatArray> {
let result = unsafe { OCaml::new(cr, caml_alloc_float_array(self.len())) };

for (i, elt) in self.iter().enumerate() {
unsafe { caml_sys_store_double_field(result.raw(), i, *elt) };
}

result
}
}

// Tuples

macro_rules! tuple_to_ocaml {
Expand Down
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,8 @@ pub use crate::error::OCamlException;
pub use crate::memory::alloc_cons as cons;
pub use crate::memory::OCamlRef;
pub use crate::mlvalues::{
bigarray, DynBox, OCamlBytes, OCamlFloat, OCamlInt, OCamlInt32, OCamlInt64, OCamlList, RawOCaml,
bigarray, DynBox, OCamlBytes, OCamlFloat, OCamlFloatArray, OCamlInt, OCamlInt32, OCamlInt64,
OCamlList, OCamlUniformArray, RawOCaml,
};
pub use crate::runtime::OCamlRuntime;
pub use crate::value::OCaml;
Expand Down
14 changes: 14 additions & 0 deletions src/mlvalues.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ pub struct OCamlList<A> {
_marker: PhantomData<A>,
}

/// [`OCaml`]`<OCamlUniformArray<T>>` is a reference to an OCaml array which is
/// guaranteed to not contain unboxed floats. If OCaml was configured with
/// `--disable-flat-float-array` this corresponds to regular `array`s, but if
/// not, `Uniform_array.t` in the `base` library can be used instead.
/// See [Lexifi's blog post on the topic](https://www.lexifi.com/blog/ocaml/about-unboxed-float-arrays/)
/// for more details.
pub struct OCamlUniformArray<A> {
_marker: PhantomData<A>,
}

/// [`OCaml`]`<OCamlFloatArray<T>>` is a reference to an OCaml `floatarray`
/// which is an array containing `float`s in an unboxed form.
pub struct OCamlFloatArray {}

/// `OCaml<DynBox<T>>` is for passing a value of type `T` to OCaml
///
/// To box a Rust value, use [`OCaml::box_value`][crate::OCaml::box_value].
Expand Down
4 changes: 3 additions & 1 deletion src/mlvalues/tag.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright (c) Viable Systems and TezEdge Contributors
// SPDX-License-Identifier: MIT

pub use ocaml_sys::{Tag, CLOSURE, NO_SCAN, STRING, TAG_CONS as CONS, TAG_SOME as SOME};
pub use ocaml_sys::{
Tag, CLOSURE, DOUBLE_ARRAY, NO_SCAN, STRING, TAG_CONS as CONS, TAG_SOME as SOME,
};

pub const TAG_POLYMORPHIC_VARIANT: Tag = 0;
pub const TAG_OK: Tag = 0;
Expand Down
9 changes: 7 additions & 2 deletions src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,19 @@ impl<'a, T> OCaml<'a, T> {
}
}

#[doc(hidden)]
pub unsafe fn size(&self) -> UIntnat {
wosize_val(self.raw)
}

#[doc(hidden)]
pub unsafe fn field<F>(&self, i: UIntnat) -> OCaml<'a, F> {
assert!(
tag_val(self.raw) < tag::NO_SCAN,
"unexpected OCaml value tag >= NO_SCAN"
);
assert!(
i < wosize_val(self.raw),
i < self.size(),
"trying to access a field bigger than the OCaml block value"
);
OCaml {
Expand All @@ -71,7 +76,7 @@ impl<'a, T> OCaml<'a, T> {

#[doc(hidden)]
pub fn is_block_sized(&self, size: usize) -> bool {
self.is_block() && unsafe { wosize_val(self.raw) == size }
self.is_block() && unsafe { self.size() == size }
}

#[doc(hidden)]
Expand Down
7 changes: 4 additions & 3 deletions testing/ocaml-caller/dune
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
(executables
(names ocaml_rust_caller)
(libraries alcotest callable_rust threads.posix))
(libraries alcotest base callable_rust threads.posix))

(rule
(alias runtest)
(action (run ./ocaml_rust_caller.exe)))
(alias runtest)
(action
(run ./ocaml_rust_caller.exe)))
63 changes: 50 additions & 13 deletions testing/ocaml-caller/ocaml_rust_caller.ml
Original file line number Diff line number Diff line change
Expand Up @@ -21,45 +21,47 @@ type movement_polymorphic =

module Rust = struct
external tests_teardown : unit -> unit = "ocaml_interop_teardown"

external twice : int -> int = "rust_twice"

external twice_boxed_i64 : int64 -> int64 = "rust_twice_boxed_i64"

external twice_boxed_i32 : int32 -> int32 = "rust_twice_boxed_i32"

external twice_boxed_float : float -> float = "rust_twice_boxed_float"

external twice_unboxed_float : (float[@unboxed]) -> (float[@unboxed])
= "" "rust_twice_unboxed_float"

external add_unboxed_floats_noalloc : float -> float -> float
= "" "rust_add_unboxed_floats_noalloc"
[@@unboxed] [@@noalloc]
[@@unboxed] [@@noalloc]

external increment_bytes : bytes -> int -> bytes = "rust_increment_bytes"

external increment_ints_list : int list -> int list
= "rust_increment_ints_list"

external make_tuple : string -> int -> string * int = "rust_make_tuple"
external increment_ints_uniform_array :
int Base.Uniform_array.t -> int Base.Uniform_array.t
= "rust_increment_ints_uniform_array"

external make_some : string -> string option = "rust_make_some"
external increment_floats_uniform_array :
float Base.Uniform_array.t -> float Base.Uniform_array.t
= "rust_increment_floats_uniform_array"

external make_ok : int -> (int, string) result = "rust_make_ok"
external increment_floats_float_array : floatarray -> floatarray
= "rust_increment_floats_float_array"

external make_tuple : string -> int -> string * int = "rust_make_tuple"
external make_some : string -> string option = "rust_make_some"
external make_ok : int -> (int, string) result = "rust_make_ok"
external make_error : string -> (int, string) result = "rust_make_error"

external sleep_releasing : int -> unit = "rust_sleep_releasing"

external sleep : int -> unit = "rust_sleep"

external string_of_movement : movement -> string = "rust_string_of_movement"

external string_of_polymorphic_movement : movement_polymorphic -> string
= "rust_string_of_polymorphic_movement"

external rust_rust_add_7ints : int -> int -> int -> int -> int -> int -> int -> int
external rust_rust_add_7ints :
int -> int -> int -> int -> int -> int -> int -> int
= "rust_rust_add_7ints_byte" "rust_rust_add_7ints"
end

Expand Down Expand Up @@ -98,6 +100,35 @@ let test_increment_ints_list () =
let result = Rust.increment_ints_list [ 0; 1; 2; 3; 4; 5; 6; 7; 8; 9 ] in
Alcotest.(check (list int)) "Increment ints in list" expected result

let test_increment_ints_uniform_array () =
let expected = [ 1; 2; 3; 4; 5; 6; 7; 8; 9; 10 ] in
let result =
Base.Uniform_array.to_list
(Rust.increment_ints_uniform_array
(Base.Uniform_array.of_list [ 0; 1; 2; 3; 4; 5; 6; 7; 8; 9 ]))
in
Alcotest.(check (list int)) "Increment ints in uniform array" expected result

let test_increment_floats_uniform_array () =
let expected = [ 1.; 2.; 3.; 4.; 5.; 6.; 7.; 8.; 9.; 10. ] in
let result =
Base.Uniform_array.to_list
(Rust.increment_floats_uniform_array
(Base.Uniform_array.of_list [ 0.; 1.; 2.; 3.; 4.; 5.; 6.; 7.; 8.; 9. ]))
in
Alcotest.(check (list (float 0.)))
"Increment ints in uniform array" expected result

let test_increment_floats_float_array () =
let expected = [ 1.; 2.; 3.; 4.; 5.; 6.; 7.; 8.; 9.; 10. ] in
let result =
Float.Array.to_list
(Rust.increment_floats_float_array
(Float.Array.of_list [ 0.; 1.; 2.; 3.; 4.; 5.; 6.; 7.; 8.; 9. ]))
in
Alcotest.(check (list (float 0.)))
"Increment ints in uniform array" expected result

let test_make_tuple () =
let expected = ("fst", 9) in
let result = Rust.make_tuple "fst" 9 in
Expand Down Expand Up @@ -192,6 +223,12 @@ let () =
test_case "Rust.twice_unboxed_float" `Quick test_twice_unboxed_float;
test_case "Rust.increment_bytes" `Quick test_increment_bytes;
test_case "Rust.increment_ints_list" `Quick test_increment_ints_list;
test_case "Rust.increment_ints_uniform_array" `Quick
test_increment_ints_uniform_array;
test_case "Rust.increment_floats_uniform_array" `Quick
test_increment_floats_uniform_array;
test_case "Rust.increment_floats_float_array" `Quick
test_increment_floats_float_array;
test_case "Rust.make_tuple" `Quick test_make_tuple;
test_case "Rust.make_some" `Quick test_make_some;
test_case "Rust.make_ok" `Quick test_make_ok;
Expand All @@ -201,7 +238,7 @@ let () =
test_case "Rust.string_of_movement" `Quick test_interpret_movement;
test_case "Rust.string_of_polymorphic_movement" `Quick
test_interpret_polymorphic_movement;
test_case "Rust.rust_rust_add_7ints" `Quick test_byte_function
test_case "Rust.rust_rust_add_7ints" `Quick test_byte_function;
] );
];
Rust.tests_teardown ()
33 changes: 32 additions & 1 deletion testing/ocaml-caller/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

use ocaml_interop::{
ocaml_export, ocaml_unpack_polymorphic_variant, ocaml_unpack_variant, OCaml, OCamlBytes,
OCamlFloat, OCamlInt, OCamlInt32, OCamlInt64, OCamlList, OCamlRef, ToOCaml,
OCamlFloat, OCamlFloatArray, OCamlInt, OCamlInt32, OCamlInt64, OCamlList, OCamlRef,
OCamlUniformArray, ToOCaml,
};
use std::{thread, time};

Expand Down Expand Up @@ -73,6 +74,36 @@ ocaml_export! {
vec.to_ocaml(cr)
}

fn rust_increment_ints_uniform_array(cr, ints: OCamlRef<OCamlUniformArray<OCamlInt>>) -> OCaml<OCamlUniformArray<OCamlInt>> {
let mut vec: Vec<i64> = ints.to_rust(cr);

for i in 0..vec.len() {
vec[i] += 1;
}

vec.to_ocaml(cr)
}

fn rust_increment_floats_uniform_array(cr, ints: OCamlRef<OCamlUniformArray<OCamlFloat>>) -> OCaml<OCamlUniformArray<OCamlFloat>> {
let mut vec: Vec<f64> = ints.to_rust(cr);

for i in 0..vec.len() {
vec[i] += 1.;
}

vec.to_ocaml(cr)
}

fn rust_increment_floats_float_array(cr, ints: OCamlRef<OCamlFloatArray>) -> OCaml<OCamlFloatArray> {
let mut vec: Vec<f64> = ints.to_rust(cr);

for i in 0..vec.len() {
vec[i] += 1.;
}

vec.to_ocaml(cr)
}

fn rust_make_tuple(cr, fst: OCamlRef<String>, snd: OCamlRef<OCamlInt>) -> OCaml<(String, OCamlInt)> {
let fst: String = fst.to_rust(cr);
let snd: i64 = snd.to_rust(cr);
Expand Down

0 comments on commit 0588ba3

Please sign in to comment.