changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > core / lisp/std/stream.lisp

changeset 670: 6856c021d084
parent: 849bbe48e32d
author: Richard Westhaver <ellis@rwest.io>
date: Mon, 23 Sep 2024 21:14:10 -0400
permissions: -rw-r--r--
description: add dir-locals to skel, fix package lock violation in castable, move .sk files
1 ;;; std/stream.lisp --- Standard Streams
2 
3 ;;
4 
5 ;;; Code:
6 (in-package :std/stream)
7 
8 (defun copy-stream (input output &key (element-type (stream-element-type input))
9  (buffer-size 4096)
10  (buffer (make-array buffer-size :element-type element-type))
11  (start 0) end
12  finish-output)
13  "Reads data from INPUT and writes it to OUTPUT. Both INPUT and OUTPUT must
14 be streams, they will be passed to READ-SEQUENCE and WRITE-SEQUENCE and must have
15 compatible element-types."
16  (check-type start non-negative-integer)
17  (check-type end (or null non-negative-integer))
18  (check-type buffer-size positive-integer)
19  (when (and end
20  (< end start))
21  (error "END is smaller than START in ~S" 'copy-stream))
22  (let ((output-position 0)
23  (input-position 0))
24  (unless (zerop start)
25  ;; FIXME add platform specific optimization to skip seekable streams
26  (loop while (< input-position start)
27  do (let ((n (read-sequence buffer input
28  :end (min (length buffer)
29  (- start input-position)))))
30  (when (zerop n)
31  (error "~@<Could not read enough bytes from the input to fulfill ~
32  the :START ~S requirement in ~S.~:@>" 'copy-stream start))
33  (incf input-position n))))
34  (assert (= input-position start))
35  (loop while (or (null end) (< input-position end))
36  do (let ((n (read-sequence buffer input
37  :end (when end
38  (min (length buffer)
39  (- end input-position))))))
40  (when (zerop n)
41  (if end
42  (error "~@<Could not read enough bytes from the input to fulfill ~
43  the :END ~S requirement in ~S.~:@>" 'copy-stream end)
44  (return)))
45  (incf input-position n)
46  (write-sequence buffer output :end n)
47  (incf output-position n)))
48  (when finish-output
49  (finish-output output))
50  output-position))
51 
52 ;; from SBCL manual
53 ;;; Wrapped Streams
54 (defclass wrapped-stream (fundamental-stream)
55  ((stream :initarg :stream :reader stream-of)))
56 
57 (defmethod stream-element-type ((stream wrapped-stream))
58  (stream-element-type (stream-of stream)))
59 
60 (defmethod close ((stream wrapped-stream) &key abort)
61  (close (stream-of stream) :abort abort))
62 
63 (defclass wrapped-character-input-stream (wrapped-stream fundamental-character-input-stream)
64  ())
65 
66 (defmethod stream-read-char ((stream wrapped-character-input-stream))
67  (read-char (stream-of stream) nil :eof))
68 
69 (defmethod stream-unread-char ((stream wrapped-character-input-stream)
70  char)
71  (unread-char char (stream-of stream)))
72 
73 #| example:
74 (with-input-from-string (input "1 2
75  3 :foo ")
76  (let ((counted-stream (make-instance 'counting-character-input-stream
77  :stream input)))
78  (loop for thing = (read counted-stream) while thing
79  unless (numberp thing) do
80  (error "Non-number ~S (line ~D, column ~D)" thing
81  (line-count-of counted-stream)
82  (- (col-count-of counted-stream)
83  (length (format nil "~S" thing))))
84  end
85  do (print thing))))
86 1
87 2
88 3
89 Non-number :FOO (line 2, column 5)
90  [Condition of type SIMPLE-ERROR]
91 |#
92 (defclass counting-character-input-stream
93  (wrapped-character-input-stream)
94  ((char-count :initform 1 :accessor char-count-of)
95  (line-count :initform 1 :accessor line-count-of)
96  (col-count :initform 1 :accessor col-count-of)
97  (prev-col-count :initform 1 :accessor prev-col-count-of)))
98 
99 (defmethod stream-read-char ((stream counting-character-input-stream))
100  (with-accessors ((inner-stream stream-of) (chars char-count-of)
101  (lines line-count-of) (cols col-count-of)
102  (prev prev-col-count-of)) stream
103  (let ((char (call-next-method)))
104  (cond ((eql char :eof)
105  :eof)
106  ((char= char #\Newline)
107  (incf lines)
108  (incf chars)
109  (setf prev cols)
110  (setf cols 1)
111  char)
112  (t
113  (incf chars)
114  (incf cols)
115  char)))))
116 
117 (defmethod stream-unread-char ((stream counting-character-input-stream)
118  char)
119  (with-accessors ((inner-stream stream-of) (chars char-count-of)
120  (lines line-count-of) (cols col-count-of)
121  (prev prev-col-count-of)) stream
122  (cond ((char= char #\Newline)
123  (decf lines)
124  (decf chars)
125  (setf cols prev))
126  (t
127  (decf chars)
128  (decf cols)
129  char))
130  (call-next-method)))
131 
132 (defclass wrapped-character-output-stream (wrapped-stream fundamental-character-output-stream)
133  ((col-index :initform 0 :accessor col-index-of)))
134 
135 (defmethod stream-line-column ((stream wrapped-character-output-stream))
136  (col-index-of stream))
137 
138 (defmethod stream-write-char ((stream wrapped-character-output-stream)
139  char)
140  (with-accessors ((inner-stream stream-of) (cols col-index-of)) stream
141  (write-char char inner-stream)
142  (if (char= char #\Newline)
143  (setf cols 0)
144  (incf cols))))
145 
146 #| example:
147 (flet ((format-timestamp (stream)
148  (apply #'format stream "[~2@*~2,' D:~1@*~2,'0D:~0@*~2,'0D] "
149  (multiple-value-list (get-decoded-time)))))
150  (let ((output (make-instance 'prefixed-character-output-stream
151  :stream *standard-output*
152  :prefix #'format-timestamp)))
153  (loop for string in '("abc" "def" "ghi") do
154  (write-line string output)
155  (sleep 1))))
156 [ 0:30:05] abc
157 [ 0:30:06] def
158 [ 0:30:07] ghi
159 NIL
160 |#
161 (defclass prefixed-character-output-stream
162  (wrapped-character-output-stream)
163  ((prefix :initarg :prefix :reader prefix-of)))
164 
165 (defgeneric write-prefix (prefix stream)
166  (:method ((prefix string) stream) (write-string prefix stream))
167  (:method ((prefix function) stream) (funcall prefix stream)))
168 
169 (defmethod stream-write-char ((stream prefixed-character-output-stream)
170  char)
171  (with-accessors ((inner-stream stream-of) (cols col-index-of)
172  (prefix prefix-of)) stream
173  (when (zerop cols)
174  (write-prefix prefix inner-stream))
175  (call-next-method)))