I am trying to cast a float Array2.t into a bigstring (char Array1.t). I started with reshaping and Obj.magic, I tried writing a C extension, nothing with success.
Is there any way I can do this? It’s pretty important for me not to make a copy.
Thanks! that is very helpful. I had to make few changes (eq. the elems counter should use * not +). I created a fork and committed the changes if you’re interested.
It seems to work fine, yet the question remains if it is possible without making a copy.
I believe this should is doable without much trouble from inside the runtime, but I don’t think all required functionality is accessible from outside the runtime (you would need to duplicate the caml_ba_update_proxy function).
CAMLprim value ba_cast_to_char(value vb)
{
CAMLparam1(vb);
CAMLlocal1(res);
#define b (Caml_ba_array_val(vb))
intnat dim[CAML_BA_MAX_NUM_DIMS];
for (int i = 0; i < b->num_dims; i++)
{
dim[i] = b->dim[i] * caml_ba_element_size[b->flags & CAML_BA_KIND_MASK];
}
int flags = CAML_BA_CHAR | (b->flags & (CAML_BA_LAYOUT_MASK | CAML_BA_MANAGED_MASK));
res = caml_ba_alloc(flags, b->num_dims, b->data, dim);
/* Copy the finalization function from the original array (PR#8568) */
Custom_ops_val(res) = Custom_ops_val(vb);
/* Create or update proxy in case of managed bigarray */
caml_ba_update_proxy(b, Caml_ba_array_val(res));
/* Return result */
CAMLreturn(res);
#undef b
}
open Bigarray
external c_cast
: ('a, 'b, 'c) Bigarray.Genarray.t
-> (char, int8_unsigned_elt, 'c) Bigarray.Genarray.t
= "ba_cast_to_char"
let elems ba = Array.reduce_exn Int.( * ) (Bigarray.Genarray.dims ba)
let cast ba =
let ba = Bigarray.reshape ba [| elems ba |] in
c_cast ba |> array1_of_genarray
;;
It again seems to work but I’m not sure if the code is correct, in particular should it replicate CAML_BA_MANAGED_MASK or should it be set to EXTERNAL and whether data should be passed to caml_ba_alloc or only to caml_ba_update_proxy?
I think there’s a problem in your code: you’re multiplying every dimension by the byte size of elements. Only one dimension should be multiplied. Consider a 2x2 array of 64-bit floats, with size 2x2x8 = 32 bytes. You’re turning it into a 16x16 array of bytes, with size 16x16x1 = 256 bytes.
More generally: can you write a specification for what you’re trying to achieve?
Yes, you are completely right. I was reshaping the array into a single dimension and I didn’t catch the mistake. Only the last dimension should be multiplied by the byte size.
The main use of this function is to cast multidimensional array into a bigstring type for hashing and sending it across the network. Hopefully that could be achieved without any C extensions. I think it’s pretty common operation.