[Libguestfs] [PATCH v6 13/41] common/mlstdutils: Implement ‘Char.mem’, ‘String.span’ and ‘String.cspan’.

Richard W.M. Jones
Thu, 15 Jun 2017 17:06:03 -0000

Char.mem tells you if a byte is a member of a string.

String.span and String.cspan are like the C functions strspn and
strcspn.
---
 common/mlstdutils/std_utils.ml       | 27 +++++++++++++++++++++++++++
 common/mlstdutils/std_utils.mli      | 12 ++++++++++++
 common/mlstdutils/std_utils_tests.ml | 21 +++++++++++++++++++++
 3 files changed, 60 insertions(+)

diff --git a/common/mlstdutils/std_utils.ml b/common/mlstdutils/std_utils.ml
index f545c6f7a..a153ceb7f 100644
--- a/common/mlstdutils/std_utils.ml
+++ b/common/mlstdutils/std_utils.ml
@@ -74,6 +74,15 @@ module Char = struct
       | 'e' | 'E' -> 14
       | 'f' | 'F' -> 15
       | _ -> -1
+
+    let mem c str +      let len = String.length str in
+      let rec loop i +        if i >= len then false
+        else if String.unsafe_get str i = c then true
+        else loop (i+1)
+      in
+      loop 0
 end

 module String = struct
@@ -246,6 +255,24 @@ module String = struct
       List.map f (explode str)

     let spaces n = String.make n ' '
+
+    let span str accept +      let len = String.length str in
+      let rec loop i +        if i >= len then len
+        else if Char.mem (String.unsafe_get str i) accept then loop (i+1)
+        else i
+      in
+      loop 0
+
+    let cspan str reject +      let len = String.length str in
+      let rec loop i +        if i >= len then len
+        else if Char.mem (String.unsafe_get str i) reject then i
+        else loop (i+1)
+      in
+      loop 0
 end

 let (//) = Filename.concat
diff --git a/common/mlstdutils/std_utils.mli b/common/mlstdutils/std_utils.mli
index 686d4193f..b61b9bb02 100644
--- a/common/mlstdutils/std_utils.mli
+++ b/common/mlstdutils/std_utils.mli
@@ -41,6 +41,9 @@ module Char : sig
     val hexdigit : char -> int
     (** Return the value of a hex digit.  If the char is not in
         the set [[0-9a-fA-F]] then this returns [-1]. *)
+
+    val mem : char -> string -> bool
+    (** [mem c str] returns true if the byte [c] is contained in [str]. *)
 end
 (** Override the Char module from stdlib. *)

@@ -109,6 +112,15 @@ module String : sig
     (** Explode string, then map function over the characters. *)
     val spaces : int -> string
     (** [spaces n] creates a string of n spaces. *)
+    val span : string -> string -> int
+    val cspan : string -> string -> int
+    (** [span str accept] returns the length in bytes of the initial
+        segment of [str] which contains only bytes in [accept].
+
+        [cspan str reject] returns the length in bytes of the initial
+        segment of [str] which contains only bytes {!i not} in [reject].
+
+        These work exactly like the C functions [strspn] and [strcspn]. *)
 end
 (** Override the String module from stdlib. *)

diff --git a/common/mlstdutils/std_utils_tests.ml
b/common/mlstdutils/std_utils_tests.ml
index 6bc74fb63..2789766c6 100644
--- a/common/mlstdutils/std_utils_tests.ml
+++ b/common/mlstdutils/std_utils_tests.ml
@@ -50,6 +50,14 @@ and test_swap int_of_x x_of_int i s    assert_equal_int64 i
(int_of_x s);
   assert_equal_string s (x_of_int i)

+(* Test Std_utils.Char.mem. *)
+let test_char_mem ctx +  assert_bool "Char.mem" (Char.mem 'a'
"abc");
+  assert_bool "Char.mem" (Char.mem 'b' "abc");
+  assert_bool "Char.mem" (Char.mem 'c' "abc");
+  assert_bool "Char.mem" (not (Char.mem 'd'
"abc"));
+  assert_bool "Char.mem" (not (Char.mem 'a' ""))
+
 (* Test Std_utils.String.is_prefix. *)
 let test_string_is_prefix ctx    assert_bool "String.is_prefix,,"
(String.is_prefix "" "");
@@ -91,16 +99,29 @@ let test_string_lines_split ctx    assert_equal_stringlist
["A\nB"; ""] (String.lines_split "A\\\nB\n");
   assert_equal_stringlist ["A\nB\n"] (String.lines_split
"A\\\nB\\\n")

+(* Test Std_utils.String.span and cspan. *)
+let test_string_span ctx +  assert_equal_int 3 (String.span "aaabb"
"a");
+  assert_equal_int 3 (String.span "aaaba" "a");
+  assert_equal_int 3 (String.span "aba" "ab");
+  assert_equal_int 0 (String.span "" "ab");
+  assert_equal_int 3 (String.cspan "defab" "ab");
+  assert_equal_int 3 (String.cspan "defba" "ab");
+  assert_equal_int 3 (String.cspan "def" "ab");
+  assert_equal_int 0 (String.cspan "" "ab")
+
 (* Suites declaration. *)
 let suite    "mllib Std_utils" >:::
     [
       "subdirectory" >:: test_subdirectory;
       "numeric.byteswap" >:: test_byteswap;
+      "char.mem" >:: test_char_mem;
       "strings.is_prefix" >:: test_string_is_prefix;
       "strings.is_suffix" >:: test_string_is_suffix;
       "strings.find" >:: test_string_find;
       "strings.lines_split" >:: test_string_lines_split;
+      "strings.span" >:: test_string_span;
     ]

 let () --
2.13.0