diff --git a/doc/content/xapi/cli/_index.md b/doc/content/xapi/cli/_index.md index 6715e7288c0..093e1576aaf 100644 --- a/doc/content/xapi/cli/_index.md +++ b/doc/content/xapi/cli/_index.md @@ -156,7 +156,7 @@ So each function receives a printer for sending text output to the xe client, an let mac = List.assoc_default "mac" params "" in let network = Client.Network.get_by_uuid rpc session_id network in let pifs = List.assoc "pif-uuids" params in - let uuids = String.split ',' pifs in + let uuids = String.split_on_char ',' pifs in let pifs = List.map (fun uuid -> Client.PIF.get_by_uuid rpc session_id uuid) uuids in let mode = Record_util.bond_mode_of_string (List.assoc_default "mode" params "") in let properties = read_map_params "properties" params in diff --git a/ocaml/database/parse_db_conf.ml b/ocaml/database/parse_db_conf.ml index 67aa5c70d80..d1c7482c080 100644 --- a/ocaml/database/parse_db_conf.ml +++ b/ocaml/database/parse_db_conf.ml @@ -13,7 +13,6 @@ *) (* !!! This needs to be moved out of xapi and into the database directory; probably being merged with db_connections !!! *) -open Xapi_stdext_std.Xstringext open Xapi_stdext_unix module D = Debug.Make (struct let name = "parse_db_conf" end) @@ -110,7 +109,7 @@ let parse_db_conf s = let conf = Unixext.string_of_file s in let lines : string list ref = ref [] in let consume_line () = lines := List.tl !lines in - lines := String.split '\n' conf ; + lines := String.split_on_char '\n' conf ; List.iter (fun line -> debug "%s" line) !lines ; let read_block () = let path_line = List.hd !lines in @@ -120,7 +119,7 @@ let parse_db_conf s = while !lines <> [] && List.hd !lines <> "" do let line = List.hd !lines in key_values := - ( match String.split ':' line with + ( match String.split_on_char ':' line with | k :: vs -> ( String.lowercase_ascii k , String.lowercase_ascii (String.concat ":" vs) diff --git a/ocaml/database/redo_log.ml b/ocaml/database/redo_log.ml index 8c2c95928d7..42175580f83 100644 --- a/ocaml/database/redo_log.ml +++ b/ocaml/database/redo_log.ml @@ -12,7 +12,6 @@ * GNU Lesser General Public License for more details. *) open Xapi_stdext_pervasives.Pervasiveext -open Xapi_stdext_std.Xstringext open Xapi_stdext_unix let with_lock = Xapi_stdext_threads.Threadext.Mutex.execute diff --git a/ocaml/doc/dune b/ocaml/doc/dune index 061ba778232..618de9e30fb 100644 --- a/ocaml/doc/dune +++ b/ocaml/doc/dune @@ -1,7 +1,7 @@ (executable (modes exe) (name jsapi) - (libraries + (libraries mustache rpclib.core rpclib.json @@ -10,7 +10,6 @@ xapi-consts xapi-datamodel xapi-stdext-pervasives - xapi-stdext-std xapi-stdext-unix ) (preprocess (pps ppx_deriving_rpc)) diff --git a/ocaml/doc/jsapi.ml b/ocaml/doc/jsapi.ml index fa891e57743..1fef7fabf00 100644 --- a/ocaml/doc/jsapi.ml +++ b/ocaml/doc/jsapi.ml @@ -12,7 +12,6 @@ * GNU Lesser General Public License for more details. *) -open Xapi_stdext_std.Xstringext open Xapi_stdext_pervasives.Pervasiveext module Unixext = Xapi_stdext_unix.Unixext open Datamodel_types diff --git a/ocaml/idl/markdown_backend.ml b/ocaml/idl/markdown_backend.ml index ca067ef9dcb..3125d850b85 100644 --- a/ocaml/idl/markdown_backend.ml +++ b/ocaml/idl/markdown_backend.ml @@ -43,41 +43,41 @@ let compare_case_ins x y = compare (String.lowercase_ascii x) (String.lowercase_ascii y) let escape s = - let esc_char = function + let replace = function | '\\' -> - "\" + Some "\" | '*' -> - "*" + Some "*" | '_' -> - "_" + Some "_" | '{' -> - "{" + Some "{" | '}' -> - "}" + Some "}" | '[' -> - "[" + Some "[" | ']' -> - "]" + Some "]" | '(' -> - "(" + Some "(" | ')' -> - ")" + Some ")" | '>' -> - ">" + Some ">" | '<' -> - "<" + Some "<" | '#' -> - "#" + Some "#" | '+' -> - "+" + Some "+" | '-' -> - "-" + Some "-" | '!' -> - "!" - | c -> - String.make 1 c + Some "!" + | _ -> + None in - String.to_seq s |> Seq.map esc_char |> List.of_seq |> String.concat "" + Xapi_stdext_std.Xstringext.String.replaced ~replace s let rec of_ty_verbatim = function | SecretString | String -> diff --git a/ocaml/idl/ocaml_backend/gen_rbac.ml b/ocaml/idl/ocaml_backend/gen_rbac.ml index 7914dba96dd..ad17e10db5f 100644 --- a/ocaml/idl/ocaml_backend/gen_rbac.ml +++ b/ocaml/idl/ocaml_backend/gen_rbac.ml @@ -70,11 +70,10 @@ let role_uuid name = Option.get (hash2uuid name) let permission_description = "A basic permission" let permission_name wire_name = - let open Xapi_stdext_std in let s1 = replace_char (Printf.sprintf "permission_%s" wire_name) '.' '_' in let s2 = replace_char s1 '/' '_' in - let s3 = Xstringext.String.replace "*" "WILDCHAR" s2 in - Xstringext.String.replace ":" "_" s3 + let s3 = Xapi_stdext_std.Xstringext.String.replace '*' ~by:"WILDCHAR" s2 in + replace_char s3 ':' '_' let permission_index = ref 0 diff --git a/ocaml/libs/http-lib/dune b/ocaml/libs/http-lib/dune index 4e8d255b6bd..ac6db1b23d4 100644 --- a/ocaml/libs/http-lib/dune +++ b/ocaml/libs/http-lib/dune @@ -63,6 +63,7 @@ xapi-backtrace xapi-log xapi-stdext-pervasives + xapi-stdext-std xapi-stdext-threads xapi-stdext-unix)) diff --git a/ocaml/libs/http-lib/http_svr.ml b/ocaml/libs/http-lib/http_svr.ml index 7c5ecc393d1..6271c5ad987 100644 --- a/ocaml/libs/http-lib/http_svr.ml +++ b/ocaml/libs/http-lib/http_svr.ml @@ -331,28 +331,22 @@ module Server = struct x.handlers [] end -let escape uri = - (* from xapi-stdext-std xstringext *) - let escaped ~rules string = - let aux h t = - ( if List.mem_assoc h rules then - List.assoc h rules - else - Astring.String.of_char h +let escape_html uri = + Xapi_stdext_std.Xstringext.String.replaced + ~replace:(function + | '<' -> + Some "<" + | '>' -> + Some ">" + | '\'' -> + Some "'" + | '"' -> + Some """ + | '&' -> + Some "&" + | _ -> + None ) - :: t - in - String.concat "" (Astring.String.fold_right aux string []) - in - escaped - ~rules: - [ - ('<', "<") - ; ('>', ">") - ; ('\'', "'") - ; ('"', """) - ; ('&', "&") - ] uri exception Generic_error of string @@ -508,7 +502,7 @@ let read_request ?proxy_seen ~read_timeout ~total_timeout ~max_length fd = ) | exc -> response_internal_error exc fd - ~extra:(escape (Printexc.to_string exc)) + ~extra:(escape_html (Printexc.to_string exc)) ) ; (None, None) @@ -557,7 +551,7 @@ let handle_one (x : 'a Server.t) ss context req = ) | exc -> response_internal_error ~req exc ss - ~extra:(escape (Printexc.to_string exc)) + ~extra:(escape_html (Printexc.to_string exc)) ) ; !finished diff --git a/ocaml/libs/http-lib/http_svr.mli b/ocaml/libs/http-lib/http_svr.mli index 61b49e7b3f2..cf335592d7b 100644 --- a/ocaml/libs/http-lib/http_svr.mli +++ b/ocaml/libs/http-lib/http_svr.mli @@ -68,6 +68,9 @@ val start : val handle_one : 'a Server.t -> Unix.file_descr -> 'a -> Http.Request.t -> bool +val escape_html : string -> string +(** Escapes HTML: replaces characters with their character references *) + exception Socket_not_found val stop : socket -> unit diff --git a/ocaml/libs/sexpr/sExpr.ml b/ocaml/libs/sexpr/sExpr.ml index 3637ac6abf5..c9794265d49 100644 --- a/ocaml/libs/sexpr/sExpr.ml +++ b/ocaml/libs/sexpr/sExpr.ml @@ -28,7 +28,7 @@ let is_escape_char = function '\\' | '\'' -> true | _ -> false (* XXX: This escapes "'c'" and "\'c\'" to "\\'c\\'". * They are both unescaped as "'c'". They have been ported * to make sure that this corner case is left unchanged. - * It is worth investigating the use of + * It is worth investigating the use of * - Astring.String.Ascii.escape_string * - Astring.String.Ascii.unescape * that have guaranteed invariants and optimised performances *) diff --git a/ocaml/libs/tgroup/dune b/ocaml/libs/tgroup/dune index 6c152a5c2ef..12070176651 100644 --- a/ocaml/libs/tgroup/dune +++ b/ocaml/libs/tgroup/dune @@ -2,7 +2,7 @@ (name tgroup) (modules tgroup) (public_name tgroup) - (libraries unix xapi-log xapi-stdext-unix xapi-stdext-std)) + (libraries astring unix xapi-log xapi-stdext-unix)) (test (name test_tgroup) diff --git a/ocaml/libs/tgroup/tgroup.ml b/ocaml/libs/tgroup/tgroup.ml index 071a9dfe0d2..e5b163fea60 100644 --- a/ocaml/libs/tgroup/tgroup.ml +++ b/ocaml/libs/tgroup/tgroup.ml @@ -70,8 +70,7 @@ module Group = struct | _ -> false - let sanitize s = - Xapi_stdext_std.Xstringext.String.filter_chars s is_alphanum + let sanitize s = Astring.String.filter is_alphanum s let make ?user_agent subject_sid = let user_agent = diff --git a/ocaml/libs/xapi-stdext/lib/xapi-stdext-std/bench/bench_xstringext.ml b/ocaml/libs/xapi-stdext/lib/xapi-stdext-std/bench/bench_xstringext.ml index 52897ee2a0c..d302737b1b1 100644 --- a/ocaml/libs/xapi-stdext/lib/xapi-stdext-std/bench/bench_xstringext.ml +++ b/ocaml/libs/xapi-stdext/lib/xapi-stdext-std/bench/bench_xstringext.ml @@ -8,36 +8,42 @@ let make_string len = String.init len (fun i -> Char.chr (33 + (i mod 94))) let escape_rules = [('a', "[A]"); ('e', "[E]"); ('i', "[I]"); ('o', "[O]"); ('u', "[U]")] -(* Reference implementation from xstringext_test.ml *) -let escaped_spec ?rules string = - match rules with - | None -> - String.escaped string - | Some rules -> - let apply_rules char = - match List.assoc_opt char rules with - | None -> - Seq.return char - | Some replacement -> - String.to_seq replacement - in - string |> String.to_seq |> Seq.concat_map apply_rules |> String.of_seq +let replace = function + | 'a' -> + Some "[A]" + | 'e' -> + Some "[E]" + | 'i' -> + Some "[I]" + | 'o' -> + Some "[O]" + | 'u' -> + Some "[U]" + | _ -> + None + +(* Reference implementation using lists *) +let replaced_spec ~rules string = + let apply_rules char = List.assoc_opt char rules in + XString.replaced ~replace:apply_rules string -let escaped_benchmark n = +let replaced ~rules string = XString.replaced ~replace:rules string + +let replaced_benchmark n = let s = make_string n in - Staged.stage @@ fun () -> ignore (XString.escaped ~rules:escape_rules s) + Staged.stage @@ fun () -> ignore (replaced ~rules:replace s) -let escaped_spec_benchmark n = +let replaced_spec_benchmark n = let s = make_string n in - Staged.stage @@ fun () -> ignore (escaped_spec ~rules:escape_rules s) + Staged.stage @@ fun () -> ignore (replaced_spec ~rules:escape_rules s) -let test_escaped = - Test.make_indexed ~name:"escaped" ~fmt:"%s %d" ~args:[100; 500; 1000] - escaped_benchmark +let test_replaced = + Test.make_indexed ~name:"replaced" ~fmt:"%s %d" ~args:[100; 500; 1000] + replaced_benchmark -let test_escaped_spec = - Test.make_indexed ~name:"escaped-spec" ~fmt:"%s %d" ~args:[100; 500; 1000] - escaped_spec_benchmark +let test_replaced_spec = + Test.make_indexed ~name:"replaced-spec" ~fmt:"%s %d" ~args:[100; 500; 1000] + replaced_spec_benchmark let benchmark () = let ols = @@ -50,8 +56,8 @@ let benchmark () = Benchmark.cfg ~limit:2000 ~quota:(Time.second 0.5) ~kde:(Some 1000) () in let test = - Test.make_grouped ~name:"escaped-comparison" - [test_escaped; test_escaped_spec] + Test.make_grouped ~name:"replaced-comparison" + [test_replaced; test_replaced_spec] in let raw_results = Benchmark.all cfg instances test in let results = @@ -97,8 +103,10 @@ let () = List.iter (fun size -> Printf.printf "String size %s:\n" size ; - let opt_test = Printf.sprintf "escaped-comparison/escaped %s" size in - let ref_test = Printf.sprintf "escaped-comparison/escaped-spec %s" size in + let opt_test = Printf.sprintf "replaced-comparison/replaced %s" size in + let ref_test = + Printf.sprintf "replaced-comparison/replaced-spec %s" size + in match (get_timing opt_test, get_timing ref_test) with | Some opt_time, Some ref_time -> let improvement = (ref_time -. opt_time) /. ref_time *. 100.0 in diff --git a/ocaml/libs/xapi-stdext/lib/xapi-stdext-std/xstringext.ml b/ocaml/libs/xapi-stdext/lib/xapi-stdext-std/xstringext.ml index 4e5379d7b36..854245b9b6d 100644 --- a/ocaml/libs/xapi-stdext/lib/xapi-stdext-std/xstringext.ml +++ b/ocaml/libs/xapi-stdext/lib/xapi-stdext-std/xstringext.ml @@ -12,79 +12,18 @@ * GNU Lesser General Public License for more details. *) module String = struct - include String - - let of_char c = String.make 1 c - - let rev_map f string = - let n = length string in - String.init n (fun i -> f string.[n - i - 1]) - - let rev_iter f string = - for i = length string - 1 downto 0 do - f string.[i] - done - - let fold_left f accu string = - let accu = ref accu in - for i = 0 to length string - 1 do - accu := f !accu string.[i] - done ; - !accu - - let fold_right f string accu = - let accu = ref accu in - for i = length string - 1 downto 0 do - accu := f string.[i] !accu - done ; - !accu - - (** Returns true for whitespace characters, false otherwise *) - let isspace = function ' ' | '\n' | '\r' | '\t' -> true | _ -> false - - let split_f p str = - let split_one seq = - let not_p c = not (p c) in - let a = Seq.take_while not_p seq in - let b = Seq.drop_while not_p seq in - (a, b) - in - let drop seq = Seq.drop_while p seq in - let rec split acc chars = - if Seq.is_empty chars then - acc - else - let a, b = split_one chars in - let b = drop b in - let acc = if Seq.is_empty a then acc else Seq.cons a acc in - split acc b - in - String.to_seq str - |> split Seq.empty - |> Seq.map String.of_seq - |> List.of_seq - |> List.rev - - let index_opt s c = - let rec loop i = - if String.length s = i then - None - else if s.[i] = c then - Some i - else - loop (i + 1) - in - loop 0 + let sub_to_end s start = + let length = String.length s in + String.sub s start (length - start) - let rec split ?(limit = -1) c s = - let i = match index_opt s c with Some x -> x | None -> -1 in - let nlimit = if limit = -1 || limit = 0 then limit else limit - 1 in - if i = -1 || nlimit = 0 then - [s] - else - let a = String.sub s 0 i - and b = String.sub s (i + 1) (String.length s - i - 1) in - a :: split ~limit:nlimit c b + let rec split ~limit sep s = + match (String.index_opt s sep, limit < 2) with + | None, _ | _, true -> + [s] + | Some pos, false -> + let first = String.sub s 0 pos in + let rest = sub_to_end s (pos + 1) in + first :: split ~limit:(limit - 1) sep rest let rtrim s = let n = String.length s in @@ -93,71 +32,6 @@ module String = struct else s - (** has_substr str sub returns true if sub is a substring of str. Simple, naive, slow. *) - let has_substr str sub = - if String.length sub > String.length str then - false - else - let result = ref false in - for start = 0 to String.length str - String.length sub do - if String.sub str start (String.length sub) = sub then result := true - done ; - !result - - (** find all occurences of needle in haystack and return all their respective index *) - let find_all needle haystack = - let m = String.length needle and n = String.length haystack in - if m > n then - [] - else - let i = ref 0 and found = ref [] in - while !i < n - m + 1 do - if String.sub haystack !i m = needle then ( - found := !i :: !found ; - i := !i + m - ) else - incr i - done ; - List.rev !found - - (* replace all @f substring in @s by @t *) - let replace f t s = - let indexes = find_all f s in - let n = List.length indexes in - if n > 0 then ( - let len_f = String.length f and len_t = String.length t in - let new_len = String.length s + (n * len_t) - (n * len_f) in - let new_b = Bytes.make new_len '\000' in - let orig_offset = ref 0 and dest_offset = ref 0 in - List.iter - (fun h -> - let len = h - !orig_offset in - Bytes.blit_string s !orig_offset new_b !dest_offset len ; - Bytes.blit_string t 0 new_b (!dest_offset + len) len_t ; - orig_offset := !orig_offset + len + len_f ; - dest_offset := !dest_offset + len + len_t - ) - indexes ; - Bytes.blit_string s !orig_offset new_b !dest_offset - (String.length s - !orig_offset) ; - Bytes.unsafe_to_string new_b - ) else - s - - let filter_chars s valid = - let badchars = ref false in - let buf = Buffer.create 0 in - for i = 0 to String.length s - 1 do - if !badchars then ( - if valid s.[i] then - Buffer.add_char buf s.[i] - ) else if not (valid s.[i]) then ( - Buffer.add_substring buf s 0 i ; - badchars := true - ) - done ; - if !badchars then Buffer.contents buf else s - let map_unlikely s f = let changed = ref false in let m = ref 0 in @@ -178,18 +52,10 @@ module String = struct ) else s - let escaped ?rules s = - match rules with - | None -> - String.escaped s - | Some rules -> - map_unlikely s (fun c -> List.assoc_opt c rules) - - let sub_to_end s start = - let length = String.length s in - String.sub s start (length - start) - - let sub_before c s = String.sub s 0 (String.index s c) + let replace char ~by s = + let replaceable = Stdlib.Char.equal char in + let get_replacement c = if replaceable c then Some by else None in + map_unlikely s get_replacement - let sub_after c s = sub_to_end s (String.index s c + 1) + let replaced ~replace s = map_unlikely s replace end diff --git a/ocaml/libs/xapi-stdext/lib/xapi-stdext-std/xstringext.mli b/ocaml/libs/xapi-stdext/lib/xapi-stdext-std/xstringext.mli index 1f27490493d..62a1053d883 100644 --- a/ocaml/libs/xapi-stdext/lib/xapi-stdext-std/xstringext.mli +++ b/ocaml/libs/xapi-stdext/lib/xapi-stdext-std/xstringext.mli @@ -12,63 +12,21 @@ * GNU Lesser General Public License for more details. *) module String : sig - include module type of String + val replaced : replace:(char -> string option) -> string -> string + (** [replaced ~replacement str] applies [replace] to all characters in [str] + and when it returns [Some rep] the character is replaced with [rep] in + the resulting string *) - val of_char : char -> string + val replace : char -> by:string -> string -> string + (** [replace ch ~by s] replaces all the occurrences of [ch] in [s] by [~by] + *) - val rev_map : (char -> char) -> string -> string - (** Map a string to a string, applying the given function in reverse - order. *) - - val rev_iter : (char -> unit) -> string -> unit - (** Iterate over the characters in a string in reverse order. *) - - val fold_left : ('a -> char -> 'a) -> 'a -> string -> 'a - (** Fold over the characters in a string. *) - - val fold_right : (char -> 'a -> 'a) -> string -> 'a -> 'a - (** Iterate over the characters in a string in reverse order. *) - - val isspace : char -> bool - (** True if the character is whitespace *) - - val escaped : ?rules:(char * string) list -> string -> string - (** Backward-compatible string escaping, defaulting to the built-in - OCaml string escaping but allowing an arbitrary mapping from characters - to strings. *) - - val split_f : (char -> bool) -> string -> string list - (** Take a predicate and a string, return a list of strings separated by - runs of characters where the predicate was true. Avoid if possible, it's - very costly to execute. *) - - val split : ?limit:int -> char -> string -> string list + val split : limit:int -> char -> string -> string list (** split a string on a single char *) val rtrim : string -> string (** FIXME document me|remove me if similar to strip *) - val has_substr : string -> string -> bool - (** True if sub is a substr of str *) - - val find_all : string -> string -> int list - (** find all occurences of needle in haystack and return all their respective index *) - - val replace : string -> string -> string -> string - (** replace all [f] substring in [s] by [t] *) - - val filter_chars : string -> (char -> bool) -> string - (** filter chars from a string *) - - val map_unlikely : string -> (char -> string option) -> string - (** map a string trying to fill the buffer by chunk *) - val sub_to_end : string -> int -> string (** a substring from the specified position to the end of the string *) - - val sub_before : char -> string -> string - (** a substring from the start of the string to the first occurrence of a given character, excluding the character *) - - val sub_after : char -> string -> string - (** a substring from the first occurrence of a given character to the end of the string, excluding the character *) end diff --git a/ocaml/libs/xapi-stdext/lib/xapi-stdext-std/xstringext_test.ml b/ocaml/libs/xapi-stdext/lib/xapi-stdext-std/xstringext_test.ml index 9b7eb2674a1..4fe39225785 100644 --- a/ocaml/libs/xapi-stdext/lib/xapi-stdext-std/xstringext_test.ml +++ b/ocaml/libs/xapi-stdext/lib/xapi-stdext-std/xstringext_test.ml @@ -13,10 +13,6 @@ module XString = Xapi_stdext_std.Xstringext.String -let test_boolean tested_f (name, case, expected) = - let check () = Alcotest.(check bool) name expected (tested_f case) in - (name, `Quick, check) - let test_string tested_f (name, case, expected) = let check () = Alcotest.(check string) name expected (tested_f case) in (name, `Quick, check) @@ -27,47 +23,14 @@ let test_list tested_f (name, case, expected) = in (name, `Quick, check) -let test_rev_map = - let spec_rev = [("", ""); ("foo bar", "rab oof")] in - let spec_func = [("id", Fun.id); ("uppercase_ascii", Char.uppercase_ascii)] in - let test (f_name, f) (case, expected) = - let expected = String.map f expected in - let name = - Printf.sprintf {|"%s" produces "%s" (%s)|} case expected f_name - in - test_string (XString.rev_map f) (name, case, expected) - in - let tests = - (* Generate the product of the two lists to generate the tests *) - List.concat_map (fun func -> List.map (test func) spec_rev) spec_func - in - ("rev_map", tests) - let test_split = - let test ?limit (splitter, splitted, expected) = - let split, name = - match limit with - | None -> - let name = Printf.sprintf {|'%c' splits "%s"|} splitter splitted in - (* limit being set to -1 is the same as not using the parameter *) - let split = XString.split ~limit:(-1) in - (split, name) - | Some limit -> - let name = - Printf.sprintf {|'%c' splits "%s" with limit %i|} splitter splitted - limit - in - let split = XString.split ~limit in - (split, name) + let test limit (splitter, splitted, expected) = + let split = XString.split ~limit in + let name = + Printf.sprintf {|'%c' splits "%s" with limit %i|} splitter splitted limit in test_list (split splitter) (name, splitted, expected) in - let specs_no_limit = - [ - ('.', "...", [""; ""; ""; ""]); ('.', "foo.bar.baz", ["foo"; "bar"; "baz"]) - ] - in - let tests_no_limit = List.map test specs_no_limit in let specs_limit = [ (0, [('.', "...", ["..."]); ('.', "foo.bar.baz", ["foo.bar.baz"])]) @@ -82,50 +45,12 @@ let test_split = ; (4, [('.', "...", [""; ""; ""; ""])]) ] in - let tests_limit = + let tests = List.concat_map - (fun (limit, spec) -> List.map (test ~limit) spec) + (fun (limit, spec) -> List.map (test limit) spec) specs_limit in - ("split", List.concat [tests_no_limit; tests_limit]) - -let test_split_f = - let specs = - [ - (XString.isspace, "foo bar", ["foo"; "bar"]) - ; (XString.isspace, "foo bar", ["foo"; "bar"]) - ; (XString.isspace, "foo \n\t\r bar", ["foo"; "bar"]) - ; (XString.isspace, " foo bar ", ["foo"; "bar"]) - ; (XString.isspace, "", []) - ; (XString.isspace, " ", []) - ] - in - let test (splitter, splitted, expected) = - let name = Printf.sprintf {|"%s"|} (String.escaped splitted) in - test_list (XString.split_f splitter) (name, splitted, expected) - in - let tests = List.map test specs in - ("split_f", tests) - -let test_has_substr = - let spec = - [ - ("", "", true) - ; ("", "foo bar", true) - ; ("f", "foof", true) - ; ("foofo", "foof", false) - ; ("foof", "foof", true) - ; ("f", "foof", true) - ; ("fo", "foof", true) - ; ("of", "foof", true) - ; ("ff", "foof", false) - ] - in - let test (contained, container, expected) = - let name = Printf.sprintf {|"%s" in "%s"|} contained container in - test_boolean (XString.has_substr container) (name, contained, expected) - in - ("has_substr", List.map test spec) + ("split", tests) let test_rtrim = let spec = @@ -147,49 +72,4 @@ let test_rtrim = in ("rtrim", List.map test spec) -(** Simple implementation of escaped for testing against *) -let escaped_spec ?rules string = - match rules with - | None -> - String.escaped string - | Some rules -> - let apply_rules char = - match List.assoc_opt char rules with - | None -> - Seq.return char - | Some replacement -> - String.to_seq replacement - in - string |> String.to_seq |> Seq.concat_map apply_rules |> String.of_seq - -let test_escaped = - let open QCheck2 in - (* Generator for escape rules: list of (char, string) mappings *) - let gen_rules = - let open Gen in - let gen_rule = pair char (string_size (int_range 0 5) ~gen:char) in - list gen_rule - in - (* Generator for test input: string and optional rules *) - let gen_input = Gen.pair Gen.string (Gen.opt gen_rules) in - let property (s, rules) = - let expected = escaped_spec ?rules s in - let actual = XString.escaped ?rules s in - String.equal expected actual - in - let test = - Test.make ~name:"escaped matches reference implementation" ~count:1000 - gen_input property - in - ("escaped", [QCheck_alcotest.to_alcotest test]) - -let () = - Alcotest.run "Xstringext" - [ - test_rev_map - ; test_split - ; test_split_f - ; test_has_substr - ; test_rtrim - ; test_escaped - ] +let () = Alcotest.run "Xstringext" [test_split; test_rtrim] diff --git a/ocaml/quicktest/quicktest_http.ml b/ocaml/quicktest/quicktest_http.ml index 0320cf12ab3..d718a61a9e7 100644 --- a/ocaml/quicktest/quicktest_http.ml +++ b/ocaml/quicktest/quicktest_http.ml @@ -34,7 +34,7 @@ module Uds = struct with_channel_aux fd func let http_response_code d = - match Xapi_stdext_std.Xstringext.String.split ' ' d with + match String.split_on_char ' ' d with | _ :: code :: _ -> int_of_string code | _ -> @@ -99,7 +99,7 @@ module Cookies = struct match body with | first_line :: _ -> D.warn "expected = [%s]; received = [%s]" expected first_line ; - Xapi_stdext_std.Xstringext.String.has_substr first_line expected + Astring.String.is_infix ~affix:first_line expected | _ -> false in @@ -210,9 +210,7 @@ module HTML_Escaping = struct let bad_command_exp = "<>'\\"&" let html_escaping expected cmd = - let check_result b = - Xapi_stdext_std.Xstringext.String.has_substr b expected - in + let check_result = Astring.String.is_infix ~affix:expected in let _, _, _, body = Uds.http_command Xapi_globs.unix_domain_socket cmd in match body with | first_line :: _ -> diff --git a/ocaml/rrd2csv/src/rrd2csv.ml b/ocaml/rrd2csv/src/rrd2csv.ml index 37e00f8148d..bb57fbbb8d3 100644 --- a/ocaml/rrd2csv/src/rrd2csv.ml +++ b/ocaml/rrd2csv/src/rrd2csv.ml @@ -149,10 +149,10 @@ module Ds_selector = struct let of_string str = let open Rrd in - let splitted = Xstringext.String.split ',' str in + let splitted = String.split_on_char ',' str in match splitted with | without_trailing_comma :: _ -> ( - let splitted = Xstringext.String.split ':' without_trailing_comma in + let splitted = String.split_on_char ':' without_trailing_comma in match splitted with | [cf; owner; uuid; metric] -> { @@ -185,14 +185,7 @@ module Ds_selector = struct let escape_metric s = let quote s = Printf.sprintf "\"%s\"" s in if String.contains s '"' then - quote - (Xstringext.String.map_unlikely s (function - | '\"' -> - Some "\"\"" - | _ -> - None - ) - ) + quote (Xstringext.String.replace '"' ~by:{|""|} s) else if String.contains s ',' || String.contains s '\n' then quote s else diff --git a/ocaml/tests/test_guest_agent.ml b/ocaml/tests/test_guest_agent.ml index 6bc0f227c7b..94774ac10c7 100644 --- a/ocaml/tests/test_guest_agent.ml +++ b/ocaml/tests/test_guest_agent.ml @@ -42,9 +42,7 @@ module Networks = Generic.MakeStateless (struct ) let construct_tree tree path = - let nodes = - Xapi_stdext_std.Xstringext.String.split_f (fun s -> s = '/') path - in + let nodes = Astring.String.cuts ~empty:false ~sep:"/" path in add_path_to_tree tree nodes let rec list_helper children = function @@ -60,9 +58,7 @@ module Networks = Generic.MakeStateless (struct ) let list (T (_root, children)) path = - let nodes = - Xapi_stdext_std.Xstringext.String.split_f (fun s -> s = '/') path - in + let nodes = Astring.String.cuts ~empty:false ~sep:"/" path in list_helper children nodes let transform input = @@ -231,9 +227,7 @@ module Initial_guest_metrics = Generic.MakeStateless (struct ) let construct_mtree mtree (path, leaf_value) = - let nodes = - Xapi_stdext_std.Xstringext.String.split_f (fun s -> s = '/') path - in + let nodes = Astring.String.cuts ~empty:false ~sep:"/" path in add_leaf_to_mtree nodes leaf_value mtree let rec list_helper children = function @@ -254,9 +248,7 @@ module Initial_guest_metrics = Generic.MakeStateless (struct | Lf (_, _) -> [] | Mt (_, children) -> - let nodes = - Xapi_stdext_std.Xstringext.String.split_f (fun s -> s = '/') path - in + let nodes = Astring.String.cuts ~empty:false ~sep:"/" path in list_helper children nodes let rec lookup_helper mtree = function @@ -274,9 +266,7 @@ module Initial_guest_metrics = Generic.MakeStateless (struct ) let lookup mtree path = - let nodes = - Xapi_stdext_std.Xstringext.String.split_f (fun s -> s = '/') path - in + let nodes = Astring.String.cuts ~empty:false ~sep:"/" path in lookup_helper mtree nodes let transform input = diff --git a/ocaml/xapi-cli-server/cli_printer.ml b/ocaml/xapi-cli-server/cli_printer.ml index 1fc1d5586fd..bb627812924 100644 --- a/ocaml/xapi-cli-server/cli_printer.ml +++ b/ocaml/xapi-cli-server/cli_printer.ml @@ -47,9 +47,15 @@ let rec multi_line_record r = (* Used to escape commas in --minimal mode *) let escape_commas x = - (* Escaping rules: *) - let rules = [(',', "\\,"); (* , -> \, *) ('\\', "\\\\") (* \ -> \\ *)] in - Xapi_stdext_std.Xstringext.String.escaped ~rules x + let replace = function + | ',' -> + Some "\\," + | '\\' -> + Some "\\\\" + | _ -> + None + in + Xapi_stdext_std.Xstringext.String.replaced ~replace x let make_printer sock minimal = let buffer = ref [] in diff --git a/ocaml/xapi/audit_log.ml b/ocaml/xapi/audit_log.ml index 0563b2c4fe3..473df72f39e 100644 --- a/ocaml/xapi/audit_log.ml +++ b/ocaml/xapi/audit_log.ml @@ -102,12 +102,8 @@ let transfer_all_audit_files fd_out ?filter since = atransfer_try_gz "" (* map the ISO8601 timestamp format into the one in our logs *) -let log_timestamp_of_iso8601 iso8601_timestamp = - let module Xstringext = Xapi_stdext_std.Xstringext in - let step1 = iso8601_timestamp in - let step2 = Xstringext.String.replace "-" "" step1 in - let step3 = Xstringext.String.replace "Z" "" step2 in - step3 +let log_timestamp_of_iso8601 iso8601 = + Astring.String.filter (function '-' | 'Z' -> false | _ -> true) iso8601 (* Assume that RBAC access for the session_id already verified by xapi_http.ml diff --git a/ocaml/xapi/authx.ml b/ocaml/xapi/authx.ml index 87d85e40332..7236c793fb4 100644 --- a/ocaml/xapi/authx.ml +++ b/ocaml/xapi/authx.ml @@ -65,7 +65,7 @@ module AuthX : Auth_signature.AUTH_MODULE = struct | [] -> raise Not_found | line :: lines -> ( - let recs = Xapi_stdext_std.Xstringext.String.split ':' line in + let recs = String.split_on_char ':' line in let username = List.nth recs 0 in let uid = List.nth recs 2 in match fn username uid recs with @@ -291,9 +291,8 @@ module AuthX : Auth_signature.AUTH_MODULE = struct | _ -> raise Not_found - (* - In addition, there are some event hooks that auth modules implement as follows: -*) + (* In addition, there are some event hooks that auth modules implement as + follows: *) (* unit on_enable(((string*string) list) config_params) diff --git a/ocaml/xapi/config_file_sync.ml b/ocaml/xapi/config_file_sync.ml index b765f1ceae6..cf877745290 100644 --- a/ocaml/xapi/config_file_sync.ml +++ b/ocaml/xapi/config_file_sync.ml @@ -15,7 +15,6 @@ module D = Debug.Make (struct let name = "config_file_sync" end) open D -open Xapi_stdext_std.Xstringext let superuser = "root" @@ -58,7 +57,8 @@ let config_file_sync_handler (req : Http.Request.t) s _ = Xapi_http.with_context "Syncing dom0 config files over HTTP" req s (fun __context -> let uri = - String.split '/' req.Http.Request.path |> List.filter (fun x -> x <> "") + String.split_on_char '/' req.Http.Request.path + |> List.filter (fun x -> x <> "") in req.Http.Request.close <- true ; debug "sending headers" ; diff --git a/ocaml/xapi/dbsync_slave.ml b/ocaml/xapi/dbsync_slave.ml index ff325b7259e..ea5dddf8f82 100644 --- a/ocaml/xapi/dbsync_slave.ml +++ b/ocaml/xapi/dbsync_slave.ml @@ -17,7 +17,6 @@ *) module Rrdd = Rrd_client.Client -open Xapi_stdext_std.Xstringext module Unixext = Xapi_stdext_unix.Unixext module Date = Clock.Date open Create_misc @@ -75,9 +74,9 @@ let get_start_time () = match Unixext.string_of_file "/proc/stat" |> String.trim - |> String.split '\n' + |> String.split_on_char '\n' |> List.find (fun s -> String.starts_with ~prefix:"btime" s) - |> String.split ' ' + |> String.split_on_char ' ' with | _ :: btime :: _ -> let boot_time = Date.of_unix_time (float_of_string btime) in @@ -111,7 +110,7 @@ let refresh_localhost_info ~__context info = | None -> [] | Some {capabilities; _} -> - String.split ' ' capabilities + String.split_on_char ' ' capabilities in Db.Host.set_capabilities ~__context ~self:host ~value:caps ; Db.Host.set_address ~__context ~self:host ~value:(get_my_ip_addr ~__context) ; diff --git a/ocaml/xapi/extauth_plugin_ADpbis.ml b/ocaml/xapi/extauth_plugin_ADpbis.ml index ea2dedfccc6..b57b2c91cae 100644 --- a/ocaml/xapi/extauth_plugin_ADpbis.ml +++ b/ocaml/xapi/extauth_plugin_ADpbis.ml @@ -18,7 +18,7 @@ module D = Debug.Make (struct let name = "extauth_plugin_ADpbis" end) open D -open Xapi_stdext_std.Xstringext +module Stringext = Xapi_stdext_std.Xstringext.String let ( let@ ) = ( @@ ) @@ -92,6 +92,8 @@ module Lwsmd = struct ) end +let is_word_sep = function '(' | ')' | ' ' | '\t' | '.' -> true | _ -> false + let match_error_tag (lines : string list) = let err_catch_list = [ @@ -105,8 +107,7 @@ let match_error_tag (lines : string list) = ] in let split_to_words str = - let seps = ['('; ')'; ' '; '\t'; '.'] in - String.split_f (fun s -> List.exists (fun sep -> sep = s) seps) str + Astring.String.fields ~empty:false ~is_sep:is_word_sep str in let rec has_err lines err_pattern = match lines with @@ -131,9 +132,11 @@ let match_error_tag (lines : string list) = let extract_sid_from_group_list group_list = List.map (fun (_, v) -> - let v = String.replace ")" "" v in - let v = String.replace "sid =" "|" v in - let vs = String.split_f (fun c -> c = '|') v in + let vs = + Astring.String.filter (function ')' -> false | _ -> true) v + |> Astring.String.cuts ~empty:false ~sep:"sid =" + |> List.concat_map (Astring.String.cuts ~empty:false ~sep:"|") + in let sid = String.trim (List.nth vs 1) in debug "extract_sid_from_group_list get sid=[%s]" sid ; sid @@ -166,7 +169,8 @@ module AuthADlw : Auth_signature.AUTH_MODULE = struct Locking_helpers.Named_mutex.create "IS_SERVER_AVAILABLE" let splitlines s = - String.split_f (fun c -> c = '\n') (String.replace "#012" "\n" s) + Astring.String.cuts ~empty:false ~sep:"#012" s + |> List.concat_map (Astring.String.cuts ~empty:false ~sep:"\n") let pbis_common_with_password (password : string) (pbis_cmd : string) (pbis_args : string list) = @@ -238,7 +242,7 @@ module AuthADlw : Auth_signature.AUTH_MODULE = struct pbis_cmd ^ " " ^ List.fold_left (fun p pp -> p ^ " " ^ pp) " " pbis_args in let debug_cmd = - if String.has_substr debug_cmd "--password" then + if Astring.String.is_infix ~affix:"--password" debug_cmd then "(omitted for security)" else debug_cmd @@ -348,9 +352,9 @@ module AuthADlw : Auth_signature.AUTH_MODULE = struct if !exited_code <> 0 then ( error "execute '%s': exit_code=[%d] output=[%s]" debug_cmd !exited_code - (String.replace "\n" ";" !output) ; + (Stringext.replace '\n' ~by:";" !output) ; let split_to_words s = - String.split_f (fun c -> c = '(' || c = ')' || c = '.' || c = ' ') s + Astring.String.fields ~empty:false ~is_sep:is_word_sep s in let revlines = List.rev @@ -416,7 +420,7 @@ module AuthADlw : Auth_signature.AUTH_MODULE = struct List.filter (fun l -> String.length l > 0) (splitlines !output) in let parse_line (acc, currkey) line = - let slices = String.split ~limit:2 ':' line in + let slices = Stringext.split ~limit:2 ':' line in debug "parse %s: currkey=[%s] line=[%s]" debug_cmd currkey line ; if List.length slices > 1 then ( let key = String.trim (List.hd slices) in @@ -619,7 +623,7 @@ module AuthADlw : Auth_signature.AUTH_MODULE = struct (* first, we try to authenticated user against our external user database *) (* pbis_common will raise an Auth_failure if external authentication fails *) let domain, user = - match String.split_f (fun c -> c = '\\') username with + match Astring.String.cuts ~empty:false ~sep:"\\" username with | [domain; user] -> (domain, user) | [user] -> @@ -976,7 +980,8 @@ module AuthADlw : Auth_signature.AUTH_MODULE = struct | "" -> [] | disabled_modules_string -> - String.split_f (fun c -> c = ',') disabled_modules_string + Astring.String.cuts ~empty:false ~sep:"," + disabled_modules_string with Not_found -> [] in let disabled_module_params = @@ -1113,8 +1118,8 @@ module AuthADlw : Auth_signature.AUTH_MODULE = struct in debug "execute %s: stdout=[%s],stderr=[%s]" pbis_force_domain_leave_script - (String.replace "\n" ";" output) - (String.replace "\n" ";" stderr) + (Stringext.replace '\n' ~by:";" output) + (Stringext.replace '\n' ~by:";" stderr) with e -> debug "exception executing %s: %s" pbis_force_domain_leave_script (ExnHelper.string_of_exn e) diff --git a/ocaml/xapi/extauth_plugin_ADwinbind.ml b/ocaml/xapi/extauth_plugin_ADwinbind.ml index 837dc429ca2..86d0e8c25b2 100644 --- a/ocaml/xapi/extauth_plugin_ADwinbind.ml +++ b/ocaml/xapi/extauth_plugin_ADwinbind.ml @@ -20,7 +20,6 @@ module D = Debug.Make (struct end) open D -open Xapi_stdext_std.Xstringext open Auth_signature module Scheduler = Xapi_stdext_threads_scheduler.Scheduler diff --git a/ocaml/xapi/fileserver.ml b/ocaml/xapi/fileserver.ml index 9820a55d5ce..8c83dac57f3 100644 --- a/ocaml/xapi/fileserver.ml +++ b/ocaml/xapi/fileserver.ml @@ -16,29 +16,17 @@ *) open Http -open Xapi_stdext_std.Xstringext +module Xstringext = Xapi_stdext_std.Xstringext.String module D = Debug.Make (struct let name = "fileserver" end) open D -let escape uri = - String.escaped - ~rules: - [ - ('<', "<") - ; ('>', ">") - ; ('\'', "'") - ; ('"', """) - ; ('&', "&") - ] - uri - let missing uri = "
\The \ requested URL " - ^ escape uri + ^ Http_svr.escape_html uri ^ " was not found on this server.