buffer-input-port added by DerGuteMoritz on Wed Apr 12 11:06:51 2017
(define (buffer-input-port in cap)
(let* ((fd (condition-case (port->fileno in)
(exn (exn type) #f)))
(buf (make-string cap))
(pos #f)
(end 0)
(fill-buffer! (lambda ()
(when (or (not pos)
(and (> end 0)
(= pos end)))
(if fd
(begin
(thread-wait-for-i/o! fd #:input)
(let ((x (file-read fd cap buf)))
(set! end (cadr x))
(set! pos 0)))
(begin
(peek-char in)
(let* ((str (read-buffered in))
(len (string-length str)))
;; Some ports (e.g. those of the
;; `unix-sockets` egg) don't
;; implement `read-buffered` and
;; will always return an empty
;; string here even after
;; `peek-char` returned a
;; char. That's why we can just
;; replace `buf` with `str` in the
;; else branch, assuming that
;; `read-buffered` will
;; consistently behave like
;; that. In case `peek-char`
;; returned #!eof, `read-string!`
;; will return 0 and thus make this
;; port produce #!eof, too.
(if (zero? len)
(begin
(set! end (read-string! 1 buf in))
(set! pos 0))
(begin
(set! buf str)
(set! end len)
(set! pos 0)))))))))
(read-buffer! (lambda (max)
(if (and (zero? pos) (<= end max))
(begin
(set! pos end)
buf)
(let ((old-pos pos))
(set! pos (min end (+ pos max)))
(substring buf old-pos pos)))))
(%read-char (lambda ()
(fill-buffer!)
(if (zero? end)
#!eof
(let ((char (string-ref buf pos)))
(set! pos (+ pos 1))
char))))
(%char-ready? (lambda ()
(and pos
(or (< pos end)
(and (char-ready? in)
(begin
(fill-buffer!)
(< pos end)))))))
(%close (lambda ()
(close-input-port in)
(set! pos 0)
(set! end 0)
(set! buf #f)))
(%peek-char (lambda ()
(fill-buffer!)
(if (zero? end)
#!eof
(string-ref buf pos))))
(%read-string! (lambda (port num out offset)
(let loop ((rem num)
(offset offset))
(if (zero? rem)
num
(begin
(fill-buffer!)
(if (zero? end)
(- num rem)
(let* ((str (read-buffer! rem))
(len (string-length str)))
(string-copy! out offset str)
(loop (- rem len)
(+ offset len)))))))))
(%read-line (lambda (port max)
(let loop ((result '())
(rem max))
(fill-buffer!)
(if (or (zero? end) (and rem (zero? rem)))
(reverse-string-append result)
(let* ((idx (substring-index "\n" buf pos))
(new-pos (or idx end))
(new-pos (if rem
(min (+ pos rem) new-pos)
new-pos))
(old-pos pos)
(new-rem (if idx
0
(and rem (- rem (- new-pos old-pos))))))
(set! pos (if idx
(+ 1 new-pos)
new-pos))
(loop (cons (substring buf old-pos new-pos) result)
new-rem))))))
(%read-buffered (lambda (port)
(cond ((not pos) "")
((= pos end) "")
(else
(let ((old-pos pos))
(set! pos end)
(substring buf old-pos end)))))))
(make-input-port %read-char
%char-ready?
%close
%peek-char
%read-string!
%read-line
%read-buffered)))