Coverage report: /home/ellis/comp/core/std/stream.lisp
Kind | Covered | All | % |
expression | 7 | 212 | 3.3 |
branch | 0 | 26 | 0.0 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
1
;;; std/stream.lisp --- Standard Streams
6
(in-package :std/stream)
7
(declaim (optimize speed))
9
(definline read-until-end (stream)
10
"Read input from STREAM until EOF and return a string."
11
(with-output-to-string (s)
12
(loop for c = (read-char stream nil)
14
do (write-char c s))))
16
(definline read-lisp-until-end (stream)
17
"Read input from STREAM until EOF and return a form."
19
(loop for c = (read stream nil eof)
23
(definline copy-stream (input output &key (element-type (stream-element-type input))
25
(buffer (make-array buffer-size :element-type element-type))
28
"Reads data from INPUT and writes it to OUTPUT. Both INPUT and OUTPUT must
29
be streams, they will be passed to READ-SEQUENCE and WRITE-SEQUENCE and must have
30
compatible element-types."
31
(check-type start non-negative-integer)
32
(check-type end (or null non-negative-integer))
33
(check-type buffer-size positive-integer)
36
(error "END is smaller than START in ~S" 'copy-stream))
37
(let ((output-position 0)
40
;; FIXME add platform specific optimization to skip seekable streams
41
(loop while (< input-position start)
42
do (let ((n (read-sequence buffer input
43
:end (min (length buffer)
44
(- start input-position)))))
46
(error "~@<Could not read enough bytes from the input to fulfill ~
47
the :START ~S requirement in ~S.~:@>" 'copy-stream start))
48
(incf input-position n))))
49
(assert (= input-position start))
50
(loop while (or (null end) (< input-position end))
51
do (let ((n (read-sequence buffer input
54
(- end input-position))))))
57
(error "~@<Could not read enough bytes from the input to fulfill ~
58
the :END ~S requirement in ~S.~:@>" 'copy-stream end)
60
(incf input-position n)
61
(write-sequence buffer output :end n)
62
(incf output-position n)))
64
(finish-output output))
69
(defclass wrapped-stream (fundamental-stream)
70
((stream :initarg :stream :reader stream-of))
71
(:documentation "A stream which wraps another stream accessible via STREAM-OF."))
73
(defmethod open-stream-p ((stream wrapped-stream))
74
(open-stream-p (stream-of stream)))
76
(defmethod stream-element-type ((stream wrapped-stream))
77
(stream-element-type (stream-of stream)))
79
(defmethod close ((stream wrapped-stream) &key abort)
80
(close (stream-of stream) :abort abort))
82
(defun wrapped-stream-p (obj) (typep obj 'wrapped-stream))
84
(defclass wrapped-character-input-stream (wrapped-stream fundamental-character-input-stream)
86
(:documentation "A wrapped CHARACTER-INPUT-STREAM."))
88
(defmethod stream-read-char ((stream wrapped-character-input-stream))
89
(read-char (stream-of stream) nil :eof))
91
(defmethod stream-unread-char ((stream wrapped-character-input-stream)
93
(unread-char char (stream-of stream)))
96
(with-input-from-string (input "1 2
98
(let ((counted-stream (make-instance 'counting-character-input-stream
100
(loop for thing = (read counted-stream) while thing
101
unless (numberp thing) do
102
(error "Non-number ~S (line ~D, column ~D)" thing
103
(line-count-of counted-stream)
104
(- (col-count-of counted-stream)
105
(length (format nil "~S" thing))))
111
Non-number :FOO (line 2, column 5)
112
[Condition of type SIMPLE-ERROR]
114
(defclass counting-character-input-stream (wrapped-character-input-stream)
115
((char-count :initform 1 :accessor char-count-of)
116
(line-count :initform 1 :accessor line-count-of)
117
(col-count :initform 1 :accessor col-count-of)
118
(prev-col-count :initform 1 :accessor prev-col-count-of))
119
(:documentation "A CHARACTER-INPUT-STREAM with automatic counters:
121
- CHAR-COUNT via CHAR-COUNT-OF
122
- LINE-COUNT via LINE-COUNT-OF
123
- COL-COUNT via COL-COUNT-OF
124
- PREV-COL-COUNT via PREV-COL-COUNT-OF"))
126
(defmethod stream-read-char ((stream counting-character-input-stream))
127
(with-accessors ((inner-stream stream-of) (chars char-count-of)
128
(lines line-count-of) (cols col-count-of)
129
(prev prev-col-count-of)) stream
130
(let ((char (call-next-method)))
131
(cond ((eql char :eof)
133
((char= char #\Newline)
144
(defmethod stream-unread-char ((stream counting-character-input-stream)
146
(with-accessors ((inner-stream stream-of) (chars char-count-of)
147
(lines line-count-of) (cols col-count-of)
148
(prev prev-col-count-of)) stream
149
(cond ((char= char #\Newline)
159
(defclass wrapped-character-output-stream (wrapped-stream fundamental-character-output-stream)
160
((col-index :initform 0 :accessor col-index-of))
161
(:documentation "A wrapped CHARACTER-OUTPUT-STREAM with the current column index accessible via
164
(defmethod stream-line-column ((stream wrapped-character-output-stream))
165
(col-index-of stream))
167
(defmethod stream-write-char ((stream wrapped-character-output-stream)
169
(with-accessors ((inner-stream stream-of) (cols col-index-of)) stream
170
(write-char char inner-stream)
171
(if (char= char #\Newline)
176
(flet ((format-timestamp (stream)
177
(apply #'format stream "[~2@*~2,' D:~1@*~2,'0D:~0@*~2,'0D] "
178
(multiple-value-list (get-decoded-time)))))
179
(let ((output (make-instance 'prefixed-character-output-stream
180
:stream *standard-output*
181
:prefix #'format-timestamp)))
182
(loop for string in '("abc" "def" "ghi") do
183
(write-line string output)
190
(defclass prefixed-character-output-stream
191
(wrapped-character-output-stream)
192
((prefix :initarg :prefix :reader prefix-of))
193
(:documentation "A CHARACTER-OUTPUT-STREAM which automatically writes each line of output with
194
a designated prefix accessible via PREFIX-OF."))
196
(defgeneric write-prefix (prefix stream)
197
(:method ((prefix string) stream) (write-string prefix stream))
198
(:method ((prefix function) stream) (funcall prefix stream))
199
(:documentation "Write a PREFIX to STREAM."))
201
(defmethod stream-write-char ((stream prefixed-character-output-stream)
203
(with-accessors ((inner-stream stream-of) (cols col-index-of)
204
(prefix prefix-of)) stream
206
(write-prefix prefix inner-stream))
210
(defmacro with-input-from-file ((stream-name file-name &rest args
211
&key (direction nil direction-p)
214
"Evaluate BODY with STREAM-NAME to an input stream on the file
216
(declare (ignore direction))
218
(error "Can't specify :DIRECTION for WITH-INPUT-FROM-FILE."))
219
`(with-open-file (,stream-name ,file-name :direction :input ,@args)
222
(defmacro with-output-to-file ((stream-name file-name &rest args
223
&key (direction nil direction-p)
226
"Evaluate BODY with STREAM-NAME to an output stream on the file
228
(declare (ignore direction))
230
(error "Can't specify :DIRECTION for WITH-OUTPUT-TO-FILE."))
231
`(with-open-file (,stream-name ,file-name :direction :output ,@args)