  Copyright ( C ) 2015 - 2016 Bloomberg Finance L.P. 
  * 
  * This program 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 3 of the License , or 
  * ( at your option ) any later version . 
  * 
  * In addition to the permissions granted to you by the LGPL , you may combine 
  * or link a " work that uses the Library " with a publicly distributed version 
  * of this file to produce a combined library or application , then distribute 
  * that combined work under the terms of your choosing , with no requirement 
  * to comply with the obligations normally placed on you by section 4 of the 
  * LGPL version 3 ( or the corresponding section of a later version of the LGPL 
  * should you choose to use a later version ) . 
  * 
  * This program 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 program ; if not , write to the Free Software 
  * Foundation , Inc. , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
 *
 * This program 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 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program 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 program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)







(*
   {[ split " test_unsafe_obj_ffi_ppx.cmi" ~keep_empty:false ' ']}
*)
let split_by ?(keep_empty=false) is_delim str =
  let len = String.length str in
  let rec loop acc last_pos pos =
    if pos = -1 then
      if last_pos = 0 && not keep_empty then

        acc
      else
        String.sub str 0 last_pos :: acc
    else
    if is_delim str.[pos] then
      let new_len = (last_pos - pos - 1) in
      if new_len <> 0 || keep_empty then
        let v = String.sub str (pos + 1) new_len in
        loop ( v :: acc)
          pos (pos - 1)
      else loop acc pos (pos - 1)
    else loop acc last_pos (pos - 1)
  in
  loop [] len (len - 1)

let trim s =
  let i = ref 0  in
  let j = String.length s in
  while !i < j &&
        let u = String.unsafe_get s !i in
        u = '\t' || u = '\n' || u = ' '
  do
    incr i;
  done;
  let k = ref (j - 1)  in
  while !k >= !i &&
        let u = String.unsafe_get s !k in
        u = '\t' || u = '\n' || u = ' ' do
    decr k ;
  done;
  String.sub s !i (!k - !i + 1)

let split ?keep_empty  str on =
  if str = "" then [] else
    split_by ?keep_empty (fun x -> (x : char) = on) str  ;;

let quick_split_by_ws str : string list =
  split_by ~keep_empty:false (fun x -> x = '\t' || x = '\n' || x = ' ') str

let starts_with s beg =
  let beg_len = String.length beg in
  let s_len = String.length s in
  beg_len <=  s_len &&
  (let i = ref 0 in
   while !i <  beg_len
         && String.unsafe_get s !i =
            String.unsafe_get beg !i do
     incr i
   done;
   !i = beg_len
  )

let rec ends_aux s end_ j k =
  if k < 0 then (j + 1)
  else if String.unsafe_get s j = String.unsafe_get end_ k then
    ends_aux s end_ (j - 1) (k - 1)
  else  -1

(** return an index which is minus when [s] does not
    end with [beg]
*)
let ends_with_index s end_ : int =
  let s_finish = String.length s - 1 in
  let s_beg = String.length end_ - 1 in
  if s_beg > s_finish then -1
  else
    ends_aux s end_ s_finish s_beg

let ends_with s end_ = ends_with_index s end_ >= 0

let ends_with_then_chop s beg =
  let i =  ends_with_index s beg in
  if i >= 0 then Some (String.sub s 0 i)
  else None

let ends_with_char s c =
  let len = String.length s in
  len > 0 && String.unsafe_get s (len - 1) = c

(* let check_suffix_case = ends_with  *)
  let check_suffix_case_then_chop = ends_with_then_chop

  let check_any_suffix_case s suffixes = 
   Ext_list.exists suffixes ( fun x - > check_suffix_case s x )  
  Ext_list.exists suffixes (fun x -> check_suffix_case s x)  *)

  let  s suffixes = 
   let rec aux suffixes = 
     match suffixes with 
     | [ ] - > None 
     | x::xs - > 
       let i d = ends_with_index s x in 
       if i d > = 0 then Some ( String.sub s 0 i d ) 
       else aux xs in 
   aux suffixes     
  let rec aux suffixes =
    match suffixes with
    | [] -> None
    | x::xs ->
      let id = ends_with_index s x in
      if id >= 0 then Some (String.sub s 0 id)
      else aux xs in
  aux suffixes     *)




