2 * DynArray - Resizeable Ocaml arrays
3 * Copyright (C) 2003 Brian Hurt
4 * Copyright (C) 2003 Nicolas Cannasse
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version,
10 * with the special exception on linking described in file LICENSE.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 type resizer_t
= currslots
:int -> oldlength
:int -> newlength
:int -> int
26 external ilen
: 'a intern
-> int = "%obj_size"
27 let idup (x
: 'a intern
) = if ilen x
= 0 then x
else (Obj.magic
(Obj.dup
(Obj.repr x
)) : 'a intern
)
28 let imake tag len
= (Obj.magic
(Obj.new_block tag len
) : 'a intern
)
29 external iget
: 'a intern
-> int -> 'a
= "%obj_field"
30 external iset
: 'a intern
-> int -> 'a
-> unit = "%obj_set_field"
33 mutable arr
: 'a intern
;
35 mutable resize
: resizer_t
;
38 exception Invalid_arg
of int * string * string
40 let invalid_arg n f p
= raise
(Invalid_arg
(n
,f
,p
))
44 let exponential_resizer ~currslots ~oldlength ~newlength
=
45 let rec doubler x
= if x
>= newlength
then x
else doubler (x
* 2) in
46 let rec halfer x
= if x
/ 2 < newlength
then x
else halfer (x
/ 2) in
49 else if currslots
= 0 then
51 else if currslots
< newlength
then
56 let step_resizer step
=
57 if step
<= 0 then invalid_arg step
"step_resizer" "step";
58 (fun ~currslots ~oldlength ~newlength
->
59 if currslots
< newlength
|| newlength
< (currslots
- step
)
61 (newlength
+ step
- (newlength
mod step
))
65 let conservative_exponential_resizer ~currslots ~oldlength ~newlength
=
66 let rec doubler x
= if x
>= newlength
then x
else doubler (x
* 2) in
67 let rec halfer x
= if x
/ 2 < newlength
then x
else halfer (x
/ 2) in
68 if currslots
< newlength
then begin
71 else if currslots
= 0 then
75 end else if oldlength
< newlength
then
80 let default_resizer = conservative_exponential_resizer
82 let changelen (d
: 'a t
) newlen
=
83 let oldsize = ilen d
.arr
in
89 (* We require the size to be at least large enough to hold the number
90 * of elements we know we need!
92 let newsize = if r < newlen
then newlen
else r in
93 if newsize <> oldsize then begin
94 let newarr = imake 0 newsize in
95 let cpylen = (if newlen
< d
.len
then newlen
else d
.len
) in
96 for i
= 0 to cpylen - 1 do
97 iset
newarr i
(iget d
.arr i
);
104 if d
.len
<> ilen d
.arr
then begin
105 let newarr = imake 0 d
.len
in
106 for i
= 0 to d
.len
- 1 do
107 iset
newarr i
(iget d
.arr i
)
114 resize
= default_resizer;
120 if initsize
< 0 then invalid_arg initsize
"make" "size";
122 resize
= default_resizer;
124 arr
= imake 0 initsize
;
128 if initlen
< 0 then invalid_arg initlen
"init" "len";
129 let arr = imake 0 initlen
in
130 for i
= 0 to initlen
-1 do
134 resize
= default_resizer;
139 let set_resizer d resizer
=
149 if idx
< 0 || idx
>= d
.len
then invalid_arg idx
"get" "index";
153 if d
.len
= 0 then invalid_arg 0 "last" "<array len is 0>";
154 iget d
.arr (d
.len
- 1)
157 if idx
< 0 || idx
>= d
.len
then invalid_arg idx
"set" "index";
161 if idx
< 0 || idx
> d
.len
then invalid_arg idx
"insert" "index";
162 if d
.len
= ilen d
.arr then changelen d
(d
.len
+ 1) else d
.len
<- d
.len
+ 1;
163 if idx
< d
.len
- 1 then begin
164 for i
= d
.len
- 1 downto idx
do
165 iset d
.arr (i
+1) (iget d
.arr i
)
171 if d
.len
= ilen d
.arr then changelen d
(d
.len
+ 1) else d
.len
<- d
.len
+ 1;
172 iset d
.arr (d
.len
- 1) v
175 if idx
< 0 || idx
>= d
.len
then invalid_arg idx
"delete" "index";
176 let oldsize = ilen d
.arr in
177 (* we don't call changelen because we want to blit *)
181 ~newlength
:(d
.len
- 1)
183 let newsize = (if r < d
.len
- 1 then d
.len
- 1 else r) in
184 if oldsize <> newsize then begin
185 let newarr = imake 0 newsize in
186 for i
= 0 to idx
- 1 do
187 iset
newarr i
(iget d
.arr i
);
189 for i
= idx
to d
.len
- 2 do
190 iset
newarr i
(iget d
.arr (i
+1));
194 for i
= idx
to d
.len
- 2 do
195 iset d
.arr i
(iget d
.arr (i
+1));
197 iset d
.arr (d
.len
- 1) (Obj.magic
0)
202 let delete_range d idx len
=
203 if len
< 0 then invalid_arg len
"delete_range" "length";
204 if idx
< 0 || idx
+ len
> d
.len
then invalid_arg idx
"delete_range" "index";
205 let oldsize = ilen d
.arr in
206 (* we don't call changelen because we want to blit *)
210 ~newlength
:(d
.len
- len
)
212 let newsize = (if r < d
.len
- len
then d
.len
- len
else r) in
213 if oldsize <> newsize then begin
214 let newarr = imake 0 newsize in
215 for i
= 0 to idx
- 1 do
216 iset
newarr i
(iget d
.arr i
);
218 for i
= idx
to d
.len
- len
- 1 do
219 iset
newarr i
(iget d
.arr (i
+len
));
223 for i
= idx
to d
.len
- len
- 1 do
224 iset d
.arr i
(iget d
.arr (i
+len
));
226 for i
= d
.len
- len
to d
.len
- 1 do
227 iset d
.arr i
(Obj.magic
0)
237 if d
.len
<= 0 then invalid_arg 0 "delete_last" "<array len is 0>";
238 (* erase for GC, in case changelen don't resize our array *)
239 iset d
.arr (d
.len
- 1) (Obj.magic
0);
240 changelen d
(d
.len
- 1)
242 let rec blit src srcidx dst dstidx len
=
243 if len
< 0 then invalid_arg len
"blit" "len";
244 if srcidx
< 0 || srcidx
+ len
> src
.len
then invalid_arg srcidx
"blit" "source index";
245 if dstidx
< 0 || dstidx
> dst
.len
then invalid_arg dstidx
"blit" "dest index";
246 let newlen = dstidx
+ len
in
247 if newlen > ilen dst
.arr then begin
248 (* this case could be inlined so we don't blit on just-copied elements *)
251 if newlen > dst
.len
then dst
.len
<- newlen;
253 (* same array ! we need to copy in reverse order *)
254 if src
.arr == dst
.arr && dstidx
> srcidx
then
255 for i
= len
- 1 downto 0 do
256 iset dst
.arr (dstidx
+i
) (iget src
.arr (srcidx
+i
));
259 for i
= 0 to len
- 1 do
260 iset dst
.arr (dstidx
+i
) (iget src
.arr (srcidx
+i
));
264 blit src
0 dst dst
.len src
.len
267 let rec loop idx accum
=
268 if idx
< 0 then accum
else loop (idx
- 1) (iget d
.arr idx
:: accum
)
273 if d
.len
= 0 then begin
274 (* since the empty array is an atom, we don't care if float or not *)
277 let arr = Array.make d
.len
(iget d
.arr 0) in
278 for i
= 1 to d
.len
- 1 do
279 Array.unsafe_set
arr i
(iget d
.arr i
)
285 let size = List.length lst
in
286 let arr = imake 0 size in
287 let rec loop idx
= function
288 | h
:: t
-> iset
arr idx h
; loop (idx
+ 1) t
293 resize
= default_resizer;
299 let size = Array.length src
in
300 let is_float = Obj.tag
(Obj.repr src
) = Obj.double_array_tag
in
301 let arr = (if is_float then begin
302 let arr = imake 0 size in
303 for i
= 0 to size - 1 do
304 iset
arr i
(Array.unsafe_get src i
);
308 (* copy the fields *)
309 idup (Obj.magic src
: 'a intern
))
312 resize
= default_resizer;
324 let sub src start len
=
325 if len
< 0 then invalid_arg len
"sub" "len";
326 if start
< 0 || start
+ len
> src
.len
then invalid_arg start
"sub" "start";
327 let arr = imake 0 len
in
328 for i
= 0 to len
- 1 do
329 iset
arr i
(iget src
.arr (i
+start
));
338 for i
= 0 to d
.len
- 1 do
343 for i
= 0 to d
.len
- 1 do
352 for i
= 0 to l - 1 do
367 if f
(iget d
.arr i
) then
375 let arr = imake 0 src
.len
in
376 for i
= 0 to src
.len
- 1 do
377 iset
arr i
(f
(iget src
.arr i
))
386 let arr = imake 0 src
.len
in
387 for i
= 0 to src
.len
- 1 do
388 iset
arr i
(f i
(iget src
.arr i
))
396 let fold_left f
x a =
398 if idx
>= a.len
then x else loop (idx
+ 1) (f
x (iget
a.arr idx
))
402 let fold_right f
a x =
405 else loop (idx
- 1) (f
(iget
a.arr idx
) x)
411 let idxref = ref 0 in
413 if !idxref >= d
.len
then
414 raise
Enum.No_more_elements
416 let retval = iget d
.arr !idxref in
420 if !idxref >= d
.len
then 0
425 Enum.make ~
next:next ~count
:count ~clone
:clone
430 if Enum.fast_count e
then begin
431 let c = Enum.count e
in
432 let arr = imake 0 c in
433 Enum.iteri (fun i
x -> iset
arr i
x) e
;
435 resize
= default_resizer;
447 let unsafe_set a n
x =