cpdf-source/cpdfstream.ml

237 lines
6.9 KiB
OCaml
Raw Normal View History

2021-09-30 20:55:52 +02:00
(**************************************************************************)
(* *)
(* OCaml *)
(* *)
(* Daniel de Rauglaudre, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1997 Institut National de Recherche en Informatique et *)
(* en Automatique. *)
(* *)
(* All rights reserved. This file is distributed under the terms of *)
(* the GNU Lesser General Public License version 2.1, with the *)
(* special exception on linking described in the file LICENSE. *)
(* *)
(**************************************************************************)
type 'a t = 'a cell option
and 'a cell = { mutable count : int; mutable data : 'a data }
and 'a data =
Sempty
| Scons of 'a * 'a data
| Sapp of 'a data * 'a data
| Slazy of 'a data Lazy.t
| Sgen of 'a gen
| Sbuffio : buffio -> char data
and 'a gen = { mutable curr : 'a option option; func : int -> 'a option }
and buffio =
{ ic : in_channel; buff : bytes; mutable len : int; mutable ind : int }
exception Failure
exception Error of string
let count = function
| None -> 0
| Some { count } -> count
let data = function
| None -> Sempty
| Some { data } -> data
let fill_buff b =
b.len <- input b.ic b.buff 0 (Bytes.length b.buff); b.ind <- 0
let rec get_data : type v. int -> v data -> v data = fun count d -> match d with
(* Returns either Sempty or Scons(a, _) even when d is a generator
or a buffer. In those cases, the item a is seen as extracted from
the generator/buffer.
The count parameter is used for calling `Sgen-functions'. *)
Sempty | Scons (_, _) -> d
| Sapp (d1, d2) ->
begin match get_data count d1 with
Scons (a, d11) -> Scons (a, Sapp (d11, d2))
| Sempty -> get_data count d2
| _ -> assert false
end
| Sgen {curr = Some None} -> Sempty
| Sgen ({curr = Some(Some a)} as g) ->
g.curr <- None; Scons(a, d)
| Sgen g ->
begin match g.func count with
None -> g.curr <- Some(None); Sempty
| Some a -> Scons(a, d)
(* Warning: anyone using g thinks that an item has been read *)
end
| Sbuffio b ->
if b.ind >= b.len then fill_buff b;
if b.len == 0 then Sempty else
let r = Bytes.unsafe_get b.buff b.ind in
(* Warning: anyone using g thinks that an item has been read *)
b.ind <- succ b.ind; Scons(r, d)
| Slazy f -> get_data count (Lazy.force f)
let rec peek_data : type v. v cell -> v option = fun s ->
(* consult the first item of s *)
match s.data with
Sempty -> None
| Scons (a, _) -> Some a
| Sapp (_, _) ->
begin match get_data s.count s.data with
Scons(a, _) as d -> s.data <- d; Some a
| Sempty -> None
| _ -> assert false
end
| Slazy f -> s.data <- (Lazy.force f); peek_data s
| Sgen {curr = Some a} -> a
| Sgen g -> let x = g.func s.count in g.curr <- Some x; x
| Sbuffio b ->
if b.ind >= b.len then fill_buff b;
if b.len == 0 then begin s.data <- Sempty; None end
else Some (Bytes.unsafe_get b.buff b.ind)
let peek = function
| None -> None
| Some s -> peek_data s
let rec junk_data : type v. v cell -> unit = fun s ->
match s.data with
Scons (_, d) -> s.count <- (succ s.count); s.data <- d
| Sgen ({curr = Some _} as g) -> s.count <- (succ s.count); g.curr <- None
| Sbuffio b ->
if b.ind >= b.len then fill_buff b;
if b.len == 0 then s.data <- Sempty
else (s.count <- (succ s.count); b.ind <- succ b.ind)
| _ ->
match peek_data s with
None -> ()
| Some _ -> junk_data s
let junk = function
| None -> ()
| Some data -> junk_data data
let rec nget_data n s =
if n <= 0 then [], s.data, 0
else
match peek_data s with
Some a ->
junk_data s;
let (al, d, k) = nget_data (pred n) s in a :: al, Scons (a, d), succ k
| None -> [], s.data, 0
let npeek_data n s =
let (al, d, len) = nget_data n s in
s.count <- (s.count - len);
s.data <- d;
al
let npeek n = function
| None -> []
| Some d -> npeek_data n d
let next s =
match peek s with
Some a -> junk s; a
| None -> raise Failure
let empty s =
match peek s with
Some _ -> raise Failure
| None -> ()
let iter f strm =
let rec do_rec () =
match peek strm with
Some a -> junk strm; ignore(f a); do_rec ()
| None -> ()
in
do_rec ()
(* Stream building functions *)
let from f = Some {count = 0; data = Sgen {curr = None; func = f}}
let of_list l =
Some {count = 0; data = List.fold_right (fun x l -> Scons (x, l)) l Sempty}
let of_string s =
let count = ref 0 in
from (fun _ ->
(* We cannot use the index passed by the [from] function directly
because it returns the current stream count, with absolutely no
guarantee that it will start from 0. For example, in the case
of [Stream.icons 'c' (Stream.from_string "ab")], the first
access to the string will be made with count [1] already.
*)
let c = !count in
if c < String.length s
then (incr count; Some s.[c])
else None)
let of_bytes s =
let count = ref 0 in
from (fun _ ->
let c = !count in
if c < Bytes.length s
then (incr count; Some (Bytes.get s c))
else None)
let of_channel ic =
Some {count = 0;
data = Sbuffio {ic = ic; buff = Bytes.create 4096; len = 0; ind = 0}}
(* Stream expressions builders *)
let iapp i s = Some {count = 0; data = Sapp (data i, data s)}
let icons i s = Some {count = 0; data = Scons (i, data s)}
let ising i = Some {count = 0; data = Scons (i, Sempty)}
let lapp f s =
Some {count = 0; data = Slazy (lazy(Sapp (data (f ()), data s)))}
let lcons f s = Some {count = 0; data = Slazy (lazy(Scons (f (), data s)))}
let lsing f = Some {count = 0; data = Slazy (lazy(Scons (f (), Sempty)))}
let sempty = None
let slazy f = Some {count = 0; data = Slazy (lazy(data (f ())))}
(* For debugging use *)
let rec dump : type v. (v -> unit) -> v t -> unit = fun f s ->
print_string "{count = ";
print_int (count s);
print_string "; data = ";
dump_data f (data s);
print_string "}";
print_newline ()
and dump_data : type v. (v -> unit) -> v data -> unit = fun f ->
function
Sempty -> print_string "Sempty"
| Scons (a, d) ->
print_string "Scons (";
f a;
print_string ", ";
dump_data f d;
print_string ")"
| Sapp (d1, d2) ->
print_string "Sapp (";
dump_data f d1;
print_string ", ";
dump_data f d2;
print_string ")"
| Slazy _ -> print_string "Slazy"
| Sgen _ -> print_string "Sgen"
| Sbuffio _ -> print_string "Sbuffio"