Skip to content

Instantly share code, notes, and snippets.

@crackcomm
Last active September 12, 2021 13:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save crackcomm/9d5fbeaa8e038194cdbf0ba990d698e6 to your computer and use it in GitHub Desktop.
Save crackcomm/9d5fbeaa8e038194cdbf0ba990d698e6 to your computer and use it in GitHub Desktop.
open Core
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 ~f:Int.( * ) (Bigarray.Genarray.dims ba)
let cast ba =
let ba = Bigarray.reshape ba [| elems ba |] in
c_cast ba |> array1_of_genarray
;;
(library
(name bigarray_str)
(public_name bigarray_str)
(libraries core)
(foreign_stubs
(language c)
(names stubs)
(flags -std=c99 -pedantic)))
#define CAML_INTERNALS
#include <caml/custom.h>
#include <caml/fail.h>
#include <caml/memory.h>
#include <caml/bigarray.h>
// Source: https://github.com/ocaml/ocaml/blob/4.12/runtime/bigarray.c#L919-L941
static void caml_ba_update_proxy(struct caml_ba_array *b1,
struct caml_ba_array *b2)
{
struct caml_ba_proxy *proxy;
/* Nothing to do for un-managed arrays */
if ((b1->flags & CAML_BA_MANAGED_MASK) == CAML_BA_EXTERNAL)
return;
if (b1->proxy != NULL)
{
/* If b1 is already a proxy for a larger array, increment refcount of
proxy */
b2->proxy = b1->proxy;
++b1->proxy->refcount;
}
else
{
/* Otherwise, create proxy and attach it to both b1 and b2 */
proxy = malloc(sizeof(struct caml_ba_proxy));
if (proxy == NULL)
caml_raise_out_of_memory();
proxy->refcount = 2; /* original array + sub array */
proxy->data = b1->data;
proxy->size =
b1->flags & CAML_BA_MAPPED_FILE ? caml_ba_byte_size(b1) : 0;
b1->proxy = proxy;
b2->proxy = proxy;
}
}
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] = (i == b->num_dims - 1) ? b->dim[i] * caml_ba_element_size[b->flags & CAML_BA_KIND_MASK] : b->dim[i];
}
int flags = (b->flags & ~CAML_BA_KIND_MASK) | CAML_BA_CHAR;
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
}
@nojb
Copy link

nojb commented Sep 11, 2021

Hello, I think you should not set the "managed" or "external" flag yourself, but simply reuse the "flags" of the source array (just setting the CAML_BA_CHAR flag). You should also probably clear the CAML_BA_KIND_MASK before OR-int with CAML_BA_CHAR to avoid leaving in place the "old" array kind.

Passing b->data to caml_ba_alloc is OK I believe.

Otherwise it looks reasonable...

@crackcomm
Copy link
Author

Hey @nojb, thanks for the answer.

Regarding clearing the flag, doesn't the line 49:

int flags = CAML_BA_CHAR | (b->flags & (CAML_BA_LAYOUT_MASK | CAML_BA_MANAGED_MASK));

work just as well? it selects only layout and managed mask from b->flags for as much as I understand C.

@crackcomm
Copy link
Author

btw. before releasing the code I checked with the assert((flags & CAML_BA_KIND_MASK) == CAML_BA_CHAR); but dealing with C, I just cannot ever be sure enough.

@nojb
Copy link

nojb commented Sep 11, 2021

Yes you are right of course, but what I was thinking about is to reuse b->flags to a maximum (in particular the "managed"/"external" bit), and set just the "kind" bit:

int flags = (b->flags & ~CAML_BA_KIND_MASK) | CAML_BA_CHAR;

... but, again, some testing will be required to make sure that everything works well :)

@crackcomm
Copy link
Author

That indeed looks much better, I will try that, thanks a lot again for all of your help.

@nojb
Copy link

nojb commented Sep 11, 2021

As a mental model you can look at the caml_ba_slice function that takes a "slice" of a bigarray without copying the data. What you are doing is not very different; and you will see there that the flags of the "source" array are copied as-is to the destination array.

@crackcomm
Copy link
Author

I was using caml_ba_reshape since it looks simpler, yet I didn't look close enough at what caml_ba_update_proxy is doing. I will get some sleep and try to make the kind an input, maybe it's possible to get that merged into ocaml trunk. It would be pretty awesome but I have no idea how the core team perceives adding new features to standard library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment