[Libguestfs] [nbdkit PATCH 2/2] ocaml: Implement .list_exports and friends

Richard W.M. Jones rjones at redhat.com
Tue Sep 1 14:13:28 UTC 2020


On Tue, Sep 01, 2020 at 08:41:40AM -0500, Eric Blake wrote:
> On 9/1/20 8:25 AM, Eric Blake wrote:
> >Fairly straightforward. I'd love for type export to be a bit more
> >flexible to make description optional, but could not figure out how to
> >decode that from the C side of things, so for now this just requires
> >the caller to supply a description for all exports during
> >.list_exports.
> >
> 
> Maybe I did figure it out after all, although I'm still not sure
> this is the best interface.  Applying this on top of the original
> patch lets me use 'string option' instead of 'string' as the second
> member of the export record. https://caml.inria.fr/pub/docs/manual-ocaml/intfc.html#s%3Ac-ocaml-datatype-repr
> didn't directly answer my question, but my understanding is that
> since 'string option' is the same as:
> 
> type 'a t = a' option =
>  | None          (* Is_block is false, value is Val_int(0) *)
>  | Some of 'a    (* Is_block is true, value is block with tag 0 *)
> 
> then checking Is_block tells me whether I have None or Some string,
> at which point another Field() deref gets me to the string contained
> in that block.

All ‘value’ things in OCaml are just 64 bit integers.  The only
difference is whether the bottom bit is 0, in which case it's a
pointer to an OCaml block, or 1 in which case it's an integer-like
thing shifted left by one bit.  Is_block() simply checks the bottom
bit.

Val_int(0) turns C int 0 into an OCaml int.  Which is done by shifting
it left one place and setting the bottom bit.  IOW it returns C 1.

Also None is represented as OCaml 0 (so C 1):
https://rwmj.wordpress.com/2009/08/04/ocaml-internals/

OCaml blocks are lists of values allocated on the OCaml heap:
https://rwmj.wordpress.com/2009/08/05/ocaml-internals-part-2-strings-and-other-types/
and those values may be ints or pointers.

Therefore the easiest way to tell if something is None from C is
to compare it with Val_int(0), ie:

  value v = /* something which has type ‘string option’ */;

  if (v == Val_int(0)) /* It's None */
    printf ("None\n");
  else {
    /* It's Some string, set sv to the string value */
    value sv = Field (v, 0);
    printf ("Some %s\n", String_val (sv));
  }

The weird casting macros have the form: To_from()

Also that String_val is only valid for a short period of time,
basically until the next OCaml allocation in the current thread.

> +++ w/plugins/ocaml/ocaml.c
> @@ -332,11 +332,12 @@ list_exports_wrapper (int readonly, int
> is_tls, struct nbdkit_exports *exports)
> 
>    /* Convert exports list into calls to nbdkit_add_export. */
>    while (rv != Val_int (0)) {
> -    const char *name, *desc;
> +    const char *name, *desc = NULL;
> 
>      v = Field (rv, 0);          /* export struct */
>      name = String_val (Field (v, 0));
> -    desc = String_val (Field (v, 1));
> +    if (Is_block (Field (v, 1)))
> +      desc = String_val (Field (Field (v, 1), 0));
>      if (nbdkit_add_export (exports, name, desc) == -1) {
>        caml_enter_blocking_section ();
>        CAMLreturnT (int, -1);

Seems OK.  I'm guessing this crashes?

Rich.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-builder quickly builds VMs from scratch
http://libguestfs.org/virt-builder.1.html




More information about the Libguestfs mailing list