1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; indent-tabs-mode: nil -*-
3 ;;; --- Device buffers.
6 (in-package :io.zeta-streams
)
8 ;;;-----------------------------------------------------------------------------
9 ;;; Buffer Classes and Types
10 ;;;-----------------------------------------------------------------------------
13 ((synchronized :initarg
:synchronized
:reader synchronizedp
)
14 (input-iobuf :initarg
:input-buffer
:accessor input-iobuf-of
)
15 (output-iobuf :initarg
:output-buffer
:accessor output-iobuf-of
))
16 (:default-initargs
:synchronized nil
))
18 (defclass single-channel-buffer
(single-channel-device buffer
)
19 ((last-io-op :initform nil
:accessor last-io-op-of
)))
21 (defclass dual-channel-buffer
(dual-channel-device buffer
) ())
24 ;;;-----------------------------------------------------------------------------
25 ;;; Buffer Generic Functions
26 ;;;-----------------------------------------------------------------------------
28 (defgeneric buffer-clear-input
(buffer))
30 (defgeneric buffer-clear-output
(buffer))
32 (defgeneric buffer-fill-input
(buffer &optional timeout
))
34 (defgeneric buffer-flush-output
(buffer &optional timeout
))
36 ;;; Internal functions
38 (defgeneric buffer-read-octets
(buffer vector start end timeout
))
40 (defgeneric buffer-write-octets
(buffer vector start end timeout
))
42 (defgeneric %buffer-clear-input
(buffer))
44 (defgeneric %buffer-fill-input
(buffer timeout
))
46 (defgeneric %buffer-flush-output
(buffer timeout
))
49 ;;;-----------------------------------------------------------------------------
51 ;;;-----------------------------------------------------------------------------
53 (defmacro with-synchronized-buffer
((buffer &optional direction
) &body body
)
54 (with-gensyms (body-fun)
55 (labels ((make-locks (body direction
)
58 `(bt:with-lock-held
((iobuf-lock (input-iobuf-of ,buffer
)))
61 `(bt:with-lock-held
((iobuf-lock (output-iobuf-of ,buffer
)))
64 (make-locks (make-locks body
:output
) :input
)))))
65 `(flet ((,body-fun
() ,@body
))
66 (if (synchronizedp ,buffer
)
67 ,(make-locks `(,body-fun
) direction
)
71 ;;;-----------------------------------------------------------------------------
72 ;;; Buffer Constructors
73 ;;;-----------------------------------------------------------------------------
75 (defmethod initialize-instance :after
76 ((device single-channel-buffer
) &key buffer buffer-size
)
77 (with-accessors ((input-iobuf input-iobuf-of
)
78 (output-iobuf output-iobuf-of
))
80 (check-type buffer
(or null iobuf
))
81 (setf input-iobuf
(or buffer
(make-iobuf buffer-size
))
82 output-iobuf input-iobuf
)))
84 (defmethod initialize-instance :after
85 ((device dual-channel-buffer
) &key input-buffer output-buffer
86 input-buffer-size output-buffer-size
)
87 (with-accessors ((input-iobuf input-iobuf-of
)
88 (output-iobuf output-iobuf-of
))
90 (check-type input-buffer
(or null iobuf
))
91 (check-type output-buffer
(or null iobuf
))
92 (setf input-iobuf
(or input-buffer
(make-iobuf input-buffer-size
)))
93 (setf output-iobuf
(or output-buffer
(make-iobuf output-buffer-size
)))))
96 ;;;-----------------------------------------------------------------------------
97 ;;; Buffer DEVICE-CLOSE
98 ;;;-----------------------------------------------------------------------------
100 (defmethod device-close ((buffer single-channel-buffer
) &optional abort
)
101 (with-accessors ((handle input-handle-of
))
103 (with-synchronized-buffer (buffer :input
)
104 (unless (or abort
(eql :read
(last-io-op-of buffer
)))
105 (%buffer-flush-output buffer
0))
106 (device-close handle
)))
109 (defmethod device-close ((buffer buffer
) &optional abort
)
110 (with-accessors ((input-handle input-handle-of buffer
)
111 (output-handle output-handle-of buffer
))
113 (with-synchronized-buffer (buffer :both
)
115 (%buffer-flush-output buffer
0))
116 (device-close input-handle
)
117 (device-close output-handle
)))
121 ;;;-----------------------------------------------------------------------------
122 ;;; Buffer DEVICE-READ
123 ;;;-----------------------------------------------------------------------------
125 (defmethod device-read ((buffer single-channel-buffer
) vector start end
127 (with-synchronized-buffer (buffer :input
)
128 ;; If the previous operation was a write, try to flush the output buffer.
129 ;; If the buffer couldn't be flushed entirely, signal an error
130 (synchronize-input buffer
)
131 (buffer-read-octets buffer buffer start end timeout
)))
133 (defmethod device-read ((buffer dual-channel-buffer
) vector start end
135 (with-synchronized-buffer (buffer :input
)
136 (buffer-read-octets buffer buffer start end timeout
)))
138 (defmethod buffer-read-octets ((buffer buffer
) vector start end timeout
)
139 (with-accessors ((input-handle input-handle-of
)
140 (input-iobuf input-iobuf-of
)
141 (output-handle output-handle-of
)
142 (output-iobuf output-iobuf-of
))
145 ((iobuf-empty-p input-iobuf
)
147 (%buffer-fill-input buffer timeout
)))
148 (if (iobuf-empty-p input-iobuf
)
149 (if (eql :eof nbytes
) :eof
0)
150 (iobuf->vector input-iobuf vector start end
))))
152 (iobuf->vector input-iobuf vector start end
)))))
155 ;;;-----------------------------------------------------------------------------
156 ;;; Buffer DEVICE-WRITE
157 ;;;-----------------------------------------------------------------------------
159 (defmethod device-write ((buffer single-channel-buffer
) vector start end
161 (with-synchronized-buffer (buffer :output
)
162 ;; If the previous operation was a read, flush the read buffer
163 ;; and reposition the file offset accordingly
164 (%buffer-clear-input buffer
)
165 (buffer-write-octets buffer vector start end timeout
)))
167 (defmethod device-write ((buffer dual-channel-buffer
) vector start end
169 (with-synchronized-buffer (buffer :output
)
170 (buffer-write-octets buffer vector start end timeout
)))
172 (defmethod buffer-write-octets ((buffer buffer
) vector start end timeout
)
173 (with-accessors ((output-handle output-handle-of
)
174 (output-iobuf output-iobuf-of
))
177 (vector->iobuf output-iobuf vector start end
)
178 (setf (last-io-op-of buffer
) :write
)
179 (when (iobuf-full-p output-iobuf
)
180 (%buffer-flush-output buffer timeout
)))))
183 ;;;-----------------------------------------------------------------------------
184 ;;; Buffer DEVICE-POSITION
185 ;;;-----------------------------------------------------------------------------
187 (defmethod device-position ((buffer single-channel-buffer
))
188 (with-synchronized-buffer (buffer :input
)
189 (%buffer-position buffer
)))
191 (defun %buffer-position
(buffer)
192 (let ((position (device-position (input-handle-of buffer
))))
193 (ecase (last-io-op-of buffer
)
195 (- position
(iobuf-available-octets (input-iobuf-of buffer
))))
197 (+ position
(iobuf-available-octets (output-iobuf-of buffer
)))))))
199 (defmethod (setf device-position
) (position (buffer single-channel-buffer
) &key
(from :start
))
200 (setf (%buffer-position buffer from
) position
))
202 (defun (setf %buffer-position
) (position buffer from
)
203 (setf (device-position (input-handle-of buffer
) :from from
) position
))
206 ;;;-----------------------------------------------------------------------------
207 ;;; Buffer CLEAR-INPUT
208 ;;;-----------------------------------------------------------------------------
210 (defmethod buffer-clear-input ((buffer single-channel-buffer
))
211 (with-synchronized-buffer (buffer :input
)
212 (%buffer-clear-input buffer
)))
214 (defmethod %buffer-clear-input
((buffer single-channel-buffer
))
215 (when (eql :read
(last-io-op-of buffer
))
216 (let ((nbytes (iobuf-available-octets (input-iobuf-of buffer
))))
217 (unless (zerop nbytes
)
218 (setf (%buffer-position buffer
:current
) (- nbytes
)))
219 (iobuf-reset (input-iobuf-of buffer
)))))
221 (defmethod buffer-clear-input ((buffer buffer
))
222 (with-synchronized-buffer (buffer :input
)
223 (%buffer-clear-input buffer
)))
225 (defmethod %buffer-clear-input
((buffer dual-channel-buffer
))
226 (iobuf-reset (input-iobuf-of buffer
)))
229 ;;;-----------------------------------------------------------------------------
230 ;;; Buffer CLEAR-OUTPUT
231 ;;;-----------------------------------------------------------------------------
233 (defmethod buffer-clear-output ((buffer single-channel-buffer
))
234 (with-synchronized-buffer (buffer :output
)
235 (when (eql :write
(last-io-op-of buffer
))
236 (iobuf-reset (output-iobuf-of buffer
)))))
238 (defmethod buffer-clear-output ((buffer dual-channel-buffer
))
239 (with-synchronized-buffer (buffer :output
)
240 (iobuf-reset (output-iobuf-of buffer
))))
243 ;;;-----------------------------------------------------------------------------
244 ;;; Buffer FILL-INPUT
245 ;;;-----------------------------------------------------------------------------
247 (defmethod buffer-fill-input ((buffer single-channel-buffer
) &optional timeout
)
248 (with-synchronized-buffer (buffer :input
)
249 ;; If the previous operation was a write, try to flush the output buffer.
250 ;; If the buffer couldn't be flushed entirely, signal an error
251 (synchronize-input buffer
)
252 (%buffer-fill-input buffer timeout
)))
254 (defun synchronize-input (buffer)
255 (when (and (eql :write
(last-io-op-of buffer
))
256 (plusp (%buffer-flush-output buffer
0)))
257 ;; FIXME: What do we do now ???
258 (error "Could not flush the entire write buffer !"))
259 (iobuf-reset (output-iobuf-of buffer
)))
261 (defmethod buffer-fill-input ((buffer dual-channel-buffer
) &optional timeout
)
262 (with-synchronized-buffer (buffer :input
)
263 (%buffer-fill-input buffer timeout
)))
265 (defmethod %buffer-fill-input
((buffer buffer
) timeout
)
266 (with-accessors ((input-handle input-handle-of
)
267 (input-iobuf input-iobuf-of
))
269 (multiple-value-bind (data start end
)
270 (iobuf-next-empty-zone input-iobuf
)
272 (device-read input-handle data start end timeout
)))
273 (setf (iobuf-end input-iobuf
) (+ start nbytes
))
274 (setf (last-io-op-of buffer
) :read
)
278 ;;;-----------------------------------------------------------------------------
279 ;;; Buffer FLUSH-OUTPUT
280 ;;;-----------------------------------------------------------------------------
282 (defmethod buffer-flush-output ((buffer single-channel-buffer
) &optional timeout
)
283 (with-synchronized-buffer (buffer :output
)
284 (when (eql :write
(last-io-op-of buffer
))
285 (%buffer-flush-output buffer timeout
))))
287 (defmethod buffer-flush-output ((buffer dual-channel-buffer
) &optional timeout
)
288 (with-synchronized-buffer (buffer :output
)
289 (%buffer-flush-output buffer timeout
)))
291 (defmethod %buffer-flush-output
((buffer dual-channel-buffer
) timeout
)
292 (with-accessors ((output-handle output-handle-of
)
293 (output-iobuf output-iobuf-of
))
295 (multiple-value-bind (data start end
)
296 (iobuf-next-data-zone output-iobuf
)
298 (device-write output-handle data start end timeout
)))
299 (setf (iobuf-start output-iobuf
) (+ start nbytes
))
300 (setf (last-io-op-of buffer
) :write
)
301 (iobuf-available-octets output-iobuf
)))))