(* it is unsafe to expose such API as unsafe since
   user can provide bad input range

*)
let rec unsafe_for_all_range s ~start ~finish p =
  start > finish ||
  p (String.unsafe_get s start) &&
  unsafe_for_all_range s ~start:(start + 1) ~finish p

let for_all_from s start  p =
  let len = String.length s in
  if start < 0  then invalid_arg "Ext_string.for_all_from"
  else unsafe_for_all_range s ~start ~finish:(len - 1) p


let for_all s (p : char -> bool)  =
  unsafe_for_all_range s ~start:0  ~finish:(String.length s - 1) p

let is_empty s = String.length s = 0


let repeat n s  =
  let len = String.length s in
  let res = Bytes.create(n * len) in
  for i = 0 to pred n do
    String.blit s 0 res (i * len) len
  done;
  Bytes.to_string res




let unsafe_is_sub ~sub i s j ~len =
  let rec check k =
    if k = len
    then true
    else
      String.unsafe_get sub (i+k) =
      String.unsafe_get s (j+k) && check (k+1)
  in
  j+len <= String.length s && check 0



let find ?(start=0) ~sub s =
  let exception Local_exit in
  let n = String.length sub in
  let s_len = String.length s in
  let i = ref start in
  try
    while !i + n <= s_len do
      if unsafe_is_sub ~sub 0 s !i ~len:n then
        raise_notrace Local_exit;
      incr i
    done;
    -1
  with Local_exit ->
    !i

let contain_substring s sub =
  find s ~sub >= 0

(** TODO: optimize
    avoid nonterminating when string is empty
*)
let non_overlap_count ~sub s =
  let sub_len = String.length sub in
  let rec aux  acc off =
    let i = find ~start:off ~sub s  in
    if i < 0 then acc
    else aux (acc + 1) (i + sub_len) in
  if String.length sub = 0 then invalid_arg "Ext_string.non_overlap_count"
  else aux 0 0


let rfind ~sub s =
  let exception Local_exit in
  let n = String.length sub in
  let i = ref (String.length s - n) in
  try
    while !i >= 0 do
      if unsafe_is_sub ~sub 0 s !i ~len:n then
        raise_notrace Local_exit;
      decr i
    done;
    -1
  with Local_exit ->
    !i

let tail_from s x =
  let len = String.length s  in
  if  x > len then invalid_arg ("Ext_string.tail_from " ^s ^ " : "^ string_of_int x )
  else String.sub s x (len - x)

let equal (x : string) y  = x = y

  let rec index_rec s lim i c = 
   if i > = lim then -1 else 
   if String.unsafe_get s i = c then i 
   else index_rec s lim ( i + 1 ) c
  if i >= lim then -1 else
  if String.unsafe_get s i = c then i
  else index_rec s lim (i + 1) c *)



let rec index_rec_count s lim i c count =
  if i >= lim then -1 else
  if String.unsafe_get s i = c then
    if count = 1 then i
    else index_rec_count s lim (i + 1) c (count - 1)
  else index_rec_count s lim (i + 1) c count

let index_count s i c count =
  let lim = String.length s in
  if i < 0 || i >= lim || count < 1 then
    invalid_arg ("index_count: ( " ^string_of_int i ^ "," ^string_of_int count ^ ")" );
  index_rec_count s lim i c count

  let index_next s i c = 
   index_count s i c 1  
  index_count s i c 1  *)

  let extract_until s cursor c = 
   let len = String.length s in 
   let start = ! cursor in 
   if start < 0 || start > = len then ( 
     cursor : = -1 ; 
     " " 
     ) 
   else 
     let i =       
     let finish = 
       if i < 0 then ( 
         cursor : = -1 ; 
         len 
       ) 
       else ( 
         cursor : = i + 1 ; 
         i 
       ) in 
     String.sub s start ( finish - start )
  let len = String.length s in
  let start = !cursor in
  if start < 0 || start >= len then (
    cursor := -1;
    ""
    )
  else
    let i = index_rec s len start c in
    let finish =
      if i < 0 then (
        cursor := -1 ;
        len
      )
      else (
        cursor := i + 1;
        i
      ) in
    String.sub s start (finish - start) *)

