Skip to content

Commit 6dc92b8

Browse files
committed
Implement various standard functions for nullable arrays
1 parent 3ea87b1 commit 6dc92b8

2 files changed

Lines changed: 142 additions & 0 deletions

File tree

lib/nullable_array.ml

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,18 @@ let make (n:int) : 'a t =
139139
already does it *)
140140
Array.make (n+1) (null:elt)
141141

142+
let make_some (n:int) (v:'a) : 'a t =
143+
if n < 0 then invalid_arg "Nullable_array.make_some";
144+
let a = Array.make (n+1) (Obj.magic (Sys.opaque_identity v) : elt) in
145+
Array.unsafe_set a 0 null;
146+
a
147+
148+
let init_some (n:int) (f:int -> 'a) : 'a t =
149+
if n < 0 then invalid_arg "Nullable_array.init_some";
150+
Array.init (n+1) (function
151+
| 0 -> (null:elt)
152+
| i -> (Obj.magic (f (i-1)) : elt))
153+
142154
let empty_array : 'a t = [| null |]
143155

144156
let get_null (a:'a t) : elt =
@@ -171,6 +183,10 @@ let set_some (a:'a t) (n:int) (v:'a) : unit =
171183
if n < 0 then invalid_arg "Nullable_array.set_some";
172184
set_elt (a:'a t) (n+1) (Obj.magic v : elt)
173185

186+
let fill_some (a:'a t) (pos:int) (len:int) (v:'a) : unit =
187+
let v = (Sys.opaque_identity (Obj.magic v : elt)) in
188+
Array.fill a (pos+1) len v
189+
174190
let clear (a:'a t) (n:int) : unit =
175191
if n < 0 then invalid_arg "Nullable_array.clear";
176192
let null = get_null a in
@@ -196,6 +212,49 @@ let iteri ~(some:int -> 'a -> unit) ~(none:int -> unit) (a:'a t) : unit =
196212
done
197213
[@@ocaml.inline]
198214

215+
let map_some (f:'a -> 'b) (from:'a t) : 'b t =
216+
let null = get_null from in
217+
let len = Array.length from in
218+
let to_ = Array.make len null in
219+
for i = 1 to len - 1 do
220+
let elt = Array.unsafe_get from i in
221+
if elt != null then
222+
let elt' : elt = Obj.magic (f (Obj.magic elt:'a)) in
223+
unsafe_set_elt to_ i elt'
224+
done;
225+
to_
226+
227+
let mapi_some (f:int -> 'a -> 'b) (from:'a t) : 'b t =
228+
let null = get_null from in
229+
let len = Array.length from in
230+
let to_ = Array.make len null in
231+
for i = 1 to len - 1 do
232+
let elt = Array.unsafe_get from i in
233+
if elt != null then
234+
let elt' : elt = Obj.magic (f (i-1) (Obj.magic elt:'a)) in
235+
unsafe_set_elt to_ i elt'
236+
done;
237+
to_
238+
239+
let unsafe_sub (a:'a t) (pos:int) (len:int) : 'a t =
240+
if pos = 0 then
241+
(* Let the runtime copy the null element *)
242+
Array.sub a pos (len+1)
243+
else
244+
(* Include an extra element at the start of the new array,
245+
then set it to [null]. *)
246+
let res = Array.sub a (pos-1) (len+1) in
247+
unsafe_set_elt res 0 (get_null a);
248+
res
249+
250+
let sub (a:'a t) (pos:int) (len:int) : 'a t =
251+
if pos < 0 || len < 0 || pos > length a - len
252+
then invalid_arg "Nullable_array.sub"
253+
else unsafe_sub a pos len
254+
255+
let copy (a:'a t) : 'a t =
256+
unsafe_sub a 0 (length a)
257+
199258
let unsafe_manual_blit (from:'a t) (from_start:int) (to_:'a t) (to_start:int) (len:int) =
200259
let null_from = get_null from in
201260
let null_to = get_null to_ in
@@ -224,6 +283,17 @@ let blit (from:'a t) (from_start:int) (to_:'a t) (to_start:int) (len:int) =
224283
(unsafe_manual_blit [@inlined never]) from from_start to_ to_start len
225284
end
226285

286+
let of_array (a:'a array) : 'a t =
287+
init_some (Array.length a) (fun i -> Array.unsafe_get a i)
288+
289+
let of_list (l:'a list) : 'a t =
290+
let a = make (List.length l) in
291+
let rec fill i = function
292+
| [] -> a
293+
| x :: xs -> unsafe_set_elt a i (Obj.magic x : elt); fill (i+1) xs
294+
in
295+
fill 1 l
296+
227297
let equal (a1:'a t) (a2:'a t) ~(equal:'a -> 'a -> bool) =
228298
length a1 = length a2 &&
229299
let null1 = get_null a1 in
@@ -247,6 +317,8 @@ let equal (a1:'a t) (a2:'a t) ~(equal:'a -> 'a -> bool) =
247317
in
248318
loop (length a1)
249319

320+
let max_length = Sys.max_array_length - 1
321+
250322
(* Unsafe functions *)
251323

252324
let unsafe_get_some (a:'a t) (n:int) : 'a =

lib/nullable_array.mli

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,26 @@ val make : int -> 'a t
4444
4545
Raise [Invalid_argument] if [n < 0] or [n > Sys.max_array_length - 1]. *)
4646

47+
val make_some : int -> 'a -> 'a t
48+
(** [make_some n x] Create an array of size [n] in which each element is
49+
initially set to [Some x].
50+
51+
Raise [Invalid_argument] if [n < 0] or [n > Sys.max_array_length - 1]. *)
52+
53+
val init_some : int -> (int -> 'a) -> 'a t
54+
(** [init_some n f] Returns a fresh array of length [n], with the element at
55+
index [i] given by [Some (f i)].
56+
57+
Raise [Invalid_argument] if [n < 0] or [n > Sys.max_array_length - 1]. *)
58+
59+
val sub : 'a t -> int -> int -> 'a t
60+
(** [sub a pos len] returns a fresh array of length [len], containing the
61+
elements number [pos] to [pos + len - 1] of array [a].
62+
63+
Raise [Invalid_argument] if [pos] and [len] do not designate a valid
64+
subarray of a; that is, if [pos < 0], or [len < 0], or [pos + len > length
65+
a]. *)
66+
4767
val empty_array : 'a t
4868
(** A preallocated empty array *)
4969

@@ -81,6 +101,13 @@ val set_some : 'a t -> int -> 'a -> unit
81101
{[set_some a n v; assert( get a n = Some v )]}
82102
*)
83103

104+
val fill_some : 'a t -> int -> int -> 'a -> unit
105+
(** [fill_some a pos len x] Modifies array [a] in place, replacing
106+
each element from [pos] to [pos + len - 1] with [Some x].
107+
108+
Raise [Invalid_argument "index out of bounds"] if [pos]
109+
and [len] do not designate a valid subarray of [a]. *)
110+
84111
val clear : 'a t -> int -> unit
85112
(** [clear a n] Modifies array [a] in place, replacing
86113
element number [n] with [None].
@@ -100,6 +127,34 @@ val iteri : some:(int -> 'a -> unit) -> none:(int -> unit) -> 'a t -> unit
100127
[some 0 v0; none 1; some 2 v2].
101128
*)
102129

130+
val map_some : ('a -> 'b) -> 'a t -> 'b t
131+
(** [map_some f a] builds an array [a'] of size equal to [a] in which each
132+
non-null element is given by applying [f] to the corresponding element
133+
in [a].
134+
135+
For example:
136+
137+
{[
138+
map_some f [| Some v0; None; Some v2; None |]
139+
= [| Some (f v0); None; Some (f v2); None |]
140+
]}
141+
*)
142+
143+
val mapi_some : (int -> 'a -> 'b) -> 'a t -> 'b t
144+
(** [mapi_some] is like {!map_some}, but also supplies the index of non-null
145+
elements to the mapping function.
146+
147+
For example:
148+
149+
{[
150+
mapi_some f [| Some v0; None; Some v2; None |]
151+
= [| Some (f 0 v0); None; Some (f 2 v2); None |]
152+
]}
153+
*)
154+
155+
val copy : 'a t -> 'a t
156+
(** [copy a] results a fresh array containing the same elements as [a]. *)
157+
103158
val blit : 'a t -> int -> 'a t -> int -> int -> unit
104159
(** [blit from from_start to to_start len] copies [len] elements
105160
from array [from], starting at element number [from_start],
@@ -112,6 +167,18 @@ val blit : 'a t -> int -> 'a t -> int -> int -> unit
112167
[to_start] and [len] do not designate a valid subarray of [to].
113168
*)
114169

170+
val of_array : 'a array -> 'a t
171+
(** [of_array a] returns a fresh array containing the elements of [a].
172+
173+
Raise [Invalid_argument] if the length of [a] is greater than
174+
{!max_length}. *)
175+
176+
val of_list : 'a list -> 'a t
177+
(** [of_list l] returns a fresh array containing the elements of [l].
178+
179+
Raise [Invalid_argument] if the length of [l] is greater than
180+
{!max_length}. *)
181+
115182
val equal : 'a t -> 'a t -> equal:('a -> 'a -> bool) -> bool
116183
(** [equal a1 a2 ~equal] is true if [a1] and [a2] have the same length
117184
and for all elements of [a1] and [a2]
@@ -124,6 +191,9 @@ val equal : 'a t -> 'a t -> equal:('a -> 'a -> bool) -> bool
124191
[equal empty_array empty_array ~equal] is [true]
125192
*)
126193

194+
val max_length : int
195+
(** [max_length] is [Sys.max_array_length - 1]. *)
196+
127197
(**/**)
128198
(** {6 Undocumented functions} *)
129199

0 commit comments

Comments
 (0)