1 ;;;; -*- Mode: Lisp; indent-tabs-mode: nil -*-
3 ;;; --- Low-level I/O functions.
6 (in-package :iolib.streams
)
8 ;;;-------------------------------------------------------------------------
10 ;;;-------------------------------------------------------------------------
12 (declaim (inline %read-once
))
13 (defun %read-once
(fd read-fn iobuf
)
14 (declare (type function read-fn
)
18 (return-from %read-once
19 (funcall read-fn fd
(iobuf-end-pointer iobuf
)
20 (iobuf-end-space-length iobuf
)))
22 (iomux:wait-until-fd-ready fd
:input nil t
)))))
24 (declaim (inline %fill-ibuf
))
25 (defun %fill-ibuf
(iobuf fd read-fn
)
26 (declare (type iobuf iobuf
))
27 (let ((nbytes (%read-once fd read-fn iobuf
)))
30 (progn (incf (iobuf-end iobuf
) nbytes
) nbytes
))))
32 (declaim (inline %read-once
/no-hang
))
33 (defun %read-once
/no-hang
(fd read-fn iobuf
)
34 (declare (type function read-fn
)
37 (funcall read-fn fd
(iobuf-end-pointer iobuf
)
38 (iobuf-end-space-length iobuf
))
39 (isys:ewouldblock
() nil
)))
41 (declaim (inline %fill-ibuf
/no-hang
))
42 (defun %fill-ibuf
/no-hang
(iobuf fd read-fn
)
43 (declare (type iobuf iobuf
))
44 (let ((nbytes (%read-once
/no-hang fd read-fn iobuf
)))
49 (incf (iobuf-end iobuf
) nbytes
)
54 (defun %read-into-simple-array-ub8
(stream array start end
)
55 (declare (type dual-channel-gray-stream stream
))
56 (with-accessors ((fd fd-of
)
58 (iobuf input-buffer-of
))
60 (let ((octets-needed (- end start
)))
61 (loop :with array-offset
:= start
62 :for octets-in-buffer
:= (iobuf-length iobuf
)
63 :for nbytes
:= (min octets-needed octets-in-buffer
)
64 :when
(plusp nbytes
) :do
65 (iobuf-copy-into-lisp-array iobuf
(iobuf-start iobuf
)
66 array array-offset nbytes
)
67 (incf array-offset nbytes
)
68 (decf octets-needed nbytes
)
69 (incf (iobuf-start iobuf
) nbytes
)
70 :if
(zerop octets-needed
) :do
(loop-finish)
71 :else
:do
(iobuf-reset iobuf
)
72 :when
(eql :eof
(%fill-ibuf iobuf fd read-fn
))
74 :finally
(return array-offset
)))))
76 (defun %read-into-string
(stream string start end
)
77 (declare (type dual-channel-gray-stream stream
))
78 (loop :for offset
:from start
:below end
79 :for char
:= (stream-read-char stream
)
80 :if
(eql :eof char
) :do
(loop-finish)
81 :else
:do
(setf (char string offset
) char
)
82 :finally
(return offset
)))
84 (defun %read-into-vector
(stream vector start end
)
85 (declare (type dual-channel-gray-stream stream
))
86 (loop :for offset
:from start
:below end
87 :for octet
:= (stream-read-byte stream
)
88 :if
(eql :eof octet
) :do
(loop-finish)
89 :else
:do
(setf (aref vector offset
) octet
)
90 :finally
(return offset
)))
93 ;;;-------------------------------------------------------------------------
95 ;;;-------------------------------------------------------------------------
97 (defmacro with-hangup-guard
(stream &body body
)
98 (with-gensyms (bytes-written hangup-p
)
99 `(multiple-value-bind (,bytes-written
,hangup-p
)
101 (declare (ignore ,bytes-written
))
102 (when (eql :hangup
,hangup-p
)
103 (error 'hangup
:stream
,stream
)))))
105 (defun %write-octets-from-foreign-memory
(fd write-fn buf start end
106 &optional non-blocking
)
107 (declare (type stream-buffer buf
))
108 (let ((old-start start
))
109 (do () ((= start end
) (- start old-start
))
111 (incf start
(funcall write-fn fd
(inc-pointer buf start
) (- end start
)))
113 (return (values (- start old-start
) :hangup
)))
116 (return (- start old-start
))
117 (iomux:wait-until-fd-ready fd
:output nil t
)))))))
119 (defun %write-octets-from-iobuf
(write-fn fd iobuf
&optional non-blocking
)
120 (declare (type iobuf iobuf
))
121 (multiple-value-bind (bytes-written hangup-p
)
122 (%write-octets-from-foreign-memory
123 fd write-fn
(iobuf-data iobuf
) (iobuf-start iobuf
) (iobuf-end iobuf
)
125 (incf (iobuf-start iobuf
) bytes-written
)
126 (when (iobuf-empty-p iobuf
) (iobuf-reset iobuf
))
127 (values bytes-written hangup-p
)))
129 (defun %flush-obuf-if-needed
(stream)
130 (declare (type dual-channel-gray-stream stream
))
131 (with-accessors ((fd fd-of
)
132 (write-fn write-fn-of
)
133 (iobuf output-buffer-of
)
136 (when (or dirtyp
(iobuf-full-p iobuf
))
137 (multiple-value-bind (bytes-written hangup-p
)
138 (%write-octets-from-iobuf write-fn fd iobuf
)
140 (return* (values bytes-written hangup-p
))))
143 (defun %write-simple-array-ub8
(stream array start end
)
144 (declare (type dual-channel-gray-stream stream
))
145 (with-accessors ((fd fd-of
)
146 (write-fn write-fn-of
)
147 (iobuf output-buffer-of
))
149 (cond ((iobuf-can-fit-slice-p iobuf start end
)
150 (iobuf-append-slice iobuf array start end
))
152 (with-hangup-guard stream
153 (%write-octets-from-iobuf write-fn fd iobuf
))
154 (with-pointer-to-vector-data (ptr array
)
155 (with-hangup-guard stream
156 (%write-octets-from-foreign-memory fd write-fn ptr start end
)))))
159 (defun %write-vector-ub8
(stream vector start end
)
160 (declare (type dual-channel-gray-stream stream
))
161 (%write-simple-array-ub8 stream
(coerce vector
'ub8-sarray
) start end
))
163 (defun %write-vector
(stream vector start end
)
164 (declare (type dual-channel-gray-stream stream
))
165 (loop :for offset
:from start
:below end
166 :for octet
:= (aref vector offset
)
167 :do
(stream-write-byte stream octet
)
168 :finally
(return vector
)))
171 ;;;-------------------------------------------------------------------------
173 ;;;-------------------------------------------------------------------------
175 (defun stream-find-lf (iobuf fd read-fn
)
176 (declare (ignore fd read-fn
)
178 (debug-only (assert (plusp (iobuf-length iobuf
))))
180 ((= (iobuf-peek iobuf
) #.
(char-code #\Linefeed
))
181 (incf (iobuf-start iobuf
))
185 (setf (fdefinition 'stream-find-lf
/no-hang
) #'stream-find-lf
)
187 (defun stream-find-cr (iobuf fd read-fn
)
188 (declare (ignore fd read-fn
)
190 (debug-only (assert (plusp (iobuf-length iobuf
))))
192 ((= (iobuf-peek iobuf
) #.
(char-code #\Return
))
193 (incf (iobuf-start iobuf
))
197 (setf (fdefinition 'stream-find-cr
/no-hang
) #'stream-find-cr
)
199 (defun stream-find-crlf (iobuf fd read-fn
)
200 (declare (type iobuf iobuf
))
201 (debug-only (assert (plusp (iobuf-length iobuf
))))
203 ((/= (iobuf-peek iobuf
) #.
(char-code #\Return
))
205 ((and (= 1 (iobuf-length iobuf
))
206 (eql :eof
(%fill-ibuf iobuf fd read-fn
)))
208 ((= (iobuf-peek iobuf
1) #.
(char-code #\Linefeed
))
209 (incf (iobuf-start iobuf
) 2)
213 (defun stream-find-crlf/no-hang
(iobuf fd read-fn
)
214 (declare (type iobuf iobuf
))
215 (debug-only (assert (plusp (iobuf-length iobuf
))))
217 ((/= (iobuf-peek iobuf
) #.
(char-code #\Return
))
219 ((= 1 (iobuf-length iobuf
))
220 (if (eql :eof
(%fill-ibuf
/no-hang iobuf fd read-fn
))
223 ((= (iobuf-peek iobuf
1) #.
(char-code #\Linefeed
))
224 (incf (iobuf-start iobuf
) 2)
228 (defun maybe-rewind-iobuf (iobuf encoding
)
229 (let ((max-octets-per-char
230 (babel-encodings:enc-max-units-per-char encoding
)))
231 ;; Some encodings such as CESU or Java's modified UTF-8 take
232 ;; as much as 6 bytes per character. Make sure we have enough
233 ;; space to collect read-ahead bytes if required.
234 (when (< (- (iobuf-size iobuf
)
237 (iobuf-copy-data-to-start iobuf
))))
239 (defun decode-one-char (fd read-fn iobuf encoding
)
240 (debug-only (assert (plusp (iobuf-length iobuf
))))
243 (multiple-value-bind (str ret
)
244 (foreign-string-to-lisp (iobuf-data iobuf
)
245 :offset
(iobuf-start iobuf
)
246 :count
(iobuf-length iobuf
)
249 (incf (iobuf-start iobuf
) ret
)
250 (return* (char str
0)))
251 (babel:end-of-input-in-character
()
252 (let ((nbytes (%fill-ibuf iobuf fd read-fn
)))
253 ;; Even if the buffer contains octets representing an
254 ;; incomplete character, we return EOF because the Gray
255 ;; streams API doesn't distinguish between "no data" and
257 (when (eql :eof nbytes
)
260 (defun decode-one-char/no-hang
(iobuf encoding
)
261 (debug-only (assert (plusp (iobuf-length iobuf
))))
263 (multiple-value-bind (string ret
)
264 (foreign-string-to-lisp (iobuf-data iobuf
)
265 :offset
(iobuf-start iobuf
)
266 :count
(iobuf-length iobuf
)
269 (incf (iobuf-start iobuf
) ret
)
271 (babel:end-of-input-in-character
() nil
)))
274 ;;;-------------------------------------------------------------------------
276 ;;;-------------------------------------------------------------------------
278 (defun stream-write-lf (stream)
279 (declare (type dual-channel-gray-stream stream
))
280 (let ((octets #.
(map 'ub8-sarray
#'char-code
'(#\Linefeed
))))
281 (%write-simple-array-ub8 stream octets
0 1)))
283 (defun stream-write-crlf (stream)
284 (declare (type dual-channel-gray-stream stream
))
285 (let ((octets #.
(map 'ub8-sarray
#'char-code
'(#\Return
#\Linefeed
))))
286 (%write-simple-array-ub8 stream octets
0 2)))
288 (defun stream-write-cr (stream)
289 (declare (type dual-channel-gray-stream stream
))
290 (let ((octets #.
(map 'ub8-sarray
#'char-code
'(#\Return
))))
291 (%write-simple-array-ub8 stream octets
0 1)))
293 (declaim (inline %write-string-chunk
))
294 (defun %write-string-chunk
(stream string start end encoding
)
296 (or (position #\Newline string
:start start
:end end
) end
))
298 (babel:string-to-octets string
299 :start start
:end chunk-end
300 :encoding encoding
)))
301 (%write-simple-array-ub8 stream octets
0 (length octets
))