let rec rindex_rec s i c =
  if i < 0 then i else
  if String.unsafe_get s i = c then i else rindex_rec s (i - 1) c;;

let rec rindex_rec_opt s i c =
  if i < 0 then None else
  if String.unsafe_get s i = c then Some i else rindex_rec_opt s (i - 1) c;;

let rindex_neg s c =
  rindex_rec s (String.length s - 1) c;;

let rindex_opt s c =
  rindex_rec_opt s (String.length s - 1) c;;


(** TODO: can be improved to return a positive integer instead *)
let rec unsafe_no_char x ch i  last_idx =
  i > last_idx  ||
  (String.unsafe_get x i <> ch && unsafe_no_char x ch (i + 1)  last_idx)

let rec unsafe_no_char_idx x ch i last_idx =
  if i > last_idx  then -1
  else
  if String.unsafe_get x i <> ch then
    unsafe_no_char_idx x ch (i + 1)  last_idx
  else i

let no_char x ch i len  : bool =
  let str_len = String.length x in
  if i < 0 || i >= str_len || len >= str_len then invalid_arg "Ext_string.no_char"
  else unsafe_no_char x ch i len


let no_slash x =
  unsafe_no_char x '/' 0 (String.length x - 1)

let no_slash_idx x =
  unsafe_no_char_idx x '/' 0 (String.length x - 1)

let no_slash_idx_from x from =
  let last_idx = String.length x - 1  in
  assert (from >= 0);
  unsafe_no_char_idx x '/' from last_idx

let replace_slash_backward (x : string ) =
  let len = String.length x in
  if unsafe_no_char x '/' 0  (len - 1) then x
  else
    String.map (function
        | '/' -> '\\'
        | x -> x ) x

let replace_backward_slash (x : string)=
  let len = String.length x in
  if unsafe_no_char x '\\' 0  (len -1) then x
  else
    String.map (function
        |'\\'-> '/'
        | x -> x) x

let empty = ""

#if defined BS_BROWSER
let compare = Bs_hash_stubs.string_length_based_compare
#else
external compare : string -> string -> int = "caml_string_length_based_compare" [@@noalloc];;
#endif
let single_space = " "
let single_colon = ":"

let concat_array sep (s : string array) =
  let s_len = Array.length s in
  match s_len with
  | 0 -> empty
  | 1 -> Array.unsafe_get s 0
  | _ ->
    let sep_len = String.length sep in
    let len = ref 0 in
    for i = 0 to  s_len - 1 do
      len := !len + String.length (Array.unsafe_get s i)
    done;
    let target =
      Bytes.create
        (!len + (s_len - 1) * sep_len ) in
    let hd = (Array.unsafe_get s 0) in
    let hd_len = String.length hd in
    String.unsafe_blit hd  0  target 0 hd_len;
    let current_offset = ref hd_len in
    for i = 1 to s_len - 1 do
      String.unsafe_blit sep 0 target  !current_offset sep_len;
      let cur = Array.unsafe_get s i in
      let cur_len = String.length cur in
      let new_off_set = (!current_offset + sep_len ) in
      String.unsafe_blit cur 0 target new_off_set cur_len;
      current_offset :=
        new_off_set + cur_len ;
    done;
    Bytes.unsafe_to_string target

let concat3 a b c =
  let a_len = String.length a in
  let b_len = String.length b in
  let c_len = String.length c in
  let len = a_len + b_len + c_len in
  let target = Bytes.create len in
  String.unsafe_blit a 0 target 0 a_len ;
  String.unsafe_blit b 0 target a_len b_len;
  String.unsafe_blit c 0 target (a_len + b_len) c_len;
  Bytes.unsafe_to_string target

let concat4 a b c d =
  let a_len = String.length a in
  let b_len = String.length b in
  let c_len = String.length c in
  let d_len = String.length d in
  let len = a_len + b_len + c_len + d_len in

  let target = Bytes.create len in
  String.unsafe_blit a 0 target 0 a_len ;
  String.unsafe_blit b 0 target a_len b_len;
  String.unsafe_blit c 0 target (a_len + b_len) c_len;
  String.unsafe_blit d 0 target (a_len + b_len + c_len) d_len;
  Bytes.unsafe_to_string target


