Blame src/dynArray.ml

rpm-build 0f2925
(*
rpm-build 0f2925
 * DynArray - Resizeable Ocaml arrays
rpm-build 0f2925
 * Copyright (C) 2003 Brian Hurt
rpm-build 0f2925
 * Copyright (C) 2003 Nicolas Cannasse
rpm-build 0f2925
 *
rpm-build 0f2925
 * This library is free software; you can redistribute it and/or
rpm-build 0f2925
 * modify it under the terms of the GNU Lesser General Public
rpm-build 0f2925
 * License as published by the Free Software Foundation; either
rpm-build 0f2925
 * version 2.1 of the License, or (at your option) any later version,
rpm-build 0f2925
 * with the special exception on linking described in file LICENSE.
rpm-build 0f2925
 *
rpm-build 0f2925
 * This library is distributed in the hope that it will be useful,
rpm-build 0f2925
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
rpm-build 0f2925
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
rpm-build 0f2925
 * Lesser General Public License for more details.
rpm-build 0f2925
 *
rpm-build 0f2925
 * You should have received a copy of the GNU Lesser General Public
rpm-build 0f2925
 * License along with this library; if not, write to the Free Software
rpm-build 0f2925
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
rpm-build 0f2925
 *)
rpm-build 0f2925
rpm-build 0f2925
type resizer_t = currslots:int -> oldlength:int -> newlength:int -> int
rpm-build 0f2925
rpm-build 0f2925
type 'a intern
rpm-build 0f2925
rpm-build 0f2925
external ilen : 'a intern -> int = "%obj_size"
rpm-build 0f2925
let idup (x : 'a intern) = if ilen x = 0 then x else (Obj.magic (Obj.dup (Obj.repr x)) : 'a intern)
rpm-build 0f2925
let imake tag len = (Obj.magic (Obj.new_block tag len) : 'a intern)
rpm-build 0f2925
external iget : 'a intern -> int -> 'a = "%obj_field"
rpm-build 0f2925
external iset : 'a intern -> int -> 'a -> unit = "%obj_set_field"
rpm-build 0f2925
rpm-build 0f2925
type 'a t = {
rpm-build 0f2925
  mutable arr : 'a intern;
rpm-build 0f2925
  mutable len : int;
rpm-build 0f2925
  mutable resize: resizer_t;
rpm-build 0f2925
}
rpm-build 0f2925
rpm-build 0f2925
exception Invalid_arg of int * string * string
rpm-build 0f2925
rpm-build 0f2925
let invalid_arg n f p = raise (Invalid_arg (n,f,p))
rpm-build 0f2925
rpm-build 0f2925
let length d = d.len
rpm-build 0f2925
rpm-build 0f2925
let exponential_resizer ~currslots ~oldlength ~newlength =
rpm-build 0f2925
  let rec doubler x = if x >= newlength then x else doubler (x * 2) in
rpm-build 0f2925
  let rec halfer x = if x / 2 < newlength then x else halfer (x / 2) in
rpm-build 0f2925
  if newlength = 1 then
rpm-build 0f2925
    1
rpm-build 0f2925
  else if currslots = 0 then
rpm-build 0f2925
    doubler 1
rpm-build 0f2925
  else if currslots < newlength then
rpm-build 0f2925
    doubler currslots
rpm-build 0f2925
  else
rpm-build 0f2925
    halfer currslots
rpm-build 0f2925
rpm-build 0f2925
let step_resizer step =
rpm-build 0f2925
  if step <= 0 then invalid_arg step "step_resizer" "step";
rpm-build 0f2925
  (fun ~currslots ~oldlength ~newlength ->
rpm-build 0f2925
    if currslots < newlength || newlength < (currslots - step)
rpm-build 0f2925
    then
rpm-build 0f2925
       (newlength + step - (newlength mod step))
rpm-build 0f2925
    else
rpm-build 0f2925
      currslots)
rpm-build 0f2925
rpm-build 0f2925
let conservative_exponential_resizer ~currslots ~oldlength ~newlength =
rpm-build 0f2925
  let rec doubler x = if x >= newlength then x else doubler (x * 2) in
rpm-build 0f2925
  let rec halfer x = if x / 2 < newlength then x else halfer (x / 2) in
