How to replace strings with bytes in old C stub code

In an old third-party project written when strings were mutable in OCaml, I have the following snippet :

external image_surface_create_from_stream_unsafe : 
  (string -> int -> unit) -> Cairo.image_surface = "ml_cairo_image_surface_create_from_png_stream_unsafe"

let image_surface_create_from_channel ic =
  image_surface_create_from_stream_unsafe
    (fun s n ->
      for i = 0 to n - 1 do
	String.unsafe_set s i (input_char ic)
      done)

The corresponding C snippet is

static value
_ml_cairo_image_surface_create_from_png_stream (value f, cairo_bool_t unsafe)
{
  CAMLparam1(f);
  CAMLlocal1(c);
  cairo_surface_t *surf;

  c = caml_alloc_small (2, 0);
  Field (c, 0) = f;
  Field (c, 1) = Val_unit;
  surf = cairo_image_surface_create_from_png_stream (unsafe ? ml_cairo_unsafe_read_func : ml_cairo_read_func, 
						     &c);
  if (Is_exception_result (Field (c, 1)))
    caml_raise (Extract_exception (Field (c, 1)));

  CAMLreturn (Val_cairo_surface_t (surf));
}

CAMLprim value
ml_cairo_image_surface_create_from_png_stream_unsafe (value f)
{
  return _ml_cairo_image_surface_create_from_png_stream (f, 1);
}

It compiles fine when I use the -unsafe-string option, but I’d like to update it and make it compatible
with the -safe-string option. So I basically need to replace strings with
bytes in the C code, but I don’t know much about the internal representation of those. What is the simplest way to do it ?

There are char Byte(bytes c, int n), unsigned Byte_u(bytes c, int n) and unsigned char *Bytes_val(bytes c) macros in the C interface, documented in the manual.

That said, I don’t understand how your C code handles the string, as far as I can tell there is nothing in your code about the string, it is the responsibility of the cairo_image_surface_create_from_png_stream and ml_cairo_{.unsafe_}read_func functions.

According to section 20.3.1 of said manual, it would seem that bytes and strings have the same internal representation, so that all I need is replace

external image_surface_create_from_stream_unsafe : 
  (string -> int -> unit) -> Cairo.image_surface = "ml_cairo_image_surface_create_from_png_stream_unsafe"

with

external image_surface_create_from_stream_unsafe : 
  (bytes -> int -> unit) -> Cairo.image_surface = "ml_cairo_image_surface_create_from_png_stream_unsafe"

and things should work just fine ?

Yes, this should be safe for now.

I guessed as much and it seems to work. The manual should be more explicit about this IMHO, as it is a rather natural question.

1 Like