let concat5 a b c d e =
  let a_len = String.length a in
  let b_len = String.length b in
  let c_len = String.length c in
  let d_len = String.length d in
  let e_len = String.length e in
  let len = a_len + b_len + c_len + d_len + e_len in

  let target = Bytes.create len in
  String.unsafe_blit a 0 target 0 a_len ;
  String.unsafe_blit b 0 target a_len b_len;
  String.unsafe_blit c 0 target (a_len + b_len) c_len;
  String.unsafe_blit d 0 target (a_len + b_len + c_len) d_len;
  String.unsafe_blit e 0 target (a_len + b_len + c_len + d_len) e_len;
  Bytes.unsafe_to_string target



let inter2 a b =
  concat3 a single_space b


let inter3 a b c =
  concat5 a  single_space  b  single_space  c





let inter4 a b c d =
  concat_array single_space [| a; b ; c; d|]


let parent_dir_lit = ".."
let current_dir_lit = "."


  reference { ! Bytes.unppercase }
let capitalize_ascii (s : string) : string =
  if String.length s = 0 then s
  else
    begin
      let c = String.unsafe_get s 0 in
      if (c >= 'a' && c <= 'z')
      || (c >= '\224' && c <= '\246')
      || (c >= '\248' && c <= '\254') then
        let uc = Char.unsafe_chr (Char.code c - 32) in
        let bytes = Bytes.of_string s in
        Bytes.unsafe_set bytes 0 uc;
        Bytes.unsafe_to_string bytes
      else s
    end

let capitalize_sub (s : string) len : string =
  let slen = String.length s in
  if  len < 0 || len > slen then invalid_arg "Ext_string.capitalize_sub"
  else
  if len = 0 then ""
  else
    let bytes = Bytes.create len in
    let uc =
      let c = String.unsafe_get s 0 in
      if (c >= 'a' && c <= 'z')
      || (c >= '\224' && c <= '\246')
      || (c >= '\248' && c <= '\254') then
        Char.unsafe_chr (Char.code c - 32) else c in
    Bytes.unsafe_set bytes 0 uc;
    for i = 1 to len - 1 do
      Bytes.unsafe_set bytes i (String.unsafe_get s i)
    done ;
    Bytes.unsafe_to_string bytes



let uncapitalize_ascii =
    String.uncapitalize_ascii

let lowercase_ascii = String.lowercase_ascii


external (.![]) : string -> int -> int = "%string_unsafe_get"

let get_int_1 (x : string) off : int =
  Char.code x.[off]

let get_int_2 (x : string) off : int =
  Char.code x.[off] lor
  Char.code x.[off+1] lsl 8

let get_int_3 (x : string) off : int =
  Char.code x.[off] lor
  Char.code x.[off+1] lsl 8  lor
  Char.code x.[off+2] lsl 16

let get_int_4 (x : string) off : int =
  Char.code x.[off] lor
  Char.code x.[off+1] lsl 8  lor
  Char.code x.[off+2] lsl 16 lor
  Char.code x.[off+3] lsl 24

let get_1_2_3_4 (x : string) ~off len : int =
  if len = 1 then get_int_1 x off
  else if len = 2 then get_int_2 x off
  else if len = 3 then get_int_3 x off
  else if len = 4 then get_int_4 x off
  else assert false

let unsafe_sub  x offs len =
  let b = Bytes.create len in
  Bytes.unsafe_blit_string x offs b 0 len;
  (Bytes.unsafe_to_string b)

let is_valid_hash_number (x:string) =
  let len = String.length x in
  len > 0 && (
    let a = x.![0] in
    a <= 57 &&
    (if len > 1 then
       a > 48 &&
       for_all_from x 1 (function '0' .. '9' -> true | _ -> false)
     else
       a >= 48 )
  )


let hash_number_as_i32_exn
    ( x : string) : int32 =
  Int32.of_string x


let first_marshal_char (x : string) =
    x <> ""   &&
    ( String.unsafe_get x  0 = '\132')

