(* Lightweight thread library for Objective Caml * http://www.ocsigen.org/lwt * Module Lwt_stream * Copyright (C) 2009 Jérémie Dimino * * 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, with linking exceptions; * either version 2.1 of the License, or (at your option) any later * version. See COPYING file for details. * * 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. *) open Lwt exception Empty type 'a t = { next : unit -> 'a option Lwt.t; (* The source of the stream *) queue : 'a option Queue.t; (* Queue of pending elements, which are not yet consumed *) clones : 'a option Queue.t Weak.t ref; (* List of queues of all clones of this event (including this event) *) mutex : Lwt_mutex.t; (* Mutex to prevent concurrent access to [next] *) } let add_clone wa q = let len = Weak.length !wa in (* loop search for a free cell in [wa] and fill it with [q]: *) let rec loop i = if i = len then begin (* Growing *) let clones = Weak.create (len + 1) in Weak.blit !wa 0 clones 0 len; wa := clones; Weak.set clones len (Some q) end else if Weak.check !wa i then loop (i + 1) else Weak.set !wa i (Some q) in loop 0 let clone s = let s' = { next = s.next; queue = Queue.copy s.queue; clones = s.clones; mutex = s.mutex; } in add_clone s'.clones s'.queue; s' let from f = let s = { next = f; queue = Queue.create (); clones = ref(Weak.create 1); mutex = Lwt_mutex.create (); } in Weak.set !(s.clones) 0 (Some s.queue); s let of_list l = let l = ref l in from (fun () -> match !l with | [] -> return None | x :: l' -> l := l'; return (Some x)) let of_array a = let len = Array.length a and i = ref 0 in from (fun () -> if !i = len then return None else begin let c = Array.unsafe_get a !i in incr i; return (Some c) end) let of_string s = let len = String.length s and i = ref 0 in from (fun () -> if !i = len then return None else begin let c = String.unsafe_get s !i in incr i; return (Some c) end) module EQueue : sig type 'a t val create : unit -> 'a t * ('a option -> unit) val pop : 'a t -> 'a option Lwt.t end = struct type 'a state = | No_mail | Waiting of 'a option Lwt.u | Full of 'a option Queue.t type 'a t = { mutable state : 'a state; } let create () = let box = { state = No_mail } in let weak_box = Weak.create 1 in Weak.set weak_box 0 (Some box); let push v = match Weak.get weak_box 0 with | None -> () | Some box -> match box.state with | No_mail -> let q = Queue.create () in Queue.push v q; box.state <- Full q | Waiting wakener -> box.state <- No_mail; wakeup_later wakener v | Full q -> Queue.push v q in (box, push) let pop b = match b.state with | No_mail -> let waiter, wakener = task () in Lwt.on_cancel waiter (fun () -> b.state <- No_mail); b.state <- Waiting wakener; waiter | Waiting _ -> (* Calls to next are serialized, so this case will never happened *) assert false | Full q -> let v = Queue.take q in if Queue.is_empty q then b.state <- No_mail; return v end let create () = let box, push = EQueue.create () in (from (fun () -> EQueue.pop box), push) let push_clones wa x = for i = 0 to Weak.length wa - 1 do match Weak.get wa i with | Some q -> Queue.push x q | None -> () done let peek s = if Queue.is_empty s.queue then Lwt_mutex.with_lock s.mutex begin fun () -> if Queue.is_empty s.queue then begin lwt result = s.next () in push_clones !(s.clones) result; return result end else return (Queue.top s.queue) end else return (Queue.top s.queue) let rec force n s = if Queue.length s.queue >= n then return () else Lwt_mutex.with_lock s.mutex begin fun () -> if Queue.length s.queue >= n then return false else begin lwt result = s.next () in push_clones !(s.clones) result; if result = None then return false else return true end end >>= function | true -> force n s | false -> return () let npeek n s = lwt () = force n s in let l, _ = Queue.fold (fun (l, n) x -> if n > 0 then match x with | Some x -> (x :: l, n - 1) | None -> (l, n) else (l, n)) ([], n) s.queue in return (List.rev l) let rec get s = if Queue.is_empty s.queue then Lwt_mutex.with_lock s.mutex begin fun () -> if Queue.is_empty s.queue then begin lwt x = s.next () in (* This prevent from calling s.next when the stream has terminated: *) if x = None then Queue.push None s.queue; let wa = !(s.clones) in for i = 0 to Weak.length wa - 1 do match Weak.get wa i with | Some q when q != s.queue -> Queue.push x q | _ -> () done; return x end else begin let x = Queue.take s.queue in if x = None then Queue.push None s.queue; return x end end else begin let x = Queue.take s.queue in if x = None then Queue.push None s.queue; return x end let nget n s = let rec loop n = if n <= 0 then return [] else get s >>= function | Some x -> lwt l = loop (n - 1) in return (x :: l) | None -> return [] in loop n let get_while f s = let rec loop () = peek s >>= function | Some x -> let test = f x in if test then begin ignore (Queue.take s.queue); lwt l = loop () in return (x :: l) end else return [] | None -> return [] in loop () let get_while_s f s = let rec loop () = peek s >>= function | Some x -> lwt test = f x in if test then begin ignore (Queue.take s.queue); lwt l = loop () in return (x :: l) end else return [] | None -> return [] in loop () let next s = get s >>= function | Some x -> return x | None -> raise_lwt Empty let last_new s = match Lwt.state (peek s) with | Return None -> raise_lwt Empty | Sleep -> next s | Fail exn -> raise_lwt exn | Return(Some x) -> let _ = Queue.take s.queue in let rec loop last = match Lwt.state (peek s) with | Sleep | Return None -> return last | Return(Some x) -> let _ = Queue.take s.queue in loop x | Fail exn -> raise_lwt exn in loop x let to_list s = let rec loop () = get s >>= function | Some x -> lwt l = loop () in return (x :: l) | None -> return [] in loop () let to_string s = let buf = Buffer.create 42 in let rec loop () = get s >>= function | Some x -> Buffer.add_char buf x; loop () | None -> return (Buffer.contents buf) in loop () let junk s = lwt _ = get s in return () let njunk n s = let rec loop n = if n <= 0 then return () else lwt _ = get s in loop (n - 1) in loop n let junk_while f s = let rec loop () = peek s >>= function | Some x -> let test = f x in if test then begin ignore (Queue.take s.queue); loop () end else return () | None -> return () in loop () let junk_while_s f s = let rec loop () = peek s >>= function | Some x -> lwt test = f x in if test then begin ignore (Queue.take s.queue); loop () end else return () | None -> return () in loop () let junk_old s = let rec loop () = match Lwt.state (peek s) with | Sleep -> return () | _ -> ignore (Queue.take s.queue); loop () in loop () let get_available s = let rec loop () = match Lwt.state (peek s) with | Sleep | Return None -> [] | Return(Some x) -> ignore (Queue.take s.queue); x :: loop () | Fail exn -> raise exn in loop () let get_available_up_to n s = let rec loop = function | 0 -> [] | n -> match Lwt.state (peek s) with | Sleep | Return None -> [] | Return(Some x) -> ignore (Queue.take s.queue); x :: loop (n - 1) | Fail exn -> raise exn in loop n let is_empty s = peek s >|= fun x -> x = None let map f s = from (fun () -> get s >>= function | Some x -> let x = f x in return (Some x) | None -> return None) let map_s f s = from (fun () -> get s >>= function | Some x -> lwt x = f x in return (Some x) | None -> return None) let filter f s = let rec next () = get s >>= function | Some x as result -> let test = f x in if test then return result else next () | None -> return None in from next let filter_s f s = let rec next () = get s >>= function | Some x as result -> lwt test = f x in if test then return result else next () | None -> return None in from next let filter_map f s = let rec next () = get s >>= function | Some x -> let x = f x in (match x with | Some _ -> return x | None -> next ()) | None -> return None in from next let filter_map_s f s = let rec next () = get s >>= function | Some x -> lwt x = f x in (match x with | Some _ -> return x | None -> next ()) | None -> return None in from next let map_list f s = let pendings = ref [] in let rec next () = match !pendings with | [] -> get s >>= (function | Some x -> let l = f x in pendings := l; next () | None -> return None) | x :: l -> pendings := l; return (Some x) in from next let map_list_s f s = let pendings = ref [] in let rec next () = match !pendings with | [] -> get s >>= (function | Some x -> lwt l = f x in pendings := l; next () | None -> return None) | x :: l -> pendings := l; return (Some x) in from next let flatten s = map_list (fun l -> l) s let fold f s acc = let rec loop acc = get s >>= function | Some x -> let acc = f x acc in loop acc | None -> return acc in loop acc let fold_s f s acc = let rec loop acc = get s >>= function | Some x -> lwt acc = f x acc in loop acc | None -> return acc in loop acc let iter f s = let rec loop () = get s >>= function | Some x -> let () = f x in loop () | None -> return () in loop () let iter_s f s = let rec loop () = get s >>= function | Some x -> lwt () = f x in loop () | None -> return () in loop () let iter_p f s = let rec loop () = get s >>= function | Some x -> f x <&> loop () | None -> return () in loop () let find f s = let rec loop () = get s >>= function | Some x as result -> let test = f x in if test then return result else loop () | None -> return None in loop () let find_s f s = let rec loop () = get s >>= function | Some x as result -> lwt test = f x in if test then return result else loop () | None -> return None in loop () let rec find_map f s = let rec loop () = get s >>= function | Some x -> let x = f x in (match x with | Some _ -> return x | None -> loop ()) | None -> return None in loop () let rec find_map_s f s = let rec loop () = get s >>= function | Some x -> lwt x = f x in (match x with | Some _ -> return x | None -> loop ()) | None -> return None in loop () let rec combine s1 s2 = let next () = lwt n1 = get s1 and n2 = get s2 in match n1, n2 with | Some x1, Some x2 -> return (Some(x1, x2)) | _ -> return None in from next let append s1 s2 = let current_s = ref s1 and s1_finished = ref false in let rec next () = get !current_s >>= function | Some _ as result -> return result | None -> if !s1_finished then return None else begin s1_finished := true; current_s := s2; next () end in from next let concat s_top = let current_s = ref(from(fun () -> return None)) in let rec next () = get !current_s >>= function | Some _ as result -> return result | None -> get s_top >>= function | Some s -> current_s := s; next () | None -> return None in from next let choose streams = let source s = (s, peek s >|= fun x -> (s, x)) in let streams = ref (List.rev_map source streams) in let rec next () = match !streams with | [] -> return None | l -> lwt s, x = Lwt.choose (List.map snd l) in let l = List.remove_assq s l in match x with | Some _ -> lwt () = junk s in streams := source s :: l; return x | None -> next () in from next let parse s f = let s' = clone s in try_lwt f s with exn -> Queue.clear s.queue; Queue.transfer s'.queue s.queue; raise_lwt exn let hexdump stream = let buf = Buffer.create 80 and num = ref 0 in from begin fun _ -> nget 16 stream >>= function | [] -> return None | l -> Buffer.clear buf; Printf.bprintf buf "%08x| " !num; num := !num + 16; let rec bytes pos = function | [] -> blanks pos | x :: l -> if pos = 8 then Buffer.add_char buf ' '; Printf.bprintf buf "%02x " (Char.code x); bytes (pos + 1) l and blanks pos = if pos < 16 then begin if pos = 8 then Buffer.add_string buf " " else Buffer.add_string buf " "; blanks (pos + 1) end in bytes 0 l; Buffer.add_string buf " |"; List.iter (fun ch -> Buffer.add_char buf (if ch >= '\x20' && ch <= '\x7e' then ch else '.')) l; Buffer.add_char buf '|'; return (Some(Buffer.contents buf)) end