in-memory.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP (HTM) git clone git://bitreich.org/clic/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/clic/ (DIR) Log (DIR) Files (DIR) Refs (DIR) Tags (DIR) README (DIR) LICENSE --- in-memory.lisp (19444B) --- 1 ;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS; Base: 10 -*- 2 ;;; $Header: /usr/local/cvsrep/flexi-streams/in-memory.lisp,v 1.31 2008/05/19 07:57:07 edi Exp $ 3 4 ;;; Copyright (c) 2005-2008, Dr. Edmund Weitz. All rights reserved. 5 6 ;;; Redistribution and use in source and binary forms, with or without 7 ;;; modification, are permitted provided that the following conditions 8 ;;; are met: 9 10 ;;; * Redistributions of source code must retain the above copyright 11 ;;; notice, this list of conditions and the following disclaimer. 12 13 ;;; * Redistributions in binary form must reproduce the above 14 ;;; copyright notice, this list of conditions and the following 15 ;;; disclaimer in the documentation and/or other materials 16 ;;; provided with the distribution. 17 18 ;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED 19 ;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 ;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 ;;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 22 ;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 ;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 24 ;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 ;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 ;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 ;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 ;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30 (in-package :flexi-streams) 31 32 (defclass in-memory-stream (trivial-gray-stream-mixin) 33 ((transformer :initarg :transformer 34 :accessor in-memory-stream-transformer 35 :documentation "A function used to transform the 36 written/read octet to the value stored/retrieved in/from the 37 underlying vector.") 38 #+:cmu 39 (open-p :initform t 40 :accessor in-memory-stream-open-p 41 :documentation "For CMUCL we have to keep track of this 42 manually.")) 43 (:documentation "An IN-MEMORY-STREAM is a binary stream that reads 44 octets from or writes octets to a sequence in RAM.")) 45 46 (defclass in-memory-input-stream (in-memory-stream fundamental-binary-input-stream) 47 () 48 (:documentation "An IN-MEMORY-INPUT-STREAM is a binary stream that 49 reads octets from a sequence in RAM.")) 50 51 #+:cmu 52 (defmethod output-stream-p ((stream in-memory-input-stream)) 53 "Explicitly states whether this is an output stream." 54 (declare (optimize speed)) 55 nil) 56 57 (defclass in-memory-output-stream (in-memory-stream fundamental-binary-output-stream) 58 () 59 (:documentation "An IN-MEMORY-OUTPUT-STREAM is a binary stream that 60 writes octets to a sequence in RAM.")) 61 62 #+:cmu 63 (defmethod input-stream-p ((stream in-memory-output-stream)) 64 "Explicitly states whether this is an input stream." 65 (declare (optimize speed)) 66 nil) 67 68 (defclass list-stream () 69 ((list :initarg :list 70 :accessor list-stream-list 71 :documentation "The underlying list of the stream.")) 72 (:documentation "A LIST-STREAM is a mixin for IN-MEMORY streams 73 where the underlying sequence is a list.")) 74 75 (defclass vector-stream () 76 ((vector :initarg :vector 77 :accessor vector-stream-vector 78 :documentation "The underlying vector of the stream which 79 \(for output) must always be adjustable and have a fill pointer.")) 80 (:documentation "A VECTOR-STREAM is a mixin for IN-MEMORY streams 81 where the underlying sequence is a vector.")) 82 83 (defclass list-input-stream (list-stream in-memory-input-stream) 84 () 85 (:documentation "A binary input stream that gets its data from an 86 associated list of octets.")) 87 88 (defclass vector-input-stream (vector-stream in-memory-input-stream) 89 ((index :initarg :index 90 :accessor vector-stream-index 91 :type (integer 0 #.array-dimension-limit) 92 :documentation "An index into the underlying vector denoting 93 the current position.") 94 (end :initarg :end 95 :accessor vector-stream-end 96 :type (integer 0 #.array-dimension-limit) 97 :documentation "An index into the underlying vector denoting 98 the end of the available data.")) 99 (:documentation "A binary input stream that gets its data from an 100 associated vector of octets.")) 101 102 (defclass vector-output-stream (vector-stream in-memory-output-stream) 103 () 104 (:documentation "A binary output stream that writes its data to an 105 associated vector.")) 106 107 #+:cmu 108 (defmethod open-stream-p ((stream in-memory-stream)) 109 "Returns a true value if STREAM is open. See ANSI standard." 110 (declare #.*standard-optimize-settings*) 111 (in-memory-stream-open-p stream)) 112 113 #+:cmu 114 (defmethod close ((stream in-memory-stream) &key abort) 115 "Closes the stream STREAM. See ANSI standard." 116 (declare #.*standard-optimize-settings*) 117 (declare (ignore abort)) 118 (prog1 119 (in-memory-stream-open-p stream) 120 (setf (in-memory-stream-open-p stream) nil))) 121 122 (defmethod check-if-open ((stream in-memory-stream)) 123 "Checks if STREAM is open and signals an error otherwise." 124 (declare #.*standard-optimize-settings*) 125 (unless (open-stream-p stream) 126 (error 'in-memory-stream-closed-error 127 :stream stream))) 128 129 (defmethod stream-element-type ((stream in-memory-stream)) 130 "The element type is always OCTET by definition." 131 (declare #.*standard-optimize-settings*) 132 'octet) 133 134 (defgeneric peek-byte (stream &optional peek-type eof-err-p eof-value) 135 (:documentation 136 "PEEK-BYTE is like PEEK-CHAR, i.e. it returns a byte from the stream without 137 actually removing it. If PEEK-TYPE is NIL the next byte is returned, if 138 PEEK-TYPE is T, the next byte which is not 0 is returned, if PEEK-TYPE is an 139 byte, the next byte which equals PEEK-TYPE is returned. EOF-ERROR-P and 140 EOF-VALUE are interpreted as usual.")) 141 142 (defmethod peek-byte ((stream vector-input-stream) &optional peek-type (eof-error-p t) eof-value) 143 "Returns a byte from VECTOR-INPUT-STREAM without actually removing it." 144 (declare #.*standard-optimize-settings*) 145 (let ((index (vector-stream-index stream))) 146 (loop :for byte = (read-byte stream eof-error-p :eof) 147 :for new-index :from index 148 :until (cond ((eq byte :eof) 149 (return eof-value)) 150 ((null peek-type)) 151 ((eq peek-type 't) 152 (plusp byte)) 153 ((= byte peek-type))) 154 :finally (setf (slot-value stream 'index) new-index) 155 (return byte)))) 156 157 (defmethod peek-byte ((stream list-input-stream) &optional peek-type (eof-error-p t) eof-value) 158 "Returns a byte from VECTOR-INPUT-STREAM without actually removing it." 159 (declare #.*standard-optimize-settings*) 160 (loop 161 :for list-elem = (car (list-stream-list stream)) 162 :for byte = (read-byte stream eof-error-p :eof) 163 :until (cond ((eq byte :eof) 164 (return eof-value)) 165 ((null peek-type)) 166 ((eq peek-type 't) 167 (plusp byte)) 168 ((= byte peek-type))) 169 :finally (push list-elem (list-stream-list stream)) 170 (return byte))) 171 172 (defmethod transform-octet ((stream in-memory-stream) octet) 173 "Applies the transformer of STREAM to octet and returns the result." 174 (declare #.*standard-optimize-settings*) 175 (funcall (or (in-memory-stream-transformer stream) 176 #'identity) octet)) 177 178 (defmethod stream-read-byte ((stream list-input-stream)) 179 "Reads one byte by simply popping it off of the top of the list." 180 (declare #.*standard-optimize-settings*) 181 (check-if-open stream) 182 (with-accessors ((list list-stream-list)) 183 stream 184 (transform-octet stream (or (pop list) (return-from stream-read-byte :eof))))) 185 186 (defmethod stream-listen ((stream list-input-stream)) 187 "Checks whether list is not empty." 188 (declare #.*standard-optimize-settings*) 189 (check-if-open stream) 190 (with-accessors ((list list-stream-list)) 191 stream 192 list)) 193 194 (defmethod stream-read-sequence ((stream list-input-stream) sequence start end &key) 195 "Repeatedly pops elements from the list until it's empty." 196 (declare #.*standard-optimize-settings*) 197 (declare (fixnum start end)) 198 (with-accessors ((list list-stream-list)) 199 stream 200 (loop with transformer = (in-memory-stream-transformer stream) 201 for index of-type fixnum from start below end 202 while list 203 do (let ((elt (pop list))) 204 (setf (elt sequence index) 205 (if transformer 206 (funcall transformer elt) 207 elt))) 208 finally (return index)))) 209 210 (defmethod stream-read-byte ((stream vector-input-stream)) 211 "Reads one byte and increments INDEX pointer unless we're beyond 212 END pointer." 213 (declare #.*standard-optimize-settings*) 214 (check-if-open stream) 215 (with-accessors ((index vector-stream-index) 216 (end vector-stream-end) 217 (vector vector-stream-vector)) 218 stream 219 (let ((current-index index)) 220 (declare (fixnum current-index)) 221 (cond ((< current-index (the fixnum end)) 222 (incf (the fixnum index)) 223 (transform-octet stream (aref vector current-index))) 224 (t :eof))))) 225 226 (defmethod stream-listen ((stream vector-input-stream)) 227 "Checking whether INDEX is beyond END." 228 (declare #.*standard-optimize-settings*) 229 (check-if-open stream) 230 (with-accessors ((index vector-stream-index) 231 (end vector-stream-end)) 232 stream 233 (< (the fixnum index) (the fixnum end)))) 234 235 (defmethod stream-read-sequence ((stream vector-input-stream) sequence start end &key) 236 "Traverses both sequences in parallel until the end of one of them 237 is reached." 238 (declare #.*standard-optimize-settings*) 239 (declare (fixnum start end)) 240 (loop with vector-end of-type fixnum = (vector-stream-end stream) 241 with vector = (vector-stream-vector stream) 242 with transformer = (in-memory-stream-transformer stream) 243 for index of-type fixnum from start below end 244 for vector-index of-type fixnum = (vector-stream-index stream) 245 while (< vector-index vector-end) 246 do (let ((elt (aref vector vector-index))) 247 (setf (elt sequence index) 248 (if transformer 249 (funcall transformer elt) 250 elt))) 251 (incf (the fixnum (vector-stream-index stream))) 252 finally (return index))) 253 254 (defmethod stream-write-byte ((stream vector-output-stream) byte) 255 "Writes a byte \(octet) by extending the underlying vector." 256 (declare #.*standard-optimize-settings*) 257 (check-if-open stream) 258 (with-accessors ((vector vector-stream-vector)) 259 stream 260 (vector-push-extend (transform-octet stream byte) vector))) 261 262 (defmethod stream-write-sequence ((stream vector-output-stream) sequence start end &key) 263 "Just calls VECTOR-PUSH-EXTEND repeatedly." 264 (declare #.*standard-optimize-settings*) 265 (declare (fixnum start end)) 266 (with-accessors ((vector vector-stream-vector)) 267 stream 268 (loop for index of-type fixnum from start below end 269 do (vector-push-extend (transform-octet stream (elt sequence index)) vector)) 270 sequence)) 271 272 (defmethod stream-file-position ((stream vector-input-stream)) 273 "Simply returns the index into the underlying vector." 274 (declare #.*standard-optimize-settings*) 275 (with-accessors ((index vector-stream-index)) 276 stream 277 index)) 278 279 (defmethod (setf stream-file-position) (position-spec (stream vector-input-stream)) 280 "Sets the index into the underlying vector if POSITION-SPEC is acceptable." 281 (declare #.*standard-optimize-settings*) 282 (with-accessors ((index vector-stream-index) 283 (end vector-stream-end)) 284 stream 285 (setq index 286 (case position-spec 287 (:start 0) 288 (:end end) 289 (otherwise 290 (unless (integerp position-spec) 291 (error 'in-memory-stream-position-spec-error 292 :format-control "Unknown file position designator: ~S." 293 :format-arguments (list position-spec) 294 :stream stream 295 :position-spec position-spec)) 296 (unless (<= 0 position-spec end) 297 (error 'in-memory-stream-position-spec-error 298 :format-control "File position designator ~S is out of bounds." 299 :format-arguments (list position-spec) 300 :stream stream 301 :position-spec position-spec)) 302 position-spec))) 303 position-spec)) 304 305 (defmethod stream-file-position ((stream vector-output-stream)) 306 "Simply returns the fill pointer of the underlying vector." 307 (declare #.*standard-optimize-settings*) 308 (with-accessors ((vector vector-stream-vector)) 309 stream 310 (fill-pointer vector))) 311 312 (defmethod (setf stream-file-position) (position-spec (stream vector-output-stream)) 313 "Sets the fill pointer underlying vector if POSITION-SPEC is 314 acceptable. Adjusts the vector if necessary." 315 (declare #.*standard-optimize-settings*) 316 (with-accessors ((vector vector-stream-vector)) 317 stream 318 (let* ((total-size (array-total-size vector)) 319 (new-fill-pointer 320 (case position-spec 321 (:start 0) 322 (:end 323 (warn "File position designator :END doesn't really make sense for an output stream.") 324 total-size) 325 (otherwise 326 (unless (integerp position-spec) 327 (error 'in-memory-stream-position-spec-error 328 :format-control "Unknown file position designator: ~S." 329 :format-arguments (list position-spec) 330 :stream stream 331 :position-spec position-spec)) 332 (unless (<= 0 position-spec array-total-size-limit) 333 (error 'in-memory-stream-position-spec-error 334 :format-control "File position designator ~S is out of bounds." 335 :format-arguments (list position-spec) 336 :stream stream 337 :position-spec position-spec)) 338 position-spec)))) 339 (declare (fixnum total-size new-fill-pointer)) 340 (when (> new-fill-pointer total-size) 341 (adjust-array vector new-fill-pointer)) 342 (setf (fill-pointer vector) new-fill-pointer) 343 position-spec))) 344 345 (defmethod make-in-memory-input-stream ((vector vector) &key (start 0) 346 (end (length vector)) 347 transformer) 348 "Returns a binary input stream which will supply, in order, the 349 octets in the subsequence of VECTOR bounded by START and END. 350 Each octet returned will be transformed in turn by the optional 351 TRANSFORMER function." 352 (declare #.*standard-optimize-settings*) 353 (make-instance 'vector-input-stream 354 :vector vector 355 :index start 356 :end end 357 :transformer transformer)) 358 359 (defmethod make-in-memory-input-stream ((list list) &key (start 0) 360 (end (length list)) 361 transformer) 362 "Returns a binary input stream which will supply, in order, the 363 octets in the subsequence of LIST bounded by START and END. Each 364 octet returned will be transformed in turn by the optional 365 TRANSFORMER function." 366 (declare #.*standard-optimize-settings*) 367 (make-instance 'list-input-stream 368 :list (subseq list start end) 369 :transformer transformer)) 370 371 (defun make-output-vector (&key (element-type 'octet)) 372 "Creates and returns an array which can be used as the underlying 373 vector for a VECTOR-OUTPUT-STREAM." 374 (declare #.*standard-optimize-settings*) 375 (make-array 0 :adjustable t 376 :fill-pointer 0 377 :element-type element-type)) 378 379 (defun make-in-memory-output-stream (&key (element-type 'octet) transformer) 380 "Returns a binary output stream which accepts objects of type 381 ELEMENT-TYPE \(a subtype of OCTET) and makes available a sequence 382 that contains the octes that were actually output. The octets 383 stored will each be transformed by the optional TRANSFORMER 384 function." 385 (declare #.*standard-optimize-settings*) 386 (make-instance 'vector-output-stream 387 :vector (make-output-vector :element-type element-type) 388 :transformer transformer)) 389 390 (defmethod get-output-stream-sequence ((stream in-memory-output-stream) &key as-list) 391 "Returns a vector containing, in order, all the octets that have 392 been output to the IN-MEMORY stream STREAM. This operation clears any 393 octets on STREAM, so the vector contains only those octets which have 394 been output since the last call to GET-OUTPUT-STREAM-SEQUENCE or since 395 the creation of the stream, whichever occurred most recently. If 396 AS-LIST is true the return value is coerced to a list." 397 (declare #.*standard-optimize-settings*) 398 (with-accessors ((vector vector-stream-vector)) 399 stream 400 (prog1 401 (if as-list 402 (coerce vector 'list) 403 vector) 404 (setq vector 405 (make-output-vector))))) 406 407 (defmethod output-stream-sequence-length ((stream in-memory-output-stream)) 408 "Returns the current length of the underlying vector of the 409 IN-MEMORY output stream STREAM." 410 (declare (optimize speed)) 411 (length (the vector (vector-stream-vector stream)))) 412 413 (defmacro with-input-from-sequence ((var sequence &key start end transformer) 414 &body body) 415 "Creates an IN-MEMORY input stream from SEQUENCE using the 416 parameters START and END, binds VAR to this stream and then 417 executes the code in BODY. A function TRANSFORMER may optionally 418 be specified to transform the returned octets. The stream is 419 automatically closed on exit from WITH-INPUT-FROM-SEQUENCE, no 420 matter whether the exit is normal or abnormal. The return value 421 of this macro is the return value of BODY." 422 (with-rebinding (sequence) 423 `(let (,var) 424 (unwind-protect 425 (progn 426 (setq ,var (make-in-memory-input-stream ,sequence 427 :start (or ,start 0) 428 :end (or ,end (length ,sequence)) 429 :transformer ,transformer)) 430 ,@body) 431 (when ,var (close ,var)))))) 432 433 (defmacro with-output-to-sequence ((var &key as-list (element-type ''octet) transformer) 434 &body body) 435 "Creates an IN-MEMORY output stream, binds VAR to this stream 436 and then executes the code in BODY. The stream stores data of 437 type ELEMENT-TYPE \(a subtype of OCTET) which is \(optionally) 438 transformed by the function TRANSFORMER prior to storage. The 439 stream is automatically closed on exit from 440 WITH-OUTPUT-TO-SEQUENCE, no matter whether the exit is normal or 441 abnormal. The return value of this macro is a vector \(or a list 442 if AS-LIST is true) containing the octets that were sent to the 443 stream within BODY." 444 `(let (,var) 445 (unwind-protect 446 (progn 447 (setq ,var (make-in-memory-output-stream :element-type ,element-type 448 :transformer ,transformer)) 449 ,@body 450 (get-output-stream-sequence ,var :as-list ,as-list)) 451 (when ,var (close ,var)))))