let fold_left f initial x =
  let acc = ref initial in
  String.iter (fun c -> acc := f !acc c) x;
  !acc


   {[ split " test_unsafe_obj_ffi_ppx.cmi" ~keep_empty:false ' ']}

* return an index which is minus when [s] does not
    end with [beg]

 let check_suffix_case = ends_with  
 it is unsafe to expose such API as unsafe since
   user can provide bad input range


* TODO: optimize
    avoid nonterminating when string is empty

* TODO: can be improved to return a positive integer instead 
  Copyright ( C ) 2015 - 2016 Bloomberg Finance L.P. 
  * 
  * This program 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 3 of the License , or 
  * ( at your option ) any later version . 
  * 
  * In addition to the permissions granted to you by the LGPL , you may combine 
  * or link a " work that uses the Library " with a publicly distributed version 
  * of this file to produce a combined library or application , then distribute 
  * that combined work under the terms of your choosing , with no requirement 
  * to comply with the obligations normally placed on you by section 4 of the 
  * LGPL version 3 ( or the corresponding section of a later version of the LGPL 
  * should you choose to use a later version ) . 
  * 
  * This program 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 program ; if not , write to the Free Software 
  * Foundation , Inc. , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
 *
 * This program 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 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program 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 program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)







let split_by ?(keep_empty=false) is_delim str =
  let len = String.length str in
  let rec loop acc last_pos pos =
    if pos = -1 then
      if last_pos = 0 && not keep_empty then

        acc
      else
        String.sub str 0 last_pos :: acc
    else
    if is_delim str.[pos] then
      let new_len = (last_pos - pos - 1) in
      if new_len <> 0 || keep_empty then
        let v = String.sub str (pos + 1) new_len in
        loop ( v :: acc)
          pos (pos - 1)
      else loop acc pos (pos - 1)
    else loop acc last_pos (pos - 1)
  in
  loop [] len (len - 1)

let trim s =
  let i = ref 0  in
  let j = String.length s in
  while !i < j &&
        let u = String.unsafe_get s !i in
        u = '\t' || u = '\n' || u = ' '
  do
    incr i;
  done;
  let k = ref (j - 1)  in
  while !k >= !i &&
        let u = String.unsafe_get s !k in
        u = '\t' || u = '\n' || u = ' ' do
    decr k ;
  done;
  String.sub s !i (!k - !i + 1)

let split ?keep_empty  str on =
  if str = "" then [] else
    split_by ?keep_empty (fun x -> (x : char) = on) str  ;;

let quick_split_by_ws str : string list =
  split_by ~keep_empty:false (fun x -> x = '\t' || x = '\n' || x = ' ') str

let starts_with s beg =
  let beg_len = String.length beg in
  let s_len = String.length s in
  beg_len <=  s_len &&
  (let i = ref 0 in
   while !i <  beg_len
         && String.unsafe_get s !i =
            String.unsafe_get beg !i do
     incr i
   done;
   !i = beg_len
  )

let rec ends_aux s end_ j k =
  if k < 0 then (j + 1)
  else if String.unsafe_get s j = String.unsafe_get end_ k then
    ends_aux s end_ (j - 1) (k - 1)
  else  -1

let ends_with_index s end_ : int =
  let s_finish = String.length s - 1 in
  let s_beg = String.length end_ - 1 in
  if s_beg > s_finish then -1
  else
    ends_aux s end_ s_finish s_beg

let ends_with s end_ = ends_with_index s end_ >= 0

let ends_with_then_chop s beg =
  let i =  ends_with_index s beg in
  if i >= 0 then Some (String.sub s 0 i)
  else None

let ends_with_char s c =
  let len = String.length s in
  len > 0 && String.unsafe_get s (len - 1) = c

  let check_suffix_case_then_chop = ends_with_then_chop

  let check_any_suffix_case s suffixes = 
   Ext_list.exists suffixes ( fun x - > check_suffix_case s x )  
  Ext_list.exists suffixes (fun x -> check_suffix_case s x)  *)

  let  s suffixes = 
   let rec aux suffixes = 
     match suffixes with 
     | [ ] - > None 
     | x::xs - > 
       let i d = ends_with_index s x in 
       if i d > = 0 then Some ( String.sub s 0 i d ) 
       else aux xs in 
   aux suffixes     
  let rec aux suffixes =
    match suffixes with
    | [] -> None
    | x::xs ->
      let id = ends_with_index s x in
      if id >= 0 then Some (String.sub s 0 id)
      else aux xs in
  aux suffixes     *)




