(* $Id$
 * ----------------------------------------------------------------------
 *
 *)


(** A netstream is an input channel that is read block by block. The 
 * fragment of the channel currently loaded into memory is called the
 * current window of the netstream. The window can be thought as
 * look-ahead buffer.
 *
 * {b Picture:}
 *
 * {[
 * 0                pos_in        pos_in + window_length            EOF
 * +------------------+-------------------+--------------------------+
 *                    ====================
 *                     The current window
 * ]}
 *
 * You can use a netstream like an [in_obj_channel], and read characters
 * and strings from the stream. The bytes come first from the look-ahead
 * buffer, and if there are not enough bytes, they are loaded from the
 * underlying channel. After every read operation it is tried to enlarge
 * the window such that it contains at least one block.
 *
 * If you want that the window becomes larger, you can call [want] (to
 * enlarge the window to a certain size) or [want_another_block] (to load
 * just another block from the underlying channel). Note that this affects only
 * the current window and not the future size of the window.
 *
 * Note [Buffer_underrun]: netstreams can cope with underruns of underlying
 * channels. An underrun happens when it is not possible to ensure the
 * minimum window size. However, it is possible that the window size
 * sinks under the minimum, but the [Buffer_underrun] is deferred until the
 * next call of an input method. Furthermore, there is a problem in the [skip]
 * method which may only be partially executed, i.e. the method skips some
 * bytes and then raises [Buffer_underrun]. 
 *)


(** An [in_obj_stream] extends [in_obj_channel] by look-ahead methods *)
class type in_obj_stream =
object
  inherit Netchannels.in_obj_channel
    (** The normal input operations work as usual. The window is moved after
     * every read sequence of bytes by exactly the number of bytes, and 
     * if the window length becomes smaller than the block size, it will
     * be ensured that the window will be enlarged to the block size (or
     * to the rest of the stream until EOF, whatever is smaller).
     *)

  method block_size : int
    (** The block size of the stream *)

  method window : Netbuffer.t
    (** The look-ahead window. The first byte of the window is the byte that
     * would be read next by [input_char]. The length of the window is returned
     * by the method [window_length]. This length may be smaller than the
     * current length of the netbuffer, i.e. the netbuffer may contain 
     * additional data that must be ignored.
     *)

  method want : int -> unit
    (** Increases the length of the window such that the length is at least
     * the passed number of bytes or that the window reaches EOF (whatever
     * happens first).
     *)

  method want_another_block : unit -> unit
    (** The same as: [want block_size] *)

  method window_length : int
    (** Returns the length of the window *)

  method window_at_eof : bool
    (** Whether the window is at eof *)

  method skip : int -> unit
    (** Skip the n bytes of the stream. It is not an error to skip more bytes
     * than available in the remaining stream.
     *)

end


class input_stream : 
        ?len:int -> 
	?block_size:int -> 
	Netchannels.in_obj_channel -> 
	  in_obj_stream
  (** Make an [in_obj_stream] on top of an [in_obj_channel]. The [block_size]
   * can be specified; it defaults to 4096. 
   *
   * If [len] is passed, this parameter limits the length of the channel:
   * Only the first [len] bytes are read from the input channel, then an EOF
   * is simulated even if the input channel is longer.
   *)


class sub_stream :
        ?len:int ->             (* default: no maximum length *)
	?delimiter:string ->    (* default: no delimiter *)
	in_obj_stream ->
	  in_obj_stream
  (** A sub stream is the part of the whole stream from the current position
   * to an arbitrary other position that is determined by [len] and
   * [delimiter]. [len] specifies the maximum length of the sub stream.
   * [delimiter] is an arbitrary string that indicates the end of the
   * sub stream (the delimiter is not part of the sub stream; i.e. the
   * sub stream ends immediately before the delimiter).
   *
   * While reading from the sub stream, not only the current position of
   * the sub stream moves, but also the current position of the main
   * stream. This means that it must be avoided to read data from the
   * main stream while the sub stream is in use. The typical pattern
   * is:
   * - Read from the main stream until the beginning of a section is
   *   recognized
   * - Create a sub stream at this point
   * - Read from the sub stream until EOF
   * - Continue reading the main stream. The next character of the main
   *   stream is exactly the character following the EOF of the sub stream
   *)

val print_in_obj_stream : Format.formatter -> in_obj_stream -> unit
  (** A top-loop printer for streams *)