rpm-build 0f2925
  if currslots < newlength then begin
rpm-build 0f2925
    if newlength = 1 then
rpm-build 0f2925
      1
rpm-build 0f2925
    else if currslots = 0 then
rpm-build 0f2925
      doubler 1
rpm-build 0f2925
    else
rpm-build 0f2925
      doubler currslots
rpm-build 0f2925
  end else if oldlength < newlength then
rpm-build 0f2925
    halfer currslots
rpm-build 0f2925
  else
rpm-build 0f2925
    currslots
rpm-build 0f2925
rpm-build 0f2925
let default_resizer = conservative_exponential_resizer
rpm-build 0f2925
rpm-build 0f2925
let changelen (d : 'a t) newlen =
rpm-build 0f2925
  if newlen > Sys.max_array_length then invalid_arg newlen "changelen" "newlen";
rpm-build 0f2925
rpm-build 0f2925
  let oldsize = ilen d.arr in
rpm-build 0f2925
  let r = d.resize
rpm-build 0f2925
      ~currslots:oldsize
rpm-build 0f2925
      ~oldlength:d.len
rpm-build 0f2925
      ~newlength:newlen
rpm-build 0f2925
  in
rpm-build 0f2925
  (* We require the size to be at least large enough to hold the number
rpm-build 0f2925
   * of elements we know we need!
rpm-build 0f2925
   * Also be sure not to exceed max_array_length
rpm-build 0f2925
   *)
rpm-build 0f2925
  let newsize = if r < newlen then newlen else min Sys.max_array_length r in
rpm-build 0f2925
  if newsize <> oldsize then begin
rpm-build 0f2925
    let newarr = imake 0 newsize in
rpm-build 0f2925
    let cpylen = (if newlen < d.len then newlen else d.len) in
rpm-build 0f2925
    for i = 0 to cpylen - 1 do
rpm-build 0f2925
      iset newarr i (iget d.arr i);
rpm-build 0f2925
    done;
rpm-build 0f2925
    d.arr <- newarr;
rpm-build 0f2925
  end;
rpm-build 0f2925
  d.len <- newlen
rpm-build 0f2925
rpm-build 0f2925
let compact d =
rpm-build 0f2925
  if d.len <> ilen d.arr then begin
rpm-build 0f2925
    let newarr = imake 0 d.len in
rpm-build 0f2925
    for i = 0 to d.len - 1 do
rpm-build 0f2925
      iset newarr i (iget d.arr i)
rpm-build 0f2925
    done;
rpm-build 0f2925
    d.arr <- newarr;
rpm-build 0f2925
  end
rpm-build 0f2925
rpm-build 0f2925
let create() =
rpm-build 0f2925
  {
rpm-build 0f2925
    resize = default_resizer;
rpm-build 0f2925
    len = 0;
rpm-build 0f2925
    arr = imake 0 0;
rpm-build 0f2925
  }
rpm-build 0f2925
rpm-build 0f2925
let make initsize =
rpm-build 0f2925
  if initsize < 0 then invalid_arg initsize "make" "size";
rpm-build 0f2925
  {
rpm-build 0f2925
    resize = default_resizer;
rpm-build 0f2925
    len = 0;
rpm-build 0f2925
    arr = imake 0 initsize;
rpm-build 0f2925
  }
rpm-build 0f2925
rpm-build 0f2925
let init initlen f =
rpm-build 0f2925
  if initlen < 0 then invalid_arg initlen "init" "len";
rpm-build 0f2925
  let arr = imake 0 initlen in
rpm-build 0f2925
  for i = 0 to initlen-1 do
rpm-build 0f2925
    iset arr i (f i)
rpm-build 0f2925
  done;
rpm-build 0f2925
  {
rpm-build 0f2925
    resize = default_resizer;
rpm-build 0f2925
    len = initlen;
rpm-build 0f2925
    arr = arr;
rpm-build 0f2925
  }
rpm-build 0f2925
rpm-build 0f2925
let set_resizer d resizer =
rpm-build 0f2925
  d.resize <- resizer
rpm-build 0f2925
rpm-build 0f2925
let get_resizer d =
rpm-build 0f2925
  d.resize
rpm-build 0f2925
rpm-build 0f2925
let empty d =
rpm-build 0f2925
  d.len = 0
rpm-build 0f2925
rpm-build 0f2925
let get d idx =
rpm-build 0f2925
  if idx < 0 || idx >= d.len then invalid_arg idx "get" "index";
rpm-build 0f2925
  iget d.arr idx
rpm-build 0f2925
rpm-build 0f2925
let last d =
rpm-build 0f2925
  if d.len = 0 then invalid_arg 0 "last" "<array len is 0>";
rpm-build 0f2925
  iget d.arr (d.len - 1)
rpm-build 0f2925
rpm-build 0f2925
let set d idx v =
rpm-build 0f2925
  if idx < 0 || idx >= d.len then   invalid_arg idx "set" "index";
rpm-build 0f2925
  iset d.arr idx v
rpm-build 0f2925
rpm-build 0f2925
let insert d idx v =
rpm-build 0f2925
  if idx < 0 || idx > d.len then invalid_arg idx "insert" "index";
rpm-build 0f2925
  if d.len = ilen d.arr then changelen d (d.len + 1) else d.len <- d.len + 1;
rpm-build 0f2925
  if idx < d.len - 1 then begin
rpm-build 0f2925
    for i = d.len - 2 downto idx do
rpm-build 0f2925
      iset d.arr (i+1) (iget d.arr i)
rpm-build 0f2925
    done;
rpm-build 0f2925
  end;
rpm-build 0f2925
  iset d.arr idx v
rpm-build 0f2925
rpm-build 0f2925
let add d v =
rpm-build 0f2925
  if d.len = ilen d.arr then changelen d (d.len + 1) else d.len <- d.len + 1;
rpm-build 0f2925
  iset d.arr (d.len - 1) v
rpm-build 0f2925
rpm-build 0f2925
let delete d idx =
rpm-build 0f2925
  if idx < 0 || idx >= d.len then invalid_arg idx "delete" "index";
rpm-build 0f2925
  let oldsize = ilen d.arr in
rpm-build 0f2925
  (* we don't call changelen because we want to blit *)
rpm-build 0f2925
  let r = d.resize
rpm-build 0f2925
    ~currslots:oldsize
rpm-build 0f2925
    ~oldlength:d.len
rpm-build 0f2925
    ~newlength:(d.len - 1)
rpm-build 0f2925
  in
rpm-build 0f2925
  let newsize = (if r < d.len - 1 then d.len - 1 else r) in
rpm-build 0f2925
  if oldsize <> newsize then begin
rpm-build 0f2925
    let newarr = imake 0 newsize in
rpm-build 0f2925
    for i = 0 to idx - 1 do
rpm-build 0f2925
      iset newarr i (iget d.arr i);
rpm-build 0f2925
    done;
rpm-build 0f2925
    for i = idx to d.len - 2 do
rpm-build 0f2925
      iset newarr i (iget d.arr (i+1));
rpm-build 0f2925
    done;
rpm-build 0f2925
    d.arr <- newarr;
rpm-build 0f2925
  end else begin
rpm-build 0f2925
    for i = idx to d.len - 2 do
rpm-build 0f2925
      iset d.arr i (iget d.arr (i+1));
rpm-build 0f2925
    done;
rpm-build 0f2925
    iset d.arr (d.len - 1) (Obj.magic 0)
rpm-build 0f2925
  end;
rpm-build 0f2925
  d.len <- d.len - 1
rpm-build 0f2925
rpm-build 0f2925
rpm-build 0f2925
let delete_range d idx len =
rpm-build 0f2925
  if len < 0 then invalid_arg len "delete_range" "length";
rpm-build 0f2925
  if idx < 0 || idx + len > d.len then invalid_arg idx "delete_range" "index";
rpm-build 0f2925
  let oldsize = ilen d.arr in
rpm-build 0f2925
  (* we don't call changelen because we want to blit *)
rpm-build 0f2925
  let r = d.resize
rpm-build 0f2925
    ~currslots:oldsize
rpm-build 0f2925
    ~oldlength:d.len
rpm-build 0f2925
    ~newlength:(d.len - len)
rpm-build 0f2925
  in
rpm-build 0f2925
  let newsize = (if r < d.len - len then d.len - len else r) in
rpm-build 0f2925
  if oldsize <> newsize then begin
rpm-build 0f2925
    let newarr = imake 0 newsize in
rpm-build 0f2925
    for i = 0 to idx - 1 do
rpm-build 0f2925
      iset newarr i (iget d.arr i);
rpm-build 0f2925
    done;
rpm-build 0f2925
    for i = idx to d.len - len - 1 do
rpm-build 0f2925
      iset newarr i (iget d.arr (i+len));
rpm-build 0f2925
    done;
rpm-build 0f2925
    d.arr <- newarr;
rpm-build 0f2925
  end else begin
rpm-build 0f2925
    for i = idx to d.len - len - 1 do
rpm-build 0f2925
      iset d.arr i (iget d.arr (i+len));
rpm-build 0f2925
    done;
rpm-build 0f2925
    for i = d.len - len to d.len - 1 do
rpm-build 0f2925
      iset d.arr i (Obj.magic 0)
rpm-build 0f2925
    done;
rpm-build 0f2925
  end;
rpm-build 0f2925
  d.len <- d.len - len
rpm-build 0f2925
rpm-build 0f2925
let clear d =
rpm-build 0f2925
  d.len <- 0;
rpm-build 0f2925
  d.arr <- imake 0 0
rpm-build 0f2925
rpm-build 0f2925
let delete_last d =
rpm-build 0f2925
  if d.len <= 0 then invalid_arg 0 "delete_last" "<array len is 0>";
rpm-build 0f2925
  (* erase for GC, in case changelen don't resize our array *)
rpm-build 0f2925
  iset d.arr (d.len - 1) (Obj.magic 0);
rpm-build 0f2925
  changelen d (d.len - 1)
rpm-build 0f2925
rpm-build 0f2925
let rec blit src srcidx dst dstidx len =
rpm-build 0f2925
  if len < 0 then invalid_arg len "blit" "len";
rpm-build 0f2925
  if srcidx < 0 || srcidx + len > src.len then invalid_arg srcidx "blit" "source index";
rpm-build 0f2925
  if dstidx < 0 || dstidx > dst.len then invalid_arg dstidx "blit" "dest index";
rpm-build 0f2925
  let newlen = dstidx + len in
rpm-build 0f2925
  if newlen > ilen dst.arr then begin
rpm-build 0f2925
    (* this case could be inlined so we don't blit on just-copied elements *)
rpm-build 0f2925
    changelen dst newlen
rpm-build 0f2925
  end else begin
rpm-build 0f2925
    if newlen > dst.len then dst.len <- newlen;
rpm-build 0f2925
  end;
rpm-build 0f2925
  (* same array ! we need to copy in reverse order *)
rpm-build 0f2925
  if src.arr == dst.arr && dstidx > srcidx then
rpm-build 0f2925
    for i = len - 1 downto 0 do
rpm-build 0f2925
      iset dst.arr (dstidx+i) (iget src.arr (srcidx+i));
rpm-build 0f2925
    done
rpm-build 0f2925
  else
rpm-build 0f2925
    for i = 0 to len - 1 do
rpm-build 0f2925
      iset dst.arr (dstidx+i) (iget src.arr (srcidx+i));
rpm-build 0f2925
    done
rpm-build 0f2925
rpm-build 0f2925
let append src dst =
rpm-build 0f2925
  blit src 0 dst dst.len src.len
rpm-build 0f2925
rpm-build 0f2925
let to_list d =
rpm-build 0f2925
  let rec loop idx accum =
rpm-build 0f2925
    if idx < 0 then accum else loop (idx - 1) (iget d.arr idx :: accum)
rpm-build 0f2925
  in
rpm-build 0f2925
  loop (d.len - 1) []
rpm-build 0f2925
rpm-build 0f2925
let to_array d =
rpm-build 0f2925
  if d.len = 0 then begin
rpm-build 0f2925
    (* since the empty array is an atom, we don't care if float or not *)
rpm-build 0f2925
    [||]
rpm-build 0f2925
  end else begin
rpm-build 0f2925
    let arr = Array.make d.len (iget d.arr 0) in
rpm-build 0f2925
    for i = 1 to d.len - 1 do
rpm-build 0f2925
      Array.unsafe_set arr i (iget d.arr i)
rpm-build 0f2925
    done;
rpm-build 0f2925
    arr;
rpm-build 0f2925
  end
rpm-build 0f2925
rpm-build 0f2925
let of_list lst =
rpm-build 0f2925
  let size = List.length lst in
rpm-build 0f2925
  let arr = imake 0 size in
rpm-build 0f2925
  let rec loop idx =  function
rpm-build 0f2925
    | h :: t -> iset arr idx h; loop (idx + 1) t
rpm-build 0f2925
    | [] -> ()
rpm-build 0f2925
  in
rpm-build 0f2925
  loop 0 lst;
rpm-build 0f2925
  {
rpm-build 0f2925
    resize = default_resizer;
rpm-build 0f2925
    len = size;
rpm-build 0f2925
    arr = arr;
rpm-build 0f2925
  }
rpm-build 0f2925
rpm-build 0f2925
let of_array src =
rpm-build 0f2925
  let size = Array.length src in
rpm-build 0f2925
  let is_float = Obj.tag (Obj.repr src) = Obj.double_array_tag in
rpm-build 0f2925
  let arr = (if is_float then begin
rpm-build 0f2925
      let arr = imake 0 size in
rpm-build 0f2925
      for i = 0 to size - 1 do
rpm-build 0f2925
        iset arr i (Array.unsafe_get src i);
rpm-build 0f2925
      done;
rpm-build 0f2925
      arr
rpm-build 0f2925
    end else
rpm-build 0f2925
      (* copy the fields *)
rpm-build 0f2925
      idup (Obj.magic src : 'a intern))
rpm-build 0f2925
  in
rpm-build 0f2925
  {
rpm-build 0f2925
    resize = default_resizer;
rpm-build 0f2925
    len = size;
rpm-build 0f2925
    arr = arr;
rpm-build 0f2925
  }
rpm-build 0f2925
rpm-build 0f2925
let copy src =
rpm-build 0f2925
  {
rpm-build 0f2925
    resize = src.resize;
rpm-build 0f2925
    len = src.len;
rpm-build 0f2925
    arr = idup src.arr;
rpm-build 0f2925
  }
rpm-build 0f2925
rpm-build 0f2925
let sub src start len =
rpm-build 0f2925
  if len < 0 then invalid_arg len "sub" "len";
rpm-build 0f2925
  if start < 0 || start + len > src.len then invalid_arg start "sub" "start";
rpm-build 0f2925
  let arr = imake 0 len in
rpm-build 0f2925
  for i = 0 to len - 1 do
rpm-build 0f2925
    iset arr i (iget src.arr (i+start));
rpm-build 0f2925
  done;
rpm-build 0f2925
  {
rpm-build 0f2925
    resize = src.resize;
rpm-build 0f2925
    len = len;
rpm-build 0f2925
    arr = arr;
rpm-build 0f2925
  }
rpm-build 0f2925
rpm-build 0f2925
let iter f d =
rpm-build 0f2925
  for i = 0 to d.len - 1 do
rpm-build 0f2925
    f (iget d.arr i)
rpm-build 0f2925
  done
rpm-build 0f2925
rpm-build 0f2925
let iteri f d =
rpm-build 0f2925
  for i = 0 to d.len - 1 do
rpm-build 0f2925
    f i (iget d.arr i)
rpm-build 0f2925
  done
rpm-build 0f2925
rpm-build 0f2925
let filter f d =
rpm-build 0f2925
  let l = d.len in
rpm-build 0f2925
  let a = imake 0 l in
rpm-build 0f2925
  let a2 = d.arr in
rpm-build 0f2925
  let p = ref 0 in
rpm-build 0f2925
  for i = 0 to l - 1 do
rpm-build 0f2925
    let x = iget a2 i in
rpm-build 0f2925
    if f x then begin
rpm-build 0f2925
      iset a !p x;
rpm-build 0f2925
      incr p;
rpm-build 0f2925
    end;
rpm-build 0f2925
  done;
rpm-build 0f2925
  d.len <- !p;
rpm-build 0f2925
  d.arr <- a
rpm-build 0f2925
rpm-build 0f2925
let index_of f d =
rpm-build 0f2925
  let rec loop i =
rpm-build 0f2925
    if i >= d.len then
rpm-build 0f2925
      raise Not_found
rpm-build 0f2925
    else
rpm-build 0f2925
      if f (iget d.arr i) then
rpm-build 0f2925
        i
rpm-build 0f2925
      else
rpm-build 0f2925
        loop (i+1)
rpm-build 0f2925
  in
rpm-build 0f2925
  loop 0
rpm-build 0f2925
rpm-build 0f2925
let map f src =
rpm-build 0f2925
  let arr = imake 0 src.len in
rpm-build 0f2925
  for i = 0 to src.len - 1 do
rpm-build 0f2925
    iset arr i (f (iget src.arr i))
rpm-build 0f2925
  done;
rpm-build 0f2925
  {
rpm-build 0f2925
    resize = src.resize;
rpm-build 0f2925
    len = src.len;
rpm-build 0f2925
    arr = arr;
rpm-build 0f2925
  }
rpm-build 0f2925
rpm-build 0f2925
let mapi f src =
rpm-build 0f2925
  let arr = imake 0 src.len in
rpm-build 0f2925
  for i = 0 to src.len - 1 do
rpm-build 0f2925
    iset arr i (f i (iget src.arr i))
rpm-build 0f2925
  done;
rpm-build 0f2925
  {
rpm-build 0f2925
    resize = src.resize;
rpm-build 0f2925
    len = src.len;
rpm-build 0f2925
    arr = arr;
rpm-build 0f2925
  }
rpm-build 0f2925
rpm-build 0f2925
let fold_left f x a =
rpm-build 0f2925
  let rec loop idx x =
rpm-build 0f2925
    if idx >= a.len then x else loop (idx + 1) (f x (iget a.arr idx))
rpm-build 0f2925
  in
rpm-build 0f2925
  loop 0 x
rpm-build 0f2925
rpm-build 0f2925
let fold_right f a x =
rpm-build 0f2925
  let rec loop idx x =
rpm-build 0f2925
    if idx < 0 then x
rpm-build 0f2925
    else loop (idx - 1) (f (iget a.arr idx) x)
rpm-build 0f2925
  in
rpm-build 0f2925
  loop (a.len - 1) x
rpm-build 0f2925
rpm-build 0f2925
let enum d =
rpm-build 0f2925
  let rec make start =
rpm-build 0f2925
    let idxref = ref 0 in
rpm-build 0f2925
    let next () =
rpm-build 0f2925
      if !idxref >= d.len then
rpm-build 0f2925
        raise Enum.No_more_elements
rpm-build 0f2925
      else
rpm-build 0f2925
        let retval = iget d.arr !idxref in
rpm-build 0f2925
        incr idxref;
rpm-build 0f2925
        retval
rpm-build 0f2925
    and count () =
rpm-build 0f2925
      if !idxref >= d.len then 0
rpm-build 0f2925
      else d.len - !idxref
rpm-build 0f2925
    and clone () =
rpm-build 0f2925
      make !idxref
rpm-build 0f2925
    in
rpm-build 0f2925
    Enum.make ~next:next ~count:count ~clone:clone
rpm-build 0f2925
  in
rpm-build 0f2925
  make 0
rpm-build 0f2925
rpm-build 0f2925
let of_enum e =
rpm-build 0f2925
  if Enum.fast_count e then begin
rpm-build 0f2925
    let c = Enum.count e in
rpm-build 0f2925
    let arr = imake 0 c in
rpm-build 0f2925
    Enum.iteri (fun i x -> iset arr i x) e;
rpm-build 0f2925
    {
rpm-build 0f2925
      resize = default_resizer;
rpm-build 0f2925
      len = c;
rpm-build 0f2925
      arr = arr;
rpm-build 0f2925
    }
rpm-build 0f2925
  end else
rpm-build 0f2925
    let d = make 0 in
rpm-build 0f2925
    Enum.iter (add d) e;
rpm-build 0f2925
    d
rpm-build 0f2925
rpm-build 0f2925
let unsafe_get a n =
rpm-build 0f2925
  iget a.arr n
rpm-build 0f2925
rpm-build 0f2925
let unsafe_set a n x =
rpm-build 0f2925
  iset a.arr n x