let rec unsafe_for_all_range s ~start ~finish p =
  start > finish ||
  p (String.unsafe_get s start) &&
  unsafe_for_all_range s ~start:(start + 1) ~finish p

let for_all_from s start  p =
  let len = String.length s in
  if start < 0  then invalid_arg "Ext_string.for_all_from"
  else unsafe_for_all_range s ~start ~finish:(len - 1) p


let for_all s (p : char -> bool)  =
  unsafe_for_all_range s ~start:0  ~finish:(String.length s - 1) p

let is_empty s = String.length s = 0


let repeat n s  =
  let len = String.length s in
  let res = Bytes.create(n * len) in
  for i = 0 to pred n do
    String.blit s 0 res (i * len) len
  done;
  Bytes.to_string res




let unsafe_is_sub ~sub i s j ~len =
  let rec check k =
    if k = len
    then true
    else
      String.unsafe_get sub (i+k) =
      String.unsafe_get s (j+k) && check (k+1)
  in
  j+len <= String.length s && check 0



let find ?(start=0) ~sub s =
  let exception Local_exit in
  let n = String.length sub in
  let s_len = String.length s in
  let i = ref start in
  try
    while !i + n <= s_len do
      if unsafe_is_sub ~sub 0 s !i ~len:n then
        raise_notrace Local_exit;
      incr i
    done;
    -1
  with Local_exit ->
    !i

let contain_substring s sub =
  find s ~sub >= 0

let non_overlap_count ~sub s =
  let sub_len = String.length sub in
  let rec aux  acc off =
    let i = find ~start:off ~sub s  in
    if i < 0 then acc
    else aux (acc + 1) (i + sub_len) in
  if String.length sub = 0 then invalid_arg "Ext_string.non_overlap_count"
  else aux 0 0


let rfind ~sub s =
  let exception Local_exit in
  let n = String.length sub in
  let i = ref (String.length s - n) in
  try
    while !i >= 0 do
      if unsafe_is_sub ~sub 0 s !i ~len:n then
        raise_notrace Local_exit;
      decr i
    done;
    -1
  with Local_exit ->
    !i

let tail_from s x =
  let len = String.length s  in
  if  x > len then invalid_arg ("Ext_string.tail_from " ^s ^ " : "^ string_of_int x )
  else String.sub s x (len - x)

let equal (x : string) y  = x = y

  let rec index_rec s lim i c = 
   if i > = lim then -1 else 
   if String.unsafe_get s i = c then i 
   else index_rec s lim ( i + 1 ) c
  if i >= lim then -1 else
  if String.unsafe_get s i = c then i
  else index_rec s lim (i + 1) c *)



let rec index_rec_count s lim i c count =
  if i >= lim then -1 else
  if String.unsafe_get s i = c then
    if count = 1 then i
    else index_rec_count s lim (i + 1) c (count - 1)
  else index_rec_count s lim (i + 1) c count

let index_count s i c count =
  let lim = String.length s in
  if i < 0 || i >= lim || count < 1 then
    invalid_arg ("index_count: ( " ^string_of_int i ^ "," ^string_of_int count ^ ")" );
  index_rec_count s lim i c count

  let index_next s i c = 
   index_count s i c 1  
  index_count s i c 1  *)

  let extract_until s cursor c = 
   let len = String.length s in 
   let start = ! cursor in 
   if start < 0 || start > = len then ( 
     cursor : = -1 ; 
     " " 
     ) 
   else 
     let i =       
     let finish = 
       if i < 0 then ( 
         cursor : = -1 ; 
         len 
       ) 
       else ( 
         cursor : = i + 1 ; 
         i 
       ) in 
     String.sub s start ( finish - start )
  let len = String.length s in
  let start = !cursor in
  if start < 0 || start >= len then (
    cursor := -1;
    ""
    )
  else
    let i = index_rec s len start c in
    let finish =
      if i < 0 then (
        cursor := -1 ;
        len
      )
      else (
        cursor := i + 1;
        i
      ) in
    String.sub s start (finish - start) *)

