(*
* ExtString - Additional functions for string manipulations.
* Copyright (C) 2003 Nicolas Cannasse
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version,
* with the special exception on linking described in file LICENSE.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*)
exception Invalid_string
open ExtBytes
module String = struct
include String
#if OCAML < 402
let init len f =
let s = Bytes.create len in
for i = 0 to len - 1 do
Bytes.unsafe_set s i (f i)
done;
(* 's' doesn't escape and will never be mutated again *)
Bytes.unsafe_to_string s
#endif
let starts_with str p =
if length str < length p then
false
else
let rec loop str p i =
if i = length p then true else
if unsafe_get str i <> unsafe_get p i then false
else loop str p (i+1)
in
loop str p 0
let ends_with s e =
if length s < length e then
false
else
let rec loop s e i =
if i = length e then true else
if unsafe_get s (length s - length e + i) <> unsafe_get e i then false
else loop s e (i+1)
in
loop s e 0
let find_from str pos sub =
let sublen = length sub in
if sublen = 0 then
0
else
let found = ref 0 in
let len = length str in
try
for i = pos to len - sublen do
let j = ref 0 in
while unsafe_get str (i + !j) = unsafe_get sub !j do
incr j;
if !j = sublen then begin found := i; raise Exit; end;
done;
done;
raise Invalid_string
with
Exit -> !found
let find str sub = find_from str 0 sub
let exists str sub =
try
ignore(find str sub);
true
with
Invalid_string -> false
let strip ?(chars=" \t\r\n") s =
let p = ref 0 in
let l = length s in
while !p < l && contains chars (unsafe_get s !p) do
incr p;
done;
let p = !p in
let l = ref (l - 1) in
while !l >= p && contains chars (unsafe_get s !l) do
decr l;
done;
sub s p (!l - p + 1)
#if OCAML < 400
let trim s = strip ~chars:" \t\r\n\012" s
#endif
let split str sep =
let p = find str sep in
let len = length sep in
let slen = length str in
sub str 0 p, sub str (p + len) (slen - p - len)
let nsplit str sep =
if str = "" then []
else if sep = "" then raise Invalid_string
else
let rec loop acc pos =
if pos > String.length str then
List.rev acc
else
let i = try find_from str pos sep with Invalid_string -> String.length str in
loop (String.sub str pos (i - pos) :: acc) (i + String.length sep)
in
loop [] 0
let join = concat
let slice =
let clip max x = if x > max then max else if x < 0 then 0 else x in
fun ?(first=0) ?(last=Sys.max_string_length) s ->
let len = String.length s in
let i = if first = 0 then 0 else clip len (if first < 0 then len + first else first) in
let j = if last = Sys.max_string_length then len else clip len (if last < 0 then len + last else last) in
if i>=j || i=len then
make 0 ' '
else
sub s i (j-i)
let lchop s =
if s = "" then "" else sub s 1 (length s - 1)
let rchop s =
if s = "" then "" else sub s 0 (length s - 1)
let of_int = string_of_int
let of_float = string_of_float
let of_char = make 1
let to_int s =
try
int_of_string s
with
_ -> raise Invalid_string
let to_float s =
try
float_of_string s
with
_ -> raise Invalid_string
let enum s =
let l = length s in
let rec make i =
Enum.make
~next:(fun () ->
if !i = l then
raise Enum.No_more_elements
else
let p = !i in
incr i;
unsafe_get s p
)
~count:(fun () -> l - !i)
~clone:(fun () -> make (ref !i))
in
make (ref 0)
let of_enum e =
let l = Enum.count e in
let s = Bytes.create l in
let i = ref 0 in
Enum.iter (fun c -> Bytes.unsafe_set s !i c; incr i) e;
(* 's' doesn't escape and will never be mutated again *)
Bytes.unsafe_to_string s
#if OCAML < 400
let map f s =
let len = length s in
let sc = Bytes.create len in
for i = 0 to len - 1 do
Bytes.unsafe_set sc i (f (unsafe_get s i))
done;
(* 'sc' doesn't escape and will never be mutated again *)
Bytes.unsafe_to_string sc
#endif
#if OCAML < 402
let mapi f s =
let len = length s in
let sc = Bytes.create len in
for i = 0 to len - 1 do
Bytes.unsafe_set sc i (f i (unsafe_get s i))
done;
(* 'sc' doesn't escape and will never be mutated again *)
Bytes.unsafe_to_string sc
#endif
#if OCAML < 400
let iteri f s =
for i = 0 to length s - 1 do
let () = f i (unsafe_get s i) in ()
done
#endif
(* fold_left and fold_right by Eric C. Cooper *)
let fold_left f init str =
let n = String.length str in
let rec loop i result =
if i = n then result
else loop (i + 1) (f result str.[i])
in
loop 0 init
let fold_right f str init =
let n = String.length str in
let rec loop i result =
if i = 0 then result
else
let i' = i - 1 in
loop i' (f str.[i'] result)
in
loop n init
(* explode and implode from the OCaml Expert FAQ. *)
let explode s =
let rec exp i l =
if i < 0 then l else exp (i - 1) (s.[i] :: l) in
exp (String.length s - 1) []
let implode l =
let res = Bytes.create (List.length l) in
let rec imp i = function
| [] -> res
| c :: l -> Bytes.set res i c; imp (i + 1) l in
let s = imp 0 l in
(* 's' doesn't escape and will never be mutated again *)
Bytes.unsafe_to_string s
let replace_chars f s =
let len = String.length s in
let tlen = ref 0 in
let rec loop i acc =
if i = len then
acc
else
let s = f (unsafe_get s i) in
tlen := !tlen + length s;
loop (i+1) (s :: acc)
in
let strs = loop 0 [] in
let sbuf = Bytes.create !tlen in
let pos = ref !tlen in
let rec loop2 = function
| [] -> ()
| s :: acc ->
let len = length s in
pos := !pos - len;
blit s 0 sbuf !pos len;
loop2 acc
in
loop2 strs;
(* 'sbuf' doesn't escape and will never be mutated again *)
Bytes.unsafe_to_string sbuf
let replace ~str ~sub ~by =
try
let i = find str sub in
(true, (slice ~last:i str) ^ by ^
(slice ~first:(i+(String.length sub)) str))
with
Invalid_string -> (false, String.sub str 0 (String.length str))
#if OCAML < 403
let uppercase_ascii = uppercase
let lowercase_ascii = lowercase
let capitalize_ascii = capitalize
let uncapitalize_ascii = uncapitalize
let equal = (=)
#endif
#if OCAML < 404
let split_on_char sep s =
let r = ref [] in
let j = ref (length s) in
for i = length s - 1 downto 0 do
if unsafe_get s i = sep then begin
r := sub s (i + 1) (!j - i - 1) :: !r;
j := i
end
done;
sub s 0 !j :: !r
#endif
#if OCAML < 405
let rec index_rec_opt s lim i c =
if i >= lim then None else
if unsafe_get s i = c then Some i else index_rec_opt s lim (i + 1) c
let index_opt s c = index_rec_opt s (length s) 0 c
let index_from_opt s i c =
let l = length s in
if i < 0 || i > l then invalid_arg "ExtString.index_from_opt" else
index_rec_opt s l i c
let rec rindex_rec_opt s i c =
if i < 0 then None else
if unsafe_get s i = c then Some i else rindex_rec_opt s (i - 1) c
let rindex_opt s c = rindex_rec_opt s (length s - 1) c
let rindex_from_opt s i c =
if i < -1 || i >= length s then
invalid_arg "ExtString.rindex_from_opt"
else
rindex_rec_opt s i c
#endif
end