(* $Id: oOChannel.ml,v 1.7 2004/11/03 11:21:53 yori Exp $ *)
(* Copyright 2002, 2003 Yamagata Yoriyuki. distributed with LGPL *)

class type ['a] obj_input_channel =
  object
    method get : unit -> 'a
    method close_in : unit -> unit
  end

class type ['a] obj_output_channel =
  object
    method put : 'a -> unit
    method flush : unit -> unit
    method close_out : unit -> unit
  end

class ['a] channel_of_stream s  =
  object
    val s = s
    method get () :'a = 
      try Stream.next s with Stream.Failure -> raise End_of_file
    method close_in () = ()
  end

let stream_of_channel inchan =
  Stream.from (fun _ ->
    try Some (inchan#get()) with End_of_file -> None)

class type char_input_channel =
  object
    method input : string -> int -> int -> int
    method close_in : unit -> unit
  end

class type char_output_channel =
  object
    method output : string -> int -> int -> int
    method flush : unit -> unit
    method close_out : unit -> unit
  end

class char_input_channel_of (oc : char #obj_input_channel) =
  object (self)
    method close_in () = oc#close_in ()
    method input b p len =
      let i = ref p in
	(try while !i < p + len - 1 do
	  b.[!i] <- oc#get();
	  incr i;
	 done; () with End_of_file -> ());
	let len = !i - p in
	  if len <= 0 then raise End_of_file else len
  end

class char_obj_input_channel_of (ic : char_input_channel) =
  let b = String.make 1024 '\000' in
  let pos = ref 0 in
  let len = ref 0 in
  object (self)
    method get () =
      if !pos >= !len then begin
	len := ic#input b 0 1024;
	pos := 0;
	self#get ()
       end else
	 let c = b.[!pos] in
	   incr pos;
	   c
    method close_in () = ic#close_in ()
  end
    
class char_output_channel_of (oc : char #obj_output_channel) =
  object
    method flush = oc#flush
    method close_out = oc#close_out
    method output b p len =
      for i = p to p+len-1 do oc#put b.[i] done;
      len
  end

class char_obj_output_channel_of (out : char_output_channel) =
  let b = String.make 1024 '\000' in
  let pos = ref 0 in
  object
    method put c =
      b.[!pos] <- c;
      incr pos;
      if !pos >= 1024 then 
	let n = out#output b 0 1024 in
	  String.blit b n b 0 (1024 - n);
	  pos := 1024 - n
    method flush () =    
      let n = out#output b 0 !pos in
	if n < !pos then 
	  failwith 
	    "OOChannel.char_output_channel_of#flush: \
             Cannot flush the entire buffer";
    method close_out () = 
      out#flush ();
      out#close_out () 
  end

class of_in_channel p_in =
  object
    method close_in () = close_in p_in
    method input b p len = 
      let len = input p_in b p len in
      if len = 0 then raise End_of_file else len
  end

class of_out_channel p_out =
  object
    method close_out () = close_out p_out
    method output b p len = output p_out b p len; len
    method flush () = flush p_out
  end