let rec rindex_rec s i c =
  if i < 0 then i else
  if String.unsafe_get s i = c then i else rindex_rec s (i - 1) c;;

let rec rindex_rec_opt s i c =
  if i < 0 then None else
  if String.unsafe_get s i = c then Some i else rindex_rec_opt s (i - 1) c;;

let rindex_neg s c =
  rindex_rec s (String.length s - 1) c;;

let rindex_opt s c =
  rindex_rec_opt s (String.length s - 1) c;;


let rec unsafe_no_char x ch i  last_idx =
  i > last_idx  ||
  (String.unsafe_get x i <> ch && unsafe_no_char x ch (i + 1)  last_idx)

let rec unsafe_no_char_idx x ch i last_idx =
  if i > last_idx  then -1
  else
  if String.unsafe_get x i <> ch then
    unsafe_no_char_idx x ch (i + 1)  last_idx
  else i

let no_char x ch i len  : bool =
  let str_len = String.length x in
  if i < 0 || i >= str_len || len >= str_len then invalid_arg "Ext_string.no_char"
  else unsafe_no_char x ch i len


let no_slash x =
  unsafe_no_char x '/' 0 (String.length x - 1)

let no_slash_idx x =
  unsafe_no_char_idx x '/' 0 (String.length x - 1)

let no_slash_idx_from x from =
  let last_idx = String.length x - 1  in
  assert (from >= 0);
  unsafe_no_char_idx x '/' from last_idx

let replace_slash_backward (x : string ) =
  let len = String.length x in
  if unsafe_no_char x '/' 0  (len - 1) then x
  else
    String.map (function
        | '/' -> '\\'
        | x -> x ) x

let replace_backward_slash (x : string)=
  let len = String.length x in
  if unsafe_no_char x '\\' 0  (len -1) then x
  else
    String.map (function
        |'\\'-> '/'
        | x -> x) x

let empty = ""

#if defined BS_BROWSER
let compare = Bs_hash_stubs.string_length_based_compare
#else
external compare : string -> string -> int = "caml_string_length_based_compare" [@@noalloc];;
#endif
let single_space = " "
let single_colon = ":"

let concat_array sep (s : string array) =
  let s_len = Array.length s in
  match s_len with
  | 0 -> empty
  | 1 -> Array.unsafe_get s 0
  | _ ->
    let sep_len = String.length sep in
    let len = ref 0 in
    for i = 0 to  s_len - 1 do
      len := !len + String.length (Array.unsafe_get s i)
    done;
    let target =
      Bytes.create
        (!len + (s_len - 1) * sep_len ) in
    let hd = (Array.unsafe_get s 0) in
    let hd_len = String.length hd in
    String.unsafe_blit hd  0  target 0 hd_len;
    let current_offset = ref hd_len in
    for i = 1 to s_len - 1 do
      String.unsafe_blit sep 0 target  !current_offset sep_len;
      let cur = Array.unsafe_get s i in
      let cur_len = String.length cur in
      let new_off_set = (!current_offset + sep_len ) in
      String.unsafe_blit cur 0 target new_off_set cur_len;
      current_offset :=
        new_off_set + cur_len ;
    done;
    Bytes.unsafe_to_string target

let concat3 a b c =
  let a_len = String.length a in
  let b_len = String.length b in
  let c_len = String.length c in
  let len = a_len + b_len + c_len in
  let target = Bytes.create len in
  String.unsafe_blit a 0 target 0 a_len ;
  String.unsafe_blit b 0 target a_len b_len;
  String.unsafe_blit c 0 target (a_len + b_len) c_len;
  Bytes.unsafe_to_string target

let concat4 a b c d =
  let a_len = String.length a in
  let b_len = String.length b in
  let c_len = String.length c in
  let d_len = String.length d in
  let len = a_len + b_len + c_len + d_len in

  let target = Bytes.create len in
  String.unsafe_blit a 0 target 0 a_len ;
  String.unsafe_blit b 0 target a_len b_len;
  String.unsafe_blit c 0 target (a_len + b_len) c_len;
  String.unsafe_blit d 0 target (a_len + b_len + c_len) d_len;
  Bytes.unsafe_to_string target


