1 ;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp -*-
3 ;; Copyright (C) 2007 Stelian Ionescu
5 ;; This code is free software; you can redistribute it and/or
6 ;; modify it under the terms of the version 2.1 of
7 ;; the GNU Lesser General Public License as published by
8 ;; the Free Software Foundation, as clarified by the
9 ;; preamble found here:
10 ;; http://opensource.franz.com/preamble.html
12 ;; This program is distributed in the hope that it will be useful,
13 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ;; GNU General Public License for more details.
17 ;; You should have received a copy of the GNU Lesser General
18 ;; Public License along with this library; if not, write to the
19 ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 ;; Boston, MA 02110-1301, USA
22 (in-package :net.sockets
)
24 (iolib-utils:define-constant
+max-octets-per-char
+ 6)
26 ;; TODO: use the buffer pool
27 ;; TODO: handle instance reinitialization
28 (defmethod shared-initialize :after
((s dual-channel-gray-stream
) slot-names
29 &key
(input-buffer-size +bytes-per-iobuf
+)
30 (output-buffer-size +bytes-per-iobuf
+)
31 (external-format :default
))
32 (declare (ignore slot-names
))
33 (check-type input-buffer-size buffer-index
)
34 (check-type output-buffer-size buffer-index
)
35 (when (open-stream-p s
) (close s
))
36 (with-slots ((ib input-buffer
) (ob output-buffer
)
37 (ef external-format
)) s
38 (setf ib
(allocate-iobuf input-buffer-size
)
39 ob
(allocate-iobuf output-buffer-size
))
40 (setf ef
(etypecase external-format
41 (symbol (find-external-format external-format
))
42 ((and list
(not null
))
43 (apply #'make-external-format external-format
))))))
51 (defmethod stream-element-type ((stream active-socket
))
55 ;; TODO: use the buffer pool
56 (defmethod close :around
((stream active-socket
) &key abort
)
57 (declare (ignore abort
))
58 (with-slots ((ib input-buffer
)
59 (ob output-buffer
)) stream
60 (when ib
(free-iobuf ib
))
61 (when ob
(free-iobuf ob
))
66 (defmethod close ((stream dual-channel-gray-stream
) &key abort
)
67 (declare (ignore stream abort
)))
75 (defmethod stream-clear-input ((stream active-socket
))
76 (with-slots ((ib input-buffer
)) stream
80 ;; (defmethod stream-read-sequence ((stream active-socket) seq
81 ;; &optional start end)
90 (defmethod stream-clear-output ((stream active-socket
))
91 (with-slots ((ob output-buffer
)) stream
95 (defmethod stream-finish-output ((stream active-socket
))
96 (with-slots ((ob output-buffer
) fd
) stream
100 (defmethod stream-force-output ((stream active-socket
))
101 ;; FIXME: add non-blocking version of this?
102 ;; and/or re-write the flush code in a non-blocking variant,
103 ;; and have the finish-output synchronize on the result.
104 (stream-finish-output stream
))
106 ;; (defmethod stream-read-sequence ((stream active-socket) seq
107 ;; &optional start end)
110 ;;;;;;;;;;;;;;;;;;;;;
112 ;; Character Input ;;
114 ;;;;;;;;;;;;;;;;;;;;;
116 (defun fill-ibuf (buf fd
&optional timeout
)
119 (iomux:wait-until-fd-ready fd
:read timeout
)))
120 (unless (member :read status
)
121 ;; FIXME signal something better
122 (return-from fill-ibuf
:timeout
))))
123 (let ((num (et:read fd
(cffi:inc-pointer
(iobuf-data buf
)
129 (incf (iobuf-end buf
) num
))))
131 (defun maybe-find-line-ending (fd ib ef
)
132 (let* ((start-off (iobuf-start ib
))
133 (char-code (bref ib start-off
)))
135 (ecase (ioenc:ef-line-terminator ef
)
136 (:unix
(when (= char-code
(char-code #\Linefeed
))
137 (incf (iobuf-start ib
))
138 (return (values #\Newline
1))))
139 (:mac
(when (= char-code
(char-code #\Return
))
140 (incf (iobuf-start ib
))
141 (return (values #\Newline
1))))
142 (:dos
(when (= char-code
(char-code #\Return
))
143 (when (and (= (iobuf-length ib
) 1)
144 (eq (fill-ibuf ib fd
) :eof
))
145 (incf (iobuf-start ib
))
146 (return (values #\Return
1)))
147 (when (= (bref ib
(1+ start-off
))
148 (char-code #\Linefeed
))
149 (incf (iobuf-start ib
) 2)
150 (return (values #\Newline
2)))))))))
152 (defmethod stream-read-char ((stream active-socket
))
153 (with-slots ((fd fd
) (ib input-buffer
)
154 (unread-index ibuf-unread-index
)
156 (ef external-format
)) stream
157 (setf unread-index
(iobuf-start ib
))
158 (let ((str (make-string 1))
160 (flet ((fill-buf-or-eof ()
161 ;; FIXME - what if we can't refill, in the middle of a wide-char??
162 (setf ret
(fill-ibuf ib fd
))
164 (return-from stream-read-char
:eof
))))
165 (cond ((zerop (iobuf-length ib
))
168 ;; Some encodings such as CESU or Java's modified UTF-8 take
169 ;; as much as 6 bytes per character. Make sure we have enough
170 ;; space to collect read-ahead bytes if required.
171 ((< 0 (iobuf-end-space-length ib
) +max-octets-per-char
+)
172 (iobuf-copy-data-to-start ib
)
173 (setf unread-index
0)))
175 (multiple-value-bind (line-end bytes-consumed
)
176 (maybe-find-line-ending fd ib ef
)
178 (incf pos bytes-consumed
)
179 (return-from stream-read-char line-end
)))
182 (setf ret
(nth-value 1 (ioenc::%octets-to-string
185 (iobuf-end ib
) ef
1)))
186 (end-of-input-in-character (err)
187 (declare (ignore err
))
191 (incf (iobuf-start ib
) ret
))
194 (defun maybe-find-line-ending-no-hang (fd ib ef
)
195 (declare (ignore fd
))
196 (let* ((start-off (iobuf-start ib
))
197 (char-code (bref ib start-off
)))
199 (ecase (ioenc:ef-line-terminator ef
)
200 (:unix
(when (= char-code
(char-code #\Linefeed
))
201 (incf (iobuf-start ib
))
202 (return (values #\Newline
1))))
203 (:mac
(when (= char-code
(char-code #\Return
))
204 (incf (iobuf-start ib
))
205 (return (values #\Newline
1))))
206 (:dos
(when (= char-code
(char-code #\Return
))
207 (when (= (iobuf-length ib
) 1)
208 (incf (iobuf-start ib
))
209 (return :starvation
))
210 (when (= (bref ib
(1+ start-off
))
211 (char-code #\Linefeed
))
212 (incf (iobuf-start ib
) 2)
213 (return (values #\Newline
2)))))))))
215 (defmethod stream-read-char-no-hang ((stream active-socket
))
216 (with-slots ((fd fd
) (ib input-buffer
)
218 (ef external-format
)) stream
219 (let ((str (make-string 1))
223 (when (< 0 (iobuf-end-space-length ib
) 4)
224 (iobuf-copy-data-to-start ib
))
225 (when (and (iomux:fd-ready-p fd
:read
)
226 (eql :eof
(fill-ibuf ib fd
)))
228 (when (zerop (iobuf-length ib
))
229 (return (if eof
:eof nil
)))
231 (multiple-value-bind (line-end bytes-consumed
)
232 (maybe-find-line-ending-no-hang fd ib ef
)
233 (cond ((eql line-end
:starvation
)
239 ((characterp line-end
)
240 (incf pos bytes-consumed
)
244 (setf ret
(nth-value 1 (ioenc::%octets-to-string
247 (iobuf-end ib
) ef
1)))
248 (end-of-input-in-character (err)
249 (declare (ignore err
))
252 (incf (iobuf-start ib
) ret
)
255 (defun %stream-unread-char
(stream)
256 ;; unreading anything but the latest character is wrong,
257 ;; but checking is not mandated by the standard
260 (%stream-unread-char stream
)
261 (unless (ignore-errors (eql (stream-read-char stream
) character
))
262 (error "Trying to unread wrong character ~S" character
)))
263 (declare (type active-socket stream
))
264 (with-slots ((ib input-buffer
) (unread-index ibuf-unread-index
)) stream
265 (symbol-macrolet ((start (iobuf-start ib
)))
267 ((> start unread-index
)
268 (setf start unread-index
))
270 (error "No uncommitted character to unread")))))
273 (defmethod stream-unread-char ((stream active-socket
) character
)
274 (declare (ignore character
))
275 (%stream-unread-char stream
))
277 (defmethod stream-peek-char ((stream active-socket
))
278 (let ((char (stream-read-char stream
)))
279 (cond ((eq char
:eof
) :eof
)
280 (t (%stream-unread-char stream
)
283 ;; (defmethod stream-read-line ((stream active-socket))
284 ;; (with-slots ((fd fd) (ib input-buffer)
286 ;; (ef external-format)) stream
287 ;; (let ((str (make-string 80)) (strsz 80) (strlen 0)
288 ;; (chars-out 0) (bytes-in 0)
292 (defmethod stream-listen ((stream active-socket
))
293 (characterp (stream-read-char-no-hang stream
)))
295 ;;;;;;;;;;;;;;;;;;;;;;
297 ;; Character Output ;;
299 ;;;;;;;;;;;;;;;;;;;;;;
301 (defun buffer-string-to-octets (string buffer start end ef fd
&optional max-char-num
)
302 (declare (string string
)
304 (type buffer-index start
)
305 (type buffer-index end
)
307 (optimize (speed 3) (space 0) (safety 0) (debug 0)))
308 (unless max-char-num
(setf max-char-num -
1))
309 (let ((ptr start
) oldptr
314 (prog1 (char string ptr
) (incf ptr
)))
316 (setf (bref buffer
(incf pos
)) octet
))
319 (error symbol
:string string
320 :start start
:end end
322 :external-format
(ef-name ef
))
324 :report
"Supply a replacement character."
325 :interactive ioenc
::read-replacement-char
327 (use-standard-unicode-replacement ()
328 :report
"Use standard UCS replacement character"
329 (code-char ioenc
::+replacement-char
+))
331 :report
"Stop decoding and return to last good offset."
335 (return-from buffer-string-to-octets
(1+ pos
))))
336 (loop :while
(and (< ptr end
)
337 (/= (incf char-count
) max-char-num
))
338 :do
(setf oldpos pos oldptr ptr
)
339 (ioenc::char-to-octets ef
#'input
#'output
#'error-fn
(- end ptr
)))
342 (defun flush-obuf (buf fd
&optional timeout
)
343 ;; FIXME - ought to loop partial writes until actual timeout,
344 ;; interleaving write
345 ;; computing the initial deadline, and retrying until it's passed
346 (flet ((write-once ()
347 (let* ((num (et:write
349 (cffi:inc-pointer
(iobuf-data buf
)
351 (iobuf-length buf
))))
354 (progn (incf (iobuf-start buf
) num
) t
))))
356 (when (iobuf-empty-p buf
)
362 (loop :with deadline
:= (+ (iomux::gettime
) timeout
)
363 :for status
:= (iomux:wait-until-fd-ready fd
:write timeout
) :do
364 (unless (member :write status
)
365 ;; FIXME signal something better -- maybe analyze the status
366 (return (values nil
:timeout
)))
368 (return (values nil
:fail
)))
370 (return (values t nil
)))
371 (setf timeout
(- deadline
(iomux::gettime
)))
372 (unless (> timeout
0)
373 (return (values nil
:timeout
))))
374 (loop :for status
:= (iomux:wait-until-fd-ready fd
:write nil
) :do
375 (unless (member :write status
)
376 ;; FIXME signal something better -- maybe analyze the status
377 (return (values nil
:fail
)))
379 (return (values nil
:fail
)))
381 (return (values t nil
))))))))
384 (defmethod %stream-write-octets
((stream active-socket
) octets
386 ;; FIXME: when calling write-sequence with a simple-array of octets
387 ;; do required I/O directly, not through a buffer
388 (check-type octets
(simple-array ub8
(*)))
389 (let ((max (length octets
)))
391 (check-type start unsigned-byte
)
395 (check-type end unsigned-byte
)
396 (assert (<= end max
)))
398 (with-slots ((buf output-buffer
) fd
) stream
399 (loop :while
(< start end
) :do
400 (let ((len (min (- end start
) (iobuf-end-space-length buf
))))
401 (setf *print-readably
* nil
)
402 ;; FIXME: optimize this BLT
403 (loop :for i
:from start
404 :for j
:from
(iobuf-end buf
)
406 (setf (bref buf j
) (aref octets i
)))
407 (incf (iobuf-end buf
) len
)
409 (when (= (iobuf-end buf
) (iobuf-size buf
))
410 (or (flush-obuf buf fd
)
411 ;; FIXME: better error handling
412 (error "Failed to write octets")))))))
414 (defmethod stream-write-char ((stream active-socket
) character
)
415 ;; FIXME: avoid consing a string here. At worst, declare it dynamic-extent
416 (stream-write-string stream
(make-string 1 :initial-element character
)))
418 ;; (defmethod stream-advance-to-column ((stream active-socket)
419 ;; (column integer)))
421 ;; (defmethod stream-line-column ((stream active-socket)))
423 ;; (defmethod stream-line-length ((stream active-socket)))
425 (defmethod stream-start-line-p ((stream active-socket
))
428 ;; (defmethod stream-terpri ((stream active-socket)))
430 ;; (defmethod stream-fresh-line ((stream active-socket)))
432 (defmethod stream-write-string ((stream active-socket
)
435 ;; FIXME: have the ef do i/o directly into the existing buffer,
436 ;; don't do double buffering of I/O
437 (%stream-write-octets
439 (ioenc:string-to-octets string
:start start
:end end
440 :external-format
(slot-value stream
'external-format
))))
442 ;; FIXME: isn't there a generic stream-write-sequence???
451 (defmethod stream-read-byte ((stream active-socket
))
452 (with-slots ((fd fd
) (ib input-buffer
)
453 (pos istream-pos
)) stream
454 (flet ((fill-buf-or-eof ()
455 (when (eq :eof
(fill-ibuf ib fd
))
456 (return-from stream-read-byte
:eof
))))
457 (when (zerop (iobuf-length ib
))
460 (prog1 (bref ib
(iobuf-start ib
))
462 (incf (iobuf-start ib
))))))
470 ;; (defmethod stream-write-byte ((stream active-socket) (integer integer))