From 20366a2a3d8f73d28987d9c1e54d0301b6dcc3e6 Mon Sep 17 00:00:00 2001 From: David Scott Date: Sun, 6 Mar 2016 16:59:02 +0000 Subject: [PATCH 1/3] Add `Cstruct.check_alignment cstr alignment` Some protocols require buffers to be aligned: for example - Xen shared memory protocols require alignment to pages - Linux O_DIRECT requires buffers to be aligned to the disk sector size The new function `check_alignment cstr alignment` returns `true` if the data at `cstr.off` has a memory address for which `address mod alignment = 0`. This will allow implementations which require aligned buffers to check for it, and fail appropriately if given unaligned buffers. Note this patch assumes that javascript does not have buffer addresses or memory alignment requirements, so the js stubs always returns an address of 0. Signed-off-by: David Scott --- js/cstruct.js | 5 +++++ lib/cstruct.ml | 6 ++++++ lib/cstruct.mli | 7 +++++++ lib/cstruct_stubs.c | 7 +++++++ 4 files changed, 25 insertions(+) diff --git a/js/cstruct.js b/js/cstruct.js index f1f7e995..5173eee5 100644 --- a/js/cstruct.js +++ b/js/cstruct.js @@ -64,3 +64,8 @@ function caml_fill_bigstring(buf, buf_off, buf_len, v) { } return 0; } + +//Provides: caml_address_bigstring +function caml_address_bigstring(buf) { + return 0; // FIXME: No concept of a fixed buffer address? +} diff --git a/lib/cstruct.ml b/lib/cstruct.ml index 5b1916b1..5b4d18a4 100644 --- a/lib/cstruct.ml +++ b/lib/cstruct.ml @@ -95,6 +95,12 @@ let create len = let check_bounds t len = Bigarray.Array1.dim t.buffer >= len +external address_bigstring : buffer -> int64 = "caml_address_bigstring" + +let check_alignment t alignment = + let address = address_bigstring t.buffer in + Int64.(rem (add address (of_int t.off)) (of_int alignment) = 0L) + type byte = char let byte (i:int) : byte = Char.chr i diff --git a/lib/cstruct.mli b/lib/cstruct.mli index 30c4bfd6..36fb177d 100644 --- a/lib/cstruct.mli +++ b/lib/cstruct.mli @@ -213,6 +213,13 @@ val check_bounds : t -> int -> bool (** [check_bounds cstr len] is [true] if [cstr.buffer]'s size is greater or equal than [len], [false] otherwise. *) +val check_alignment : t -> int -> bool +(** [check_alignment cstr alignment] is [true] if the first byte stored + within [cstr] is at a memory address where [address mod alignment = 0], + [false] otherwise. + Typical uses are to check a buffer is aligned to a page or disk sector + boundary. *) + val get_char: t -> int -> char (** [get_char t off] returns the character contained in the cstruct at offset [off]. diff --git a/lib/cstruct_stubs.c b/lib/cstruct_stubs.c index 6f82a007..17d714c1 100644 --- a/lib/cstruct_stubs.c +++ b/lib/cstruct_stubs.c @@ -69,3 +69,10 @@ caml_fill_bigstring(value val_buf, value val_ofs, value val_len, value val_byte) Long_val(val_len)); return Val_unit; } + +CAMLprim value +caml_address_bigstring(value val_buf) +{ + uint64_t address = (uint64_t) Caml_ba_data_val(val_buf); + return caml_copy_int64(address); +} From da83796b4d2afc68b28db17c45d6e1dad877c8eb Mon Sep 17 00:00:00 2001 From: David Scott Date: Sun, 6 Mar 2016 17:12:58 +0000 Subject: [PATCH 2/3] test: add tests for `check_alignment` function These tests verify that we find the expected number of aligned offsets within a couple of example buffers. Signed-off-by: David Scott --- lib_test/tests.ml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib_test/tests.ml b/lib_test/tests.ml index 8ccad3ee..9dc19c1d 100644 --- a/lib_test/tests.ml +++ b/lib_test/tests.ml @@ -103,6 +103,17 @@ let fillv () = test [Cstruct.of_string "abc"; Cstruct.of_string ""; Cstruct.of_string "def"] 6; test [Cstruct.of_string "abc"; Cstruct.of_string ""; Cstruct.of_string "def"] 7 +let check_alignment alignment () = + (* Make the buffer big enough to find 4 aligned offsets within it *) + let expected = 4 in + let buf = Cstruct.create (expected * alignment) in + (* How many aligned offsets are there in this buffer? *) + let actual = ref 0 in + for i = 0 to Cstruct.len buf - 1 do + if Cstruct.(check_alignment (shift buf i) alignment) then incr actual + done; + assert_equal ~printer:string_of_int expected !actual + let _ = let suite = "misc tests" >::: [ @@ -118,8 +129,11 @@ let _ = ] ; "append" >::: [ "append is concat" >:: append_is_concat ~n:5000 + ] ; + "alignment" >::: [ + "aligned to 4096" >:: check_alignment 4096 + ; "aligned to 512" >:: check_alignment 512 ] ] in run_test_tt suite - From 5ce7af77ee25108388340b521d4fa0d5a3824dbb Mon Sep 17 00:00:00 2001 From: David Scott Date: Tue, 8 Mar 2016 10:37:40 +0000 Subject: [PATCH 3/3] Move `Cstruct.check_alignment` into the stubs Before this patch we returned the buffer address from C and then calculated using OCaml's boxed Int64.t. This patch reduces minor allocations by performing the calculation in the C stubs. This makes the function suitable for use in an assert in a performance sensitive path. Signed-off-by: David Scott --- js/cstruct.js | 6 +++--- lib/cstruct.ml | 6 ++---- lib/cstruct_stubs.c | 8 +++++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/js/cstruct.js b/js/cstruct.js index 5173eee5..d7f0e415 100644 --- a/js/cstruct.js +++ b/js/cstruct.js @@ -65,7 +65,7 @@ function caml_fill_bigstring(buf, buf_off, buf_len, v) { return 0; } -//Provides: caml_address_bigstring -function caml_address_bigstring(buf) { - return 0; // FIXME: No concept of a fixed buffer address? +//Provides: caml_check_alignment_bigstring +function caml_check_alignment_bigstring(buf, ofs, alignment) { + return true; // FIXME: No concept of a fixed buffer address? } diff --git a/lib/cstruct.ml b/lib/cstruct.ml index 5b4d18a4..f2866e06 100644 --- a/lib/cstruct.ml +++ b/lib/cstruct.ml @@ -95,11 +95,9 @@ let create len = let check_bounds t len = Bigarray.Array1.dim t.buffer >= len -external address_bigstring : buffer -> int64 = "caml_address_bigstring" +external check_alignment_bigstring : buffer -> int -> int -> bool = "caml_check_alignment_bigstring" -let check_alignment t alignment = - let address = address_bigstring t.buffer in - Int64.(rem (add address (of_int t.off)) (of_int alignment) = 0L) +let check_alignment t alignment = check_alignment_bigstring t.buffer t.off alignment type byte = char diff --git a/lib/cstruct_stubs.c b/lib/cstruct_stubs.c index 17d714c1..f5b5c5a8 100644 --- a/lib/cstruct_stubs.c +++ b/lib/cstruct_stubs.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -71,8 +72,9 @@ caml_fill_bigstring(value val_buf, value val_ofs, value val_len, value val_byte) } CAMLprim value -caml_address_bigstring(value val_buf) +caml_check_alignment_bigstring(value val_buf, value val_ofs, value val_alignment) { - uint64_t address = (uint64_t) Caml_ba_data_val(val_buf); - return caml_copy_int64(address); + uint64_t address = (uint64_t) (Caml_ba_data_val(val_buf) + Long_val(val_ofs)); + int alignment = Int_val(val_alignment); + return Val_bool(address % alignment == 0); }