-
-
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 | |
} |
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.
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.
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 :)
That indeed looks much better, I will try that, thanks a lot again for all of your help.
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.
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.
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 theCAML_BA_KIND_MASK
before OR-int withCAML_BA_CHAR
to avoid leaving in place the "old" array kind.Passing
b->data
tocaml_ba_alloc
is OK I believe.Otherwise it looks reasonable...