spotlib / lib / channel.ml

open Base

module Buffer = struct
  type t = { strings : string Dllist.t;
             pos : int }

  (* CR jfuruse: slow *)
  let length t = 
    Dllist.fold_left ~f:(fun sum node -> 
      sum + String.length (Dllist.value node)) ~init:0 t.strings - t.pos

  let contents t = 
    match Dllist.to_list t.strings with
    | [] -> ""
    | x::xs -> String.concat "" & Xstring.drop t.pos x :: xs

  let jump_to t pos =
    let pos = t.pos + pos in
    Dllist.scan_left ~f:(fun acc node ->
      match acc with
      | `Yet pos ->
          let len = String.length & Dllist.value node in
          if len > pos then `Stop (`Found (node, pos))
          else `Continue (`Yet (len - pos))
      | _ -> assert false)
      ~init:(`Yet pos) t.strings

  let index ?(from=0) p t =
    let node, pos = 
      match jump_to t from with
      | `Yet _ -> invalid_arg "Channel.Buffer.index"
      | `Found (node, pos) -> node, pos
    in
    assert false
(*
    match Dllist.hd_tl t.strings with
    | None -> None
    | Some (hd, _tlopt) ->
        match 
          Xstring.scani_left (fun pos _ c ->
            if p pos c then `Stop (Some pos)
            else `Continue None) None ~from:t.pos & Dllist.value hd
        with
        | Some p -> Some p
        | None ->
            assert false
*)
(*
  let index p t =
    match Dllist.hd_tl with
    | None -> None
    | Some (hd, tlopt) ->
        
        Xstring.index_from_to hd t.pos (String.length hd - 1)

    match Dllist.to_list t.strings with
    | [] -> ""
    | x::xs -> String.concat "" & Xstring.drop t.pos x :: xs
*)

end
    
(*

module String = struct
  include String
  include Xstring
end

class type t = object
  method close : unit
  method read : string -> int -> int -> int
  method read_line : string option
  method read_all : string
  method read_all_lines : string list
end

class in_channel ic : t = object (self)

  val mutable closed = false

  method close = 
    if not closed then begin
      close_in ic;
      closed <- true
    end

  method read = input ic

  method read_line = try Some (input_line ic) with End_of_file -> None

  method read_all_lines =
    let rec loop rev_lines = 
      match catch ~f:input_line ic with 
      | `Ok l -> loop (l :: rev_lines)
      | `Error End_of_file -> List.rev rev_lines
      | `Error e -> raise e
    in
    let res = loop [] in
    self#close; 
    res

  method read_all =
    let buf = String.create 1024 in
    let final = Buffer.create 1024 in
    let rec loop () =
      let read = input ic buf 0 1024 in
      if read = 0 then ()
      else begin
        Buffer.add_substring final buf 0 read;
        loop ()
      end
    in
    loop ();
    let res = Buffer.contents final in
    self#close;
    res
    
  initializer
    Gc.finalise (fun _ -> 
      prerr_endline "auto closing!";
      close_in ic) self
end

let in_channel ic = new in_channel ic
  
class of_function ?(close=(fun () -> ())) read : t = object (self)
    
  val mutable closed = false

  val buffer = Buffer.create 1024

  method close = close (); closed <- true

  method read buf pos len = 
    let buflen = Buffer.length buffer in
    if buflen >= len then begin
      let s = Buffer.contents buffer in
      String.blit s 0 buf pos len;
      Buffer.reset buffer;
      Buffer.add_string buffer (String.drop (buflen - len) s);
      len
    end else begin
      if buflen > 0 then begin
        let s = Buffer.contents buffer in
        Buffer.reset buffer;
        String.blit s 0 buf pos buflen;
      end;
      read buf (pos+buflen) (len-buflen)
    end

  method read_line = 
    let s = Buffer.contents buffer in
    match String.index_opt s '\n' with
    | Some pos ->
        Buffer.reset buffer;
        Buffer.add_string buffer & String.drop pos s;
        Some (String.take (pos-1) s)
    | None ->
        let s = String.create 1024 in
        let rec loop () = 
          let read = read s 0 1024 in
          if read = 0 then None
          else 
            match String.index_from_to s 0 (read-1) '\n' with
            | None -> Buffer.add_string buffer s; loop ()
            | Some pos ->
                Buffer.add_substring buffer s 0 (pos-1);
                let line = Buffer.contents buffer in
                Buffer.reset buffer;
                Buffer.add_substring buffer s (pos+1) (read - pos - 1);
                Some line
        in
        loop ()

  method read_all_lines = 
    let rec loop rev_list = 
      match self#read_line with
      | None -> List.rev rev_list
      | Some l -> loop (l::rev_list)
    in
    let res = loop [] in
    self#close;
    res

  method read_all = 
    let s = String.create 1024 in
    let rec loop () = 
      let chars = read s 0 1024 in
      if chars = 0 then begin
        let res = Buffer.contents buffer in
        Buffer.reset buffer;
        res
      end else begin
        Buffer.add_substring buffer s 0 chars;
        loop ()
      end
    in
    let res = loop () in
    self#close;
    res
    
  initializer
    Gc.finalise (fun _ -> close ()) (self :> t)
end

let of_function ?close read = new of_function ?close read

let string src =
  let srcpos = ref 0 in
  let srclen = String.length src in
  of_function (fun s pos len ->
    if !srcpos >= srclen then 0 (* [src] leaks? *)
    else begin
      let read = min (srclen - !srcpos) len in
      String.blit src !srcpos s pos read;
      srcpos := !srcpos + read;
      read
    end)

*)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.