# Cast Bigarray kind

**URL:** <https://discuss.ocaml.org/t/cast-bigarray-kind/8469>\
**Category:** Learning\
**Tags:** bigarray\
**Created:** [September 10, 2021, 7:25pm UTC](https://discuss.ocaml.org/t/cast-bigarray-kind/8469 "2021-09-10T19:25:40Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![crackcomm](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/crackcomm/32/3201_2.png) [@crackcomm](https://discuss.ocaml.org/u/crackcomm)\
**Post date:** [September 10, 2021, 7:25pm UTC](https://discuss.ocaml.org/t/cast-bigarray-kind/8469/1 "2021-09-10T19:25:40Z")

</div>

Hey there,

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.

---

<div class="post-metadata">

**Author:** ![hcarty](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/hcarty/32/1824_2.png) [@hcarty](https://discuss.ocaml.org/u/hcarty)\
**Post date:** [September 10, 2021, 8:09pm UTC](https://discuss.ocaml.org/t/cast-bigarray-kind/8469/2 "2021-09-10T20:09:27Z")

</div>

I have some code/stubs here which I wrote a long while ago to cast the underlying bytes between different bigarray types:

- C stubs: [ocaml-hdf/hdf\_impl.c at 7cd8843fc07047ff008aa31d9444e8be656e1b30 · hcarty/ocaml-hdf · GitHub](https://github.com/hcarty/ocaml-hdf/blob/7cd8843fc07047ff008aa31d9444e8be656e1b30/src/hdf_impl.c#L681-L754)
- OCaml binding: [ocaml-hdf/extGenarray.ml at 7cd8843fc07047ff008aa31d9444e8be656e1b30 · hcarty/ocaml-hdf · GitHub](https://github.com/hcarty/ocaml-hdf/blob/7cd8843fc07047ff008aa31d9444e8be656e1b30/src/extGenarray.ml#L40-L66)

I haven’t tested or used this code in several years at this point, so please proceed with caution if you give it a try!

---

<div class="post-metadata">

**Author:** ![crackcomm](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/crackcomm/32/3201_2.png) [@crackcomm](https://discuss.ocaml.org/u/crackcomm)\
**Post date:** [September 10, 2021, 8:44pm UTC](https://discuss.ocaml.org/t/cast-bigarray-kind/8469/3 "2021-09-10T20:44:21Z")

</div>

Thanks! that is very helpful. I had to make few changes (eq. the elems counter should use `*` not `+`). I created a fork and [committed](https://github.com/crackcomm/ocaml-hdf/commit/8f2ee92c9b72be65ec70ab823732f4e7e8eaf261) the changes if you’re interested.

It seems to work fine, yet the question remains if it is possible without making a copy.

---

<div class="post-metadata">

**Author:** ![nojb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/nojb/32/519_2.png) [@nojb](https://discuss.ocaml.org/u/nojb)\
**Post date:** [September 10, 2021, 9:54pm UTC](https://discuss.ocaml.org/t/cast-bigarray-kind/8469/4 "2021-09-10T21:54:53Z")

</div>

> [@crackcomm](#):
>
> 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`](https://github.com/ocaml/ocaml/blob/f1b9c23d18e5fc6dcf1d17ea21ba478de58db929/runtime/bigarray.c#L917-L941) function).

Cheers,  
Nicolas

---

<div class="post-metadata">

**Author:** ![crackcomm](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/crackcomm/32/3201_2.png) [@crackcomm](https://discuss.ocaml.org/u/crackcomm)\
**Post date:** [September 10, 2021, 10:13pm UTC](https://discuss.ocaml.org/t/cast-bigarray-kind/8469/5 "2021-09-10T22:13:29Z")

</div>

Here is what I have ([gist](https://gist.github.com/crackcomm/9d5fbeaa8e038194cdbf0ba990d698e6)):

```auto
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
}

```

```auto
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`?

---

<div class="post-metadata">

**Author:** ![nojb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/nojb/32/519_2.png) [@nojb](https://discuss.ocaml.org/u/nojb)\
**Post date:** [September 11, 2021, 6:18am UTC](https://discuss.ocaml.org/t/cast-bigarray-kind/8469/6 "2021-09-11T06:18:16Z")

</div>

> [@crackcomm](#):
>
> I’m not sure if the code is correct,

I left some [comments](https://gist.github.com/crackcomm/9d5fbeaa8e038194cdbf0ba990d698e6#gistcomment-3889407) in your gist.

Cheers,  
Nicolas

---

<div class="post-metadata">

**Author:** ![xavierleroy](https://avatars.discourse-cdn.com/v4/letter/x/a9adbd/32.png) [@xavierleroy](https://discuss.ocaml.org/u/xavierleroy)\
**Post date:** [September 11, 2021, 5:28pm UTC](https://discuss.ocaml.org/t/cast-bigarray-kind/8469/7 "2021-09-11T17:28:11Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![crackcomm](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/crackcomm/32/3201_2.png) [@crackcomm](https://discuss.ocaml.org/u/crackcomm)\
**Post date:** [September 11, 2021, 8:30pm UTC](https://discuss.ocaml.org/t/cast-bigarray-kind/8469/8 "2021-09-11T20:30:21Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![yallop](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yallop/32/517_2.png) [@yallop](https://discuss.ocaml.org/u/yallop)\
**Post date:** [September 13, 2021, 2:10pm UTC](https://discuss.ocaml.org/t/cast-bigarray-kind/8469/9 "2021-09-13T14:10:59Z")

</div>

> [@crackcomm](#):
>
> 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.

In case it’s of interest, Ctypes can do this. For example, here’s a function that provides a `bigstring` view onto a `float` `Array2.t` value:

```ocaml
let bigstring_of_float64_array2 arr =
  let start = coerce (ptr double) (ptr char) (bigarray_start array2 arr) in
  Bigarray.(bigarray_of_ptr array1 (Array2.dim1 arr * Array2.dim2 arr * sizeof double) char start)

```

Here’s the function in action, showing that the size of the bigstring is calculated appropriately:

```ocaml
# let arr = Bigarray.(Array2.(create float64) c_layout 10 20);;
val arr : [...]
# let bs = bigstring_of_float64_array2 arr;;
val bs : [...]
# Bigarray.Array1.dim bs;;
- : int = 1600

```

Here’s a demonstration that there’s no copying going on:

```ocaml
# arr.{0,0} <- 10.5;;
- : unit = ()
# Int64.float_of_bits (Core_kernel.Bigstring.get_int64_t_le bs 0);;
- : float = 10.5
# arr.{1,1} <- -4.1;;
- : unit = ()
# Int64.float_of_bits (Core_kernel.Bigstring.get_int64_t_le bs 168);;
- : float = -4.1

```

---

<div class="post-metadata">

**Author:** ![crackcomm](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/crackcomm/32/3201_2.png) [@crackcomm](https://discuss.ocaml.org/u/crackcomm)\
**Post date:** [September 13, 2021, 4:12pm UTC](https://discuss.ocaml.org/t/cast-bigarray-kind/8469/10 "2021-09-13T16:12:44Z")

</div>

That’s pretty much what I was looking for, brilliant.

---

<div class="post-metadata">

**Author:** ![UnixJunkie](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/unixjunkie/32/638_2.png) [@UnixJunkie](https://discuss.ocaml.org/u/UnixJunkie)\
**Post date:** [February 20, 2023, 7:09am UTC](https://discuss.ocaml.org/t/cast-bigarray-kind/8469/11 "2023-02-20T07:09:12Z")

</div>

In the same vain, could Ctypes give a bytes view of a char bigarray (a bigstring)?

---

<div class="post-metadata">

**Author:** ![yallop](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yallop/32/517_2.png) [@yallop](https://discuss.ocaml.org/u/yallop)\
**Post date:** [February 20, 2023, 11:18am UTC](https://discuss.ocaml.org/t/cast-bigarray-kind/8469/12 "2023-02-20T11:18:34Z")

</div>

I don’t think there’s a way to do that, because the memory layouts are too different.