let concat5 a b c d e =
  let a_len = String.length a in
  let b_len = String.length b in
  let c_len = String.length c in
  let d_len = String.length d in
  let e_len = String.length e in
  let len = a_len + b_len + c_len + d_len + e_len in

  let target = Bytes.create len in
  String.unsafe_blit a 0 target 0 a_len ;
  String.unsafe_blit b 0 target a_len b_len;
  String.unsafe_blit c 0 target (a_len + b_len) c_len;
  String.unsafe_blit d 0 target (a_len + b_len + c_len) d_len;
  String.unsafe_blit e 0 target (a_len + b_len + c_len + d_len) e_len;
  Bytes.unsafe_to_string target



let inter2 a b =
  concat3 a single_space b


let inter3 a b c =
  concat5 a  single_space  b  single_space  c





let inter4 a b c d =
  concat_array single_space [| a; b ; c; d|]


let parent_dir_lit = ".."
let current_dir_lit = "."


  reference { ! Bytes.unppercase }
let capitalize_ascii (s : string) : string =
  if String.length s = 0 then s
  else
    begin
      let c = String.unsafe_get s 0 in
      if (c >= 'a' && c <= 'z')
      || (c >= '\224' && c <= '\246')
      || (c >= '\248' && c <= '\254') then
        let uc = Char.unsafe_chr (Char.code c - 32) in
        let bytes = Bytes.of_string s in
        Bytes.unsafe_set bytes 0 uc;
        Bytes.unsafe_to_string bytes
      else s
    end

let capitalize_sub (s : string) len : string =
  let slen = String.length s in
  if  len < 0 || len > slen then invalid_arg "Ext_string.capitalize_sub"
  else
  if len = 0 then ""
  else
    let bytes = Bytes.create len in
    let uc =
      let c = String.unsafe_get s 0 in
      if (c >= 'a' && c <= 'z')
      || (c >= '\224' && c <= '\246')
      || (c >= '\248' && c <= '\254') then
        Char.unsafe_chr (Char.code c - 32) else c in
    Bytes.unsafe_set bytes 0 uc;
    for i = 1 to len - 1 do
      Bytes.unsafe_set bytes i (String.unsafe_get s i)
    done ;
    Bytes.unsafe_to_string bytes



let uncapitalize_ascii =
    String.uncapitalize_ascii

let lowercase_ascii = String.lowercase_ascii


external (.![]) : string -> int -> int = "%string_unsafe_get"

let get_int_1 (x : string) off : int =
  Char.code x.[off]

let get_int_2 (x : string) off : int =
  Char.code x.[off] lor
  Char.code x.[off+1] lsl 8

let get_int_3 (x : string) off : int =
  Char.code x.[off] lor
  Char.code x.[off+1] lsl 8  lor
  Char.code x.[off+2] lsl 16

let get_int_4 (x : string) off : int =
  Char.code x.[off] lor
  Char.code x.[off+1] lsl 8  lor
  Char.code x.[off+2] lsl 16 lor
  Char.code x.[off+3] lsl 24

let get_1_2_3_4 (x : string) ~off len : int =
  if len = 1 then get_int_1 x off
  else if len = 2 then get_int_2 x off
  else if len = 3 then get_int_3 x off
  else if len = 4 then get_int_4 x off
  else assert false

let unsafe_sub  x offs len =
  let b = Bytes.create len in
  Bytes.unsafe_blit_string x offs b 0 len;
  (Bytes.unsafe_to_string b)

let is_valid_hash_number (x:string) =
  let len = String.length x in
  len > 0 && (
    let a = x.![0] in
    a <= 57 &&
    (if len > 1 then
       a > 48 &&
       for_all_from x 1 (function '0' .. '9' -> true | _ -> false)
     else
       a >= 48 )
  )


let hash_number_as_i32_exn
    ( x : string) : int32 =
  Int32.of_string x


let first_marshal_char (x : string) =
    x <> ""   &&
    ( String.unsafe_get x  0 = '\132')

let fold_left f initial x =
  let acc = ref initial in
  String.iter (fun c -> acc := f !acc c) x;
  !acc
