Merge cmucl simple-streams

... major code shifting: simple-streams.lisp is no more; its contents
    have moved into several  files, each for a specific kind of stream
... file layout is now the same as cmucl pcl/simple-streams directory
    for easier trading of fixes / functionality
... new functionality: external formats (need some work still, but
    infractructure is there), string-streams
... Some functionality still missing (see TODO file)
This commit is contained in:
Rudi Schlatte 2003-09-07 14:10:26 +00:00
parent 303253a69e
commit cc9a73604f
16 changed files with 3312 additions and 977 deletions

View file

@ -9,8 +9,7 @@ Documentation about simple streams is available at
http://www.franz.com/support/documentation/6.2/doc/streams.htm
This code was originally written by Paul Foley for cmucl; Paul's
version resides (as of 2003-05-12) at
http://users.actrix.co.nz/mycroft/cl.html
version is now in cmucl cvs.
The port to sbcl was done by Rudi Schlatte (rudi@constantly.at).

View file

@ -1,22 +1,30 @@
-*- text -*-
- Test writing beyond the end of a mapped-simple-stream
- Implement & test read-sequence, write-sequence for (un)signed-8 vectors
- Make reader work with simple-streams
- external format handling: load aliases, load formats, etc.
- Handle writing beyond the end of a mapped-simple-stream properly
- handle device-file-position for mapped streams
- Test write-octets / read-octets handling of encapsulated streams
- handle ansi-streams in write-octets / read-octets
- handle ansi-streams in write-octets / read-octets?
- Implement socket-base-simple-stream and chunked transfer encoding.
- Implement / test string streams.
- Test string streams.
- Make sure the code examples for stream encapsulation from Franz work
- Test every single output function
- Handle character position (slot charpos)
- make file-position work for non-file streams, where applicable
- Test character position (slot charpos)
- make pathname work for simple-streams
- test :abort argument to close (should revert to original file)

View file

@ -1,28 +1,30 @@
;;; -*- lisp -*-
;;;
;;; **********************************************************************
;;; This code was written by Paul Foley and has been placed in the public
;;; domain.
;;;
;;; This code is in the public domain.
;;; The cmucl implementation of simple-streams was done by Paul Foley,
;;; who placed the code in the public domain. Sbcl port by Rudi
;;; Schlatte.
;;; Sbcl port by Rudi Schlatte.
(in-package "SB-SIMPLE-STREAMS")
;;; (pushnew :sb-simple-stream *features*)
;;;
;;; TYPES FOR BUFFER AND STRATEGY FUNCTIONS
;;; **********************************************************************
;;;
;;; Base class and generic function definitions for simple-streams
;;; See chapter
;;; 12.2 Strategy descriptions necessary for encapsulation
;;; in the Franz documentation for a description of the j-xxx-fn slots.
;;;; Types for buffer and strategy functions
(deftype simple-stream-buffer ()
'(or sb-sys:system-area-pointer (sb-kernel:simple-unboxed-array (*))))
(deftype blocking ()
`(member t nil :bnb))
'(member t nil :bnb))
(deftype j-listen-fn ()
'(function (simple-stream) boolean))
@ -38,290 +40,104 @@
'(function ((or character null) simple-stream) (or character null)))
(deftype j-write-chars-fn ()
'(function (string simple-stream fixnum fixnum) t)) ;; return chars-written?
'(function (string simple-stream fixnum fixnum) t)) ; return chars-written?
(deftype j-unread-char-fn ()
'(function (simple-stream t) t)) ;; "relaxed" arg is boolean? what return?
'(function (simple-stream t) t)) ; "relaxed" arg is boolean? what return?
;;;
;;; STREAM CLASSES
;;;
;;; KLUDGE (sat 2003-01-15): def-stream-class and the
;;; with-stream-class / sm accessors implement a form of "sealing" of
;;; classes -- i.e., implementing very fast slot access at the price
;;; of not being able to change the class definition at runtime.
;;; Instead of a method call, a slot access for a simple-stream
;;; subclass is a funcall or (when the def-stream-class form has a
;;; location argument for the slot) a sb-pcl::clos-slots-ref. Given a
;;; sufficiently advanced PCL with (non-standard) sealing
;;; declarations, this machinery would be superfluous. For the time
;;; being, replacing 4 method calls with vector accesses for the fast
;;; path of read-char seems worthwhile to me. Besides, it's the
;;; documented interface to simple-stream internals, and so it's worth
;;; keeping.
(eval-when (:compile-toplevel :load-toplevel :execute)
(declaim (type hash-table *slot-access-functions*))
(defvar *slot-access-functions* (make-hash-table))
(defvar *automagic-accessors* nil))
;;; Commented out in favor of standard class machinery that does not
;;; depend on implementation internals.
#+nil
(defmacro def-stream-class (name superclasses slots &rest options)
(let ((accessors ())
(real-slots ()))
(dolist (slot slots)
;; Frob the slot arguments, memorizing either the location (an
;; integer) or the accessor of the slot. Optionally construct
;; an accessor if none is given.
(cond ((and (consp slot) (getf (rest slot) 'sb-pcl::location))
;; We have got a location specifier. Memorize it and
;; extract it until pcl itself can work with these.
(push (cons (first slot)
(cons (getf (rest slot) :type t)
(getf (rest slot) 'sb-pcl::location)))
accessors)
(let ((slot (copy-list slot)))
(remf (rest slot) 'sb-pcl::location) ; until PCL accepts this
(push slot real-slots)))
((or (not (consp slot)) (not (getf (rest slot) :accessor)))
(if *automagic-accessors*
;; Add an :accessor argument, and memorize it. FIXME:
;; will this work with sbcl? reader/writers are
;; named differently there (see
;; src/pcl/slot-name.lisp)
(let* ((slot (if (consp slot) slot (list slot)))
(accessor (or (cdr (gethash (first slot)
*slot-access-functions*))
(intern (format nil "~A ~A slot ACCESSOR"
name (first slot))
"SB-SLOT-ACCESSOR-NAME"))))
(push (cons (first slot)
(cons (getf (rest slot) :type t) accessor))
accessors)
(push (append slot `(:accessor ,accessor)) real-slots))
(push slot real-slots)))
(t
;; No location given, but we have an accessor. Memorize it.
(push (cons (first slot)
(cons (getf (rest slot) :type t)
(getf (rest slot) :accessor)))
accessors)
(push slot real-slots))))
`(prog1
(defclass ,name ,superclasses ,(nreverse real-slots) ,@options)
(eval-when (:compile-toplevel :load-toplevel :execute)
,@(loop for accessor in accessors
do (let ((exists (gethash (car accessor)
*slot-access-functions*)))
(when (and exists
(integerp (cdr exists))
(integerp (cddr accessor))
(/= (cdr exists) (cddr accessor)))
(warn "~S slot ~S has moved! ~
I hope you know what you're doing!"
name (car accessor))))
collect `(setf (gethash ',(car accessor) *slot-access-functions*)
',(cdr accessor)))))))
(defmacro def-stream-class (name superclasses slots &rest options)
(let ((slots (copy-tree slots)))
(dolist (slot slots) (remf (cdr slot) 'sb-pcl::location))
`(defclass ,name ,superclasses ,slots ,@options)))
;;;; Base simple-stream classes
(def-stream-class simple-stream (standard-object stream)
((plist :initform nil :type list :accessor stream-plist sb-pcl::location 19)
(;; instance flags (not a normal slot in Allegro CL)
(%flags :initform 0 :type fixnum)
(plist :initform nil :type list :accessor stream-plist)
;; Strategy slots. See section 12.2 of streams.htm for function
;; signatures and possible side-effects.
;; A function that determines if one character can be successfully
;; read from stream.
(j-listen :initform #'sb-kernel:ill-in :type j-listen-fn sb-pcl::location 18)
(j-listen :initform #'sb-kernel:ill-in :type j-listen-fn)
;; A function that reads one character.
(j-read-char :initform #'sb-kernel:ill-in :type j-read-char-fn sb-pcl::location 17)
(j-read-char :initform #'sb-kernel:ill-in :type j-read-char-fn)
;; A function that reads characters into a string.
(j-read-chars :initform #'sb-kernel:ill-in :type j-read-chars-fn sb-pcl::location 16)
(j-read-chars :initform #'sb-kernel:ill-in :type j-read-chars-fn)
;; A function that writes one character.
(j-write-char :initform #'sb-kernel:ill-out :type j-write-char-fn sb-pcl::location 15)
(j-write-char :initform #'sb-kernel:ill-out :type j-write-char-fn)
;; A function that writes characters from a string into the stream.
(j-write-chars :initform #'sb-kernel:ill-out :type j-write-chars-fn sb-pcl::location 14)
(j-write-chars :initform #'sb-kernel:ill-out :type j-write-chars-fn)
;; A function that unreads the last character read.
(j-unread-char :initform #'sb-kernel:ill-in :type j-unread-char-fn sb-pcl::location 13)
(j-unread-char :initform #'sb-kernel:ill-in :type j-unread-char-fn)
;; Other slots
;; Always a stream, allowing for composing external formats (see
;; streams.htm, section 12.5) TODO: document this better
(melded-stream sb-pcl::location 12)
;; Always a stream, allowing for composing external formats (see
;; streams.htm, section 12.5) TODO: document this better
(melding-base sb-pcl::location 11)
;; Number of octets the last read-char operation consumed TODO:
;; document this better; what is the difference to
;; last-char-read-size ?
(encapsulated-char-read-size :initform 0 :type fixnum sb-pcl::location 10)
(mode :initform 0 :type fixnum sb-pcl::location 9)
(control-in :initform nil :type (or null simple-vector)
sb-pcl::location 8)
(control-out :initform nil :type (or null simple-vector)
sb-pcl::location 7)
;; TODO: find out what this one does
(oc-state :initform nil)
;; TODO: find out what this one does
(co-state :initform nil)
(external-format :initform :default)
;; A fixnum (denoting a valid file descriptor), a stream, or nil if
;; the stream is not open for input.
(input-handle :initform nil :initarg :input-handle sb-pcl::location 6
:type (or null fixnum stream)
(input-handle :initform nil :initarg :input-handle
:type (or null fixnum stream)
:accessor stream-input-handle)
;; A fixnum (denoting a valid file descriptor), a stream, or nil if
;; the stream is not open for output.
(output-handle :initform nil :initarg :output-handle sb-pcl::location 5
(output-handle :initform nil :initarg :output-handle
:type (or null fixnum stream)
:accessor stream-output-handle)
(external-format :initform :default sb-pcl::location 4)
(record-end :initform nil :type (or null fixnum) sb-pcl::location 3)
;; The character position of the stream.
(charpos :initform 0 :type (or null integer) sb-pcl::location 2)
(control-in :initform nil :type (or null simple-vector))
(control-out :initform nil :type (or null simple-vector))
;; a stream, allowing for composing external formats (see
;; streams.htm, section 12.5) TODO: document this better
(melded-stream :type (or null simple-stream))
;; a stream, allowing for composing external formats (see
;; streams.htm, section 12.5) TODO: document this better
(melding-base :type (or null simple-stream))
;; Number of octets the last read-char operation consumed TODO:
;; document this better; what is the difference to
;; last-char-read-size ?
(encapsulated-char-read-size :initform 0 :type fixnum)
;; Number of octets the last read-char operation consumed
(last-char-read-size :initform 0 :type fixnum sb-pcl::location 1)
;; instance flags (not a normal slot in Allegro CL)
(%flags :initform 0 :type fixnum sb-pcl::location 0)))
(last-char-read-size :initform 0 :type fixnum)
(charpos :initform 0 :type (or null integer)
:accessor stream-line-column)
(record-end :initform nil :type (or null fixnum))
(def-stream-class probe-simple-stream (simple-stream)
())
;;; A stream with a single buffer, for example a file stream.
(def-stream-class single-channel-simple-stream (simple-stream)
;; Input/output buffer.
((buffer :initform nil :type (or simple-stream-buffer null)
sb-pcl::location 23)
(buffer :initform nil :type (or simple-stream-buffer null))
;; Current position in buffer.
(buffpos :initform 0 :type fixnum sb-pcl::location 22)
(buffpos :initform 0 :type fixnum)
;; Maximum valid position in buffer, or -1 on eof.
(buffer-ptr :initform 0 :type fixnum sb-pcl::location 21)
(buf-len :initform 0 :type fixnum sb-pcl::location 20)))
(buffer-ptr :initform 0 :type fixnum)
(buf-len :initform 0 :type fixnum)
(def-stream-class direct-simple-stream (single-channel-simple-stream)
())
(pending :initform nil :type list)
(handler :initform nil :type (or null sb-impl::handler))))
(def-stream-class buffer-input-simple-stream (direct-simple-stream)
())
(def-stream-class single-channel-simple-stream (simple-stream)
(;; the "dirty" flag -- if this is > 0, write out buffer contents
;; before changing position; see flush-buffer
(mode :initform 0 :type fixnum)))
(def-stream-class buffer-output-simple-stream (direct-simple-stream)
((out-buffer :initform nil :type (or simple-stream-buffer null)
sb-pcl::location 26)
;; Current position in output buffer.
(outpos :initform 0 :type fixnum sb-pcl::location 25)
;; Buffer length (one greater than maximum output buffer index)
(max-out-pos :initform 0 :type fixnum sb-pcl::location 24)))
(def-stream-class null-simple-stream (single-channel-simple-stream)
())
(def-stream-class file-simple-stream (single-channel-simple-stream)
((pathname :initform nil :initarg :pathname sb-pcl::location 27)
(filename :initform nil :initarg :filename sb-pcl::location 26)
(original :initform nil :initarg :original sb-pcl::location 25)
(delete-original :initform nil :initarg :delete-original
sb-pcl::location 24)
))
(def-stream-class mapped-file-simple-stream (file-simple-stream
direct-simple-stream)
())
;;; A stream with two octet buffers, for example a socket or terminal
;;; stream.
(def-stream-class dual-channel-simple-stream (simple-stream)
(;; Output buffer.
(out-buffer :initform nil :type (or simple-stream-buffer null)
sb-pcl::location 26)
(out-buffer :initform nil :type (or simple-stream-buffer null))
;; Current position in output buffer.
(outpos :initform 0 :type fixnum sb-pcl::location 25)
(outpos :initform 0 :type fixnum)
;; Buffer length (one greater than maximum output buffer index)
(max-out-pos :initform 0 :type fixnum sb-pcl::location 24)
;; Input buffer (in this case; the 'buffer' slot serves as
;; bidirectional buffer for single-channel-simple-streams).
(buffer :initform nil :type (or simple-stream-buffer null)
sb-pcl::location 23)
;; Current position in buffer.
(buffpos :initform 0 :type fixnum sb-pcl::location 22)
;; Maximum valid position in buffer, or -1 on eof.
(buffer-ptr :initform 0 :type fixnum sb-pcl::location 21)
(buf-len :initform 0 :type fixnum sb-pcl::location 20)))
(def-stream-class terminal-simple-stream (dual-channel-simple-stream)
())
(def-stream-class socket-simple-stream (dual-channel-simple-stream)
((socket :initform nil :type (or sb-bsd-sockets:socket null)
:initarg :socket sb-pcl::location 27)))
(def-stream-class socket-base-simple-stream (dual-channel-simple-stream)
())
(def-stream-class hiper-socket-simple-stream (dual-channel-simple-stream)
())
(max-out-pos :initform 0 :type fixnum)))
;;; A stream with a string as buffer.
(def-stream-class string-simple-stream (simple-stream)
())
(def-stream-class composing-stream (string-simple-stream)
())
(def-stream-class string-input-simple-stream (string-simple-stream)
(;; The input buffer.
(buffer :initform nil :type (or simple-stream-buffer null)
sb-pcl::location 23)
;; Current position in buffer.
(buffpos :initform 0 :type fixnum sb-pcl::location 22)
;; Maximum valid position in buffer, or -1 on eof.
(buffer-ptr :initform 0 :type fixnum sb-pcl::location 21)
(buf-len :initform 0 :type fixnum sb-pcl::location 20)))
;;; ======================================================
(def-stream-class string-output-simple-stream (string-simple-stream)
(;; The input buffer.
(buffer :initform nil :type (or simple-stream-buffer null)
sb-pcl::location 26)
;; Current position in input buffer.
(buffpos :initform 0 :type fixnum sb-pcl::location 25)
;; Maximum valid position in input buffer, or -1 on eof.
(buffer-ptr :initform 0 :type fixnum sb-pcl::location 24)
(buf-len :initform 0 :type fixnum sb-pcl::location 23)
;; The output buffer (slot added so that a class can inherit from
;; both string-input-simple-stream and string-output-simple-stream
;; without the strategies clashing)
(out-buffer :initform nil :type (or simple-stream-buffer null)
sb-pcl::location 22)
;; Current position in output buffer.
(outpos :initform 0 :type fixnum sb-pcl::location 21)
;; Buffer length (one greater than maximum output buffer index)
(max-out-pos :initform 0 :type fixnum sb-pcl::location 20)))
(def-stream-class fill-pointer-output-simple-stream
(string-output-simple-stream)
())
(def-stream-class limited-string-output-simple-stream
(string-output-simple-stream)
())
(def-stream-class xp-simple-stream (string-output-simple-stream)
())
(def-stream-class annotation-output-simple-stream (string-output-simple-stream)
())
(defclass default-latin1-base-ef () ())
(defclass stream-recording-mixin () ())
(defclass stream-recording-repaint-mixin () ())
(eval-when (:compile-toplevel :load-toplevel :execute)
(setf *automagic-accessors* nil))
;;;
;;; DEVICE-LEVEL FUNCTIONS
@ -348,3 +164,110 @@
(defgeneric device-clear-output (stream))
(defgeneric device-finish-record (stream blocking action))
(defmethod shared-initialize :after ((instance simple-stream) slot-names
&rest initargs &key &allow-other-keys)
(declare (ignore slot-names))
(unless (slot-boundp instance 'melded-stream)
(setf (slot-value instance 'melded-stream) instance)
(setf (slot-value instance 'melding-base) instance))
(unless (device-open instance initargs)
(device-close instance t)))
(defmethod print-object ((object simple-stream) stream)
(print-unreadable-object (object stream :type nil :identity nil)
(cond ((not (any-stream-instance-flags object :simple))
(princ "Invalid " stream))
((not (any-stream-instance-flags object :input :output))
(princ "Closed " stream)))
(format stream "~:(~A~)" (type-of object))))
;;; This takes care of the things all device-close methods have to do,
;;; regardless of the type of simple-stream
(defmethod device-close :around ((stream simple-stream) abort)
(with-stream-class (simple-stream stream)
(when (any-stream-instance-flags stream :input :output)
(when (any-stream-instance-flags stream :output)
(ignore-errors (if abort
(clear-output stream)
(force-output stream))))
(call-next-method)
(setf (sm input-handle stream) nil
(sm output-handle stream) nil)
(remove-stream-instance-flags stream :input :output)
(sb-ext:cancel-finalization stream)
;; This sets all readers and writers to error-raising functions
(setf (stream-external-format stream) :void))))
(defmethod device-close ((stream simple-stream) abort)
(declare (ignore abort))
t)
(defmethod device-buffer-length ((stream simple-stream))
4096)
(defmethod device-file-position ((stream simple-stream))
(with-stream-class (simple-stream stream)
(sm buffpos stream)))
(defmethod (setf device-file-position) (value (stream simple-stream))
(with-stream-class (simple-stream stream)
(setf (sm buffpos stream) value)))
(defmethod device-file-length ((stream simple-stream))
nil)
(defgeneric (setf stream-external-format) (value stream))
(defmethod (setf stream-external-format) :before (value (stream simple-stream))
;; (unless (eq value (sm external-format stream))
;; flush out the existing external-format
)
(defmethod (setf stream-external-format) :after
(ef (stream single-channel-simple-stream))
(compose-encapsulating-streams stream ef)
(install-single-channel-character-strategy (melding-stream stream) ef nil))
(defmethod (setf stream-external-format) :after
(ef (stream dual-channel-simple-stream))
(compose-encapsulating-streams stream ef)
(install-dual-channel-character-strategy (melding-stream stream) ef))
(defmethod device-read ((stream single-channel-simple-stream) buffer
start end blocking)
(read-octets stream buffer start end blocking))
(defmethod device-read ((stream dual-channel-simple-stream) buffer
start end blocking)
(read-octets stream buffer start end blocking))
(defmethod device-clear-input ((stream simple-stream) buffer-only)
(declare (ignore buffer-only))
nil)
(defmethod device-write ((stream single-channel-simple-stream) buffer
start end blocking)
;; buffer may be :flush to force/finish-output
(when (or (and (null buffer) (not (eql start end)))
(eq buffer :flush))
(with-stream-class (single-channel-simple-stream stream)
(setf buffer (sm buffer stream))
(setf end (sm buffpos stream))))
(write-octets stream buffer start end blocking))
(defmethod device-write ((stream dual-channel-simple-stream) buffer
start end blocking)
;; buffer may be :flush to force/finish-output
(when (or (and (null buffer) (not (eql start end)))
(eq buffer :flush))
(with-stream-class (dual-channel-simple-stream stream)
(setf buffer (sm out-buffer stream))
(setf end (sm outpos stream))))
(write-octets stream buffer start end blocking))
(defmethod device-clear-output ((stream simple-stream))
nil)

View file

@ -0,0 +1,40 @@
;;; -*- lisp -*-
;;;
;;; **********************************************************************
;;; This code was written by Paul Foley and has been placed in the public
;;; domain.
;;;
;;; Sbcl port by Rudi Schlatte.
(in-package "SB-SIMPLE-STREAMS")
;;;
;;; **********************************************************************
;;;
;;; Direct-Simple-Stream and Buffer-(Input|Output)-Simple-Stream
(def-stream-class direct-simple-stream (single-channel-simple-stream)
())
(def-stream-class buffer-input-simple-stream (direct-simple-stream)
())
(def-stream-class buffer-output-simple-stream (direct-simple-stream)
((out-buffer :initform nil :type (or simple-stream-buffer null))
(outpos :initform 0 :type fixnum)
(max-out-pos :initform 0 :type fixnum)))
(defmethod device-file-length ((stream direct-simple-stream))
;; return buffer length
)
(defmethod device-open ((stream buffer-input-simple-stream) options)
#| do something |#
stream)
(defmethod device-open ((stream buffer-output-simple-stream) options)
#| do something |#
stream)

View file

@ -0,0 +1,274 @@
;;; -*- lisp -*-
;;;
;;; **********************************************************************
;;; This code was written by Paul Foley and has been placed in the public
;;; domain.
;;;
;;; Sbcl port by Rudi Schlatte.
(in-package "SB-SIMPLE-STREAMS")
;;;
;;; **********************************************************************
;;;
;;; Definition of File-Simple-Stream and relations
(def-stream-class file-simple-stream (single-channel-simple-stream)
((pathname :initform nil :initarg :pathname)
(filename :initform nil :initarg :filename)
(original :initform nil :initarg :original)
(delete-original :initform nil :initarg :delete-original)))
(def-stream-class mapped-file-simple-stream (file-simple-stream
direct-simple-stream)
())
(def-stream-class probe-simple-stream (simple-stream)
((pathname :initform nil :initarg :pathname)))
(defmethod print-object ((object file-simple-stream) stream)
(print-unreadable-object (object stream :type nil :identity nil)
(with-stream-class (file-simple-stream object)
(cond ((not (any-stream-instance-flags object :simple))
(princ "Invalid " stream))
((not (any-stream-instance-flags object :input :output))
(princ "Closed " stream)))
(format stream "~:(~A~) for ~S"
(type-of object) (sm filename object)))))
(defun open-file-stream (stream options)
(let ((filename (pathname (getf options :filename)))
(direction (getf options :direction :input))
(if-exists (getf options :if-exists))
(if-exists-given (not (eql (getf options :if-exists t) t)))
(if-does-not-exist (getf options :if-does-not-exist))
(if-does-not-exist-given (not (eql (getf options :if-does-not-exist t) t))))
(with-stream-class (file-simple-stream stream)
(ecase direction
(:input (add-stream-instance-flags stream :input))
(:output (add-stream-instance-flags stream :output))
(:io (add-stream-instance-flags stream :input :output)))
(cond ((and (sm input-handle stream) (sm output-handle stream)
(not (eql (sm input-handle stream)
(sm output-handle stream))))
(error "Input-Handle and Output-Handle can't be different."))
((or (sm input-handle stream) (sm output-handle stream))
(add-stream-instance-flags stream :simple)
;; get namestring, etc., from handle, if possible
;; (i.e., if it's a stream)
;; set up buffers
stream)
(t
(multiple-value-bind (fd namestring original delete-original)
(%fd-open filename direction if-exists if-exists-given
if-does-not-exist if-does-not-exist-given)
(when fd
(add-stream-instance-flags stream :simple)
(setf (sm pathname stream) filename
(sm filename stream) namestring
(sm original stream) original
(sm delete-original stream) delete-original)
(when (any-stream-instance-flags stream :input)
(setf (sm input-handle stream) fd))
(when (any-stream-instance-flags stream :output)
(setf (sm output-handle stream) fd))
(sb-ext:finalize stream
(lambda ()
(sb-unix:unix-close fd)
(format *terminal-io* "~&;;; ** closed ~S (fd ~D)~%"
namestring fd)
(when original
(revert-file namestring original))))
stream)))))))
(defmethod device-open ((stream file-simple-stream) options)
(with-stream-class (file-simple-stream stream)
(when (open-file-stream stream options)
;; Franz says:
;; "The device-open method must be prepared to recognize resource
;; and change-class situations. If no filename is specified in
;; the options list, and if no input-handle or output-handle is
;; given, then the input-handle and output-handle slots should
;; be examined; if non-nil, that means the stream is still open,
;; and thus the operation being requested of device-open is a
;; change-class. Also, a device-open method need not allocate a
;; buffer every time it is called, but may instead reuse a
;; buffer it finds in a stream, if it does not become a security
;; issue."
(unless (sm buffer stream)
(let ((length (device-buffer-length stream)))
(setf (sm buffer stream) (allocate-buffer length)
(sm buffpos stream) 0
(sm buffer-ptr stream) 0
(sm buf-len stream) length)))
(when (any-stream-instance-flags stream :output)
(setf (sm control-out stream) *std-control-out-table*))
(setf (stream-external-format stream)
(getf options :external-format :default))
stream)))
;;; Revert a file, if possible; otherwise just delete it. Used during
;;; CLOSE when the abort flag is set.
;;;
;;; TODO: use this in src/code/fd-stream.lisp:fd-stream-misc-routine
;;; as well, snarf error reporting from there.
(defun revert-file (filename original)
(declare (type simple-base-string filename)
(type (or simple-base-string null) original))
;; We can't do anything unless we know what file were
;; dealing with, and we don't want to do anything
;; strange unless we were writing to the file.
(if original
(multiple-value-bind (okay err) (sb-unix:unix-rename original filename)
(unless okay
(cerror "Go on as if nothing bad happened."
"Could not restore ~S to its original contents: ~A"
filename (sb-int:strerror err))))
;; We can't restore the original, so nuke that puppy.
(multiple-value-bind (okay err) (sb-unix:unix-unlink filename)
(unless okay
(cerror "Go on as if nothing bad happened."
"Could not remove ~S: ~A"
filename (sb-int:strerror err))))))
;;; DELETE-ORIGINAL -- internal
;;;
;;; Delete a backup file. Used during CLOSE.
;;;
;;; TODO: use this in src/code/fd-stream.lisp:fd-stream-misc-routine
;;; as well, snarf error reporting from there.
(defun delete-original (filename original)
(declare (type simple-base-string filename)
(type (or simple-base-string null) original))
(when original
(multiple-value-bind (okay err) (sb-unix:unix-unlink original)
(unless okay
(cerror "Go on as if nothing bad happened."
"Could not delete ~S during close of ~S: ~A"
original filename (sb-int:strerror err))))))
(defmethod device-close ((stream file-simple-stream) abort)
(with-stream-class (file-simple-stream stream)
(let ((fd (or (sm input-handle stream) (sm output-handle stream))))
(when (sb-int:fixnump fd)
(cond (abort
(when (any-stream-instance-flags stream :output)
(revert-file (sm filename stream) (sm original stream))))
(t
(when (sm delete-original stream)
(delete-original (sm filename stream) (sm original stream)))))
(sb-unix:unix-close fd))
(when (sm buffer stream)
(free-buffer (sm buffer stream))
(setf (sm buffer stream) nil))))
t)
(defmethod device-file-position ((stream file-simple-stream))
(with-stream-class (file-simple-stream stream)
(let ((fd (or (sm input-handle stream) (sm output-handle stream))))
(if (sb-int:fixnump fd)
(values (sb-unix:unix-lseek fd 0 sb-unix:l_incr))
(file-position fd)))))
(defmethod (setf device-file-position) (value (stream file-simple-stream))
(declare (type fixnum value))
(with-stream-class (file-simple-stream stream)
(let ((fd (or (sm input-handle stream) (sm output-handle stream))))
(if (sb-int:fixnump fd)
(values (sb-unix:unix-lseek fd
(if (minusp value) (1+ value) value)
(if (minusp value) sb-unix:l_xtnd sb-unix:l_set)))
(file-position fd value)))))
(defmethod device-file-length ((stream file-simple-stream))
(with-stream-class (file-simple-stream stream)
(let ((fd (or (sm input-handle stream) (sm output-handle stream))))
(if (sb-int:fixnump fd)
(multiple-value-bind (okay dev ino mode nlink uid gid rdev size)
(sb-unix:unix-fstat (sm input-handle stream))
(declare (ignore dev ino mode nlink uid gid rdev))
(if okay size nil))
(file-length fd)))))
(defmethod device-open ((stream mapped-file-simple-stream) options)
(with-stream-class (mapped-file-simple-stream stream)
(when (open-file-stream stream options)
(let* ((input (any-stream-instance-flags stream :input))
(output (any-stream-instance-flags stream :output))
(prot (logior (if input sb-posix::PROT-READ 0)
(if output sb-posix::PROT-WRITE 0)))
(fd (or (sm input-handle stream) (sm output-handle stream))))
(unless (sb-int:fixnump fd)
(error "Can't memory-map an encapsulated stream."))
(multiple-value-bind (okay dev ino mode nlink uid gid rdev size)
(sb-unix:unix-fstat fd)
(declare (ignore ino mode nlink uid gid rdev))
(unless okay
(sb-unix:unix-close fd)
(sb-ext:cancel-finalization stream)
(error "Error fstating ~S: ~A" stream
(sb-int:strerror dev)))
(when (> size most-positive-fixnum)
;; Or else BUF-LEN has to be a general integer, or
;; maybe (unsigned-byte 32). In any case, this means
;; BUF-MAX and BUF-PTR have to be the same, which means
;; number-consing every time BUF-PTR moves...
;; Probably don't have the address space available to map
;; bigger files, anyway. Maybe DEVICE-READ can adjust
;; the mapped portion of the file when necessary?
(warn "Unable to memory-map entire file.")
(setf size most-positive-fixnum))
(let ((buffer
(handler-case
(sb-posix:mmap nil size prot sb-posix::MAP-SHARED fd 0)
(sb-posix:syscall-error nil))))
(when (null buffer)
(sb-unix:unix-close fd)
(sb-ext:cancel-finalization stream)
(error "Unable to map file."))
(setf (sm buffer stream) buffer
(sm buffpos stream) 0
(sm buffer-ptr stream) size
(sm buf-len stream) size)
(when (any-stream-instance-flags stream :output)
(setf (sm control-out stream) *std-control-out-table*))
(let ((efmt (getf options :external-format :default)))
(compose-encapsulating-streams stream efmt)
(setf (stream-external-format stream) efmt)
;; overwrite the strategy installed in :after method of
;; (setf stream-external-format)
(install-single-channel-character-strategy
(melding-stream stream) efmt 'mapped))
(sb-ext:finalize stream
(lambda ()
(sb-posix:munmap buffer size)
(format *terminal-io* "~&;;; ** unmapped ~S" buffer))))))
stream)))
(defmethod device-close ((stream mapped-file-simple-stream) abort)
(with-stream-class (mapped-file-simple-stream stream)
(when (sm buffer stream)
(sb-posix:munmap (sm buffer stream) (sm buf-len stream))
(setf (sm buffer stream) nil))
(sb-unix:unix-close (or (sm input-handle stream) (sm output-handle stream))))
t)
;; TODO: implement msync in sb-posix; activate this
#+paul
(defmethod device-write ((stream mapped-file-simple-stream) buffer
start end blocking)
(assert (eq buffer :flush) (buffer)) ; finish/force-output
(with-stream-class (mapped-file-simple-stream stream)
(unix:unix-msync (sm buffer stream) (sm buf-len stream)
(if blocking unix:ms_sync unix:ms_async))))
(defmethod device-open ((stream probe-simple-stream) options)
(let ((pathname (getf options :filename)))
(with-stream-class (probe-simple-stream stream)
(add-stream-instance-flags stream :simple)
(when (sb-unix:unix-access (sb-int:unix-namestring pathname nil) sb-unix:f_ok)
(setf (sm pathname stream) pathname)
t))))

File diff suppressed because it is too large Load diff

View file

@ -1,204 +1,21 @@
;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Package: STREAM -*-
;;; This code is in the public domain.
;;; **********************************************************************
;;; This code was written by Paul Foley and has been placed in the public
;;; domain.
;;;
;;; The cmucl implementation of simple-streams was done by Paul Foley,
;;; who placed the code in the public domain. Sbcl port by Rudi
;;; Schlatte.
;;; Sbcl port by Rudi Schlatte.
(in-package "SB-SIMPLE-STREAMS")
;;;
;;; HELPER FUNCTIONS
;;; **********************************************************************
;;;
;;; Various functions needed by simple-streams
;; All known stream flags. Note that the position in the constant
;; list is significant (cf. %flags below).
(sb-int:defconstant-eqx +flag-bits+
'(:simple ; instance is valid
:input :output ; direction
:dual :string ; type of stream
:eof ; latched EOF
:dirty ; output buffer needs write
:interactive) ; interactive stream
#'equal)
(eval-when (:compile-toplevel :load-toplevel :execute)
(defun %flags (flags)
(loop for flag in flags
as pos = (position flag +flag-bits+)
when (eq flag :gray) do
(error "Gray streams not supported.")
if pos
sum (ash 1 pos) into bits
else
collect flag into unused
finally (when unused
(warn "Invalid stream instance flag~P: ~{~S~^, ~}"
(length unused) unused))
(return bits))))
;;; Setup an environment where sm, funcall-stm-handler and
;;; funcall-stm-handler-2 are valid and efficient for a stream of type
;;; class-name or for the stream argument (in which case the
;;; class-name argument is ignored). In nested with-stream-class
;;; forms, the inner with-stream-class form must specify a stream
;;; argument if the outer one specifies one, or the wrong object will
;;; be accessed.
;;; Commented out in favor of standard class machinery that does not
;;; depend on implementation internals.
#+nil
(defmacro with-stream-class ((class-name &optional stream) &body body)
(if stream
(let ((stm (gensym "STREAM"))
(slt (gensym "SV")))
`(let* ((,stm ,stream)
(,slt (sb-pcl::std-instance-slots ,stm)))
(declare (type ,class-name ,stm) (ignorable ,slt))
(macrolet ((sm (slot-name stream)
(declare (ignore stream))
(let ((slot-access (gethash slot-name
*slot-access-functions*)))
(cond ((sb-int:fixnump (cdr slot-access))
;; Get value in nth slot
`(the ,(car slot-access)
(sb-pcl::clos-slots-ref ,',slt
,(cdr slot-access))))
(slot-access
;; Call memorized function
`(the ,(car slot-access) (,(cdr slot-access)
,',stm)))
(t
;; Use slot-value
`(slot-value ,',stm ',slot-name)))))
(add-stream-instance-flags (stream &rest flags)
(declare (ignore stream))
`(setf (sm %flags ,',stm) (logior (sm %flags ,',stm)
,(%flags flags))))
(remove-stream-instance-flags (stream &rest flags)
(declare (ignore stream))
`(setf (sm %flags ,',stm) (logandc2 (sm %flags ,',stm)
,(%flags flags))))
(any-stream-instance-flags (stream &rest flags)
(declare (ignore stream))
`(not (zerop (logand (sm %flags ,',stm)
,(%flags flags))))))
,@body)))
`(macrolet ((sm (slot-name stream)
(let ((slot-access (gethash slot-name
*slot-access-functions*)))
(cond ((sb-int:fixnump (cdr slot-access))
`(the ,(car slot-access)
(sb-pcl::clos-slots-ref
(sb-pcl::std-instance-slots ,stream)
,(cdr slot-access))))
(slot-access
`(the ,(car slot-access) (,(cdr slot-access)
,stream)))
(t `(slot-value ,stream ',slot-name))))))
,@body)))
(defmacro with-stream-class ((class-name &optional stream) &body body)
(if stream
(let ((stm (gensym "STREAM"))
(slt (gensym "SV")))
`(let* ((,stm ,stream)
(,slt (sb-kernel:%instance-ref ,stm 1)))
(declare (type ,class-name ,stm)
(type simple-vector ,slt)
(ignorable ,slt))
(macrolet ((sm (slot-name stream)
(declare (ignore stream))
#-count-sm
`(slot-value ,',stm ',slot-name)
#+count-sm
`(%sm ',slot-name ,',stm))
(add-stream-instance-flags (stream &rest flags)
(declare (ignore stream))
`(setf (sm %flags ,',stm) (logior (sm %flags ,',stm)
,(%flags flags))))
(remove-stream-instance-flags (stream &rest flags)
(declare (ignore stream))
`(setf (sm %flags ,',stm) (logandc2 (sm %flags ,',stm)
,(%flags flags))))
(any-stream-instance-flags (stream &rest flags)
(declare (ignore stream))
`(not (zerop (logand (sm %flags ,',stm)
,(%flags flags))))))
,@body)))
`(macrolet ((sm (slot-name stream)
#-count-sm
`(slot-value ,stream ',slot-name)
#+count-sm
`(%sm ',slot-name ,stream)))
,@body)))
;;; Commented out in favor of standard class machinery that does not
;;; depend on implementation internals.
#+nil
(defmacro sm (slot-name stream)
(let ((slot-access (gethash slot-name *slot-access-functions*)))
(warn "Using ~S macro outside ~S" 'sm 'with-stream-class)
(cond ((sb-int:fixnump (cdr slot-access))
`(the ,(car slot-access) (sb-pcl::clos-slots-ref
(sb-pcl::std-instance-slots ,stream)
,(cdr slot-access))))
(slot-access
`(the ,(car slot-access) (,(cdr slot-access) ,stream)))
(t `(slot-value ,stream ',slot-name)))))
(defmacro sm (slot-name stream)
"Access the named slot in Stream."
(warn "Using ~S macro outside ~S." 'sm 'with-stream-class)
`(slot-value ,stream ',slot-name))
(defmacro funcall-stm-handler (slot-name stream &rest args)
(let ((s (gensym)))
`(let ((,s ,stream))
(funcall (sm ,slot-name ,s) ,s ,@args))))
(defmacro funcall-stm-handler-2 (slot-name arg1 stream &rest args)
(let ((s (gensym)))
`(let ((,s ,stream))
(funcall (sm ,slot-name ,s) ,arg1 ,s ,@args))))
(defmacro add-stream-instance-flags (stream &rest flags)
"Set the given flag bits in STREAM."
(let ((s (gensym "STREAM")))
`(let ((,s ,stream))
(with-stream-class (simple-stream ,s)
(setf (sm %flags ,s) (logior (sm %flags ,s) ,(%flags flags)))))))
(defmacro remove-stream-instance-flags (stream &rest flags)
"Clear the given flag bits in STREAM."
(let ((s (gensym "STREAM")))
`(let ((,s ,stream))
(with-stream-class (simple-stream ,s)
(setf (sm %flags ,s) (logandc2 (sm %flags ,s) ,(%flags flags)))))))
(defmacro any-stream-instance-flags (stream &rest flags)
"Determine whether any one of the FLAGS is set in STREAM."
(let ((s (gensym "STREAM")))
`(let ((,s ,stream))
(with-stream-class (simple-stream ,s)
(not (zerop (logand (sm %flags ,s) ,(%flags flags))))))))
(defmacro simple-stream-dispatch (stream single dual string)
(let ((s (gensym "STREAM")))
`(let ((,s ,stream))
(with-stream-class (simple-stream ,s)
(let ((%flags (sm %flags ,s)))
(cond ((zerop (logand %flags ,(%flags '(:string :dual))))
,single)
((zerop (logand %flags ,(%flags '(:string))))
,dual)
(t
,string)))))))
(declaim (inline buffer-sap bref (setf bref) buffer-copy))
(declaim (inline buffer-sap bref (setf bref) buffer-copy
allocate-buffer free-buffer))
(defun buffer-sap (thing &optional offset)
(declare (type simple-stream-buffer thing) (type (or fixnum null) offset)
@ -237,6 +54,399 @@
(push buffer sb-impl::*available-buffers*))
t)
(defun make-control-table (&rest inits)
(let ((table (make-array 32 :initial-element nil)))
(do* ((char (pop inits) (pop inits))
(func (pop inits) (pop inits)))
((null char))
(when (< (char-code char) 32)
(setf (aref table (char-code char)) func)))
table))
(defun std-newline-out-handler (stream character)
(declare (ignore character))
(with-stream-class (simple-stream stream)
(setf (sm charpos stream) -1)
nil))
(defun std-tab-out-handler (stream character)
(declare (ignore character))
(with-stream-class (simple-stream stream)
(let ((col (sm charpos stream)))
(when col
(setf (sm charpos stream) (1- (* 8 (1+ (floor col 8)))))))
nil))
(defun std-dc-newline-in-handler (stream character)
(with-stream-class (dual-channel-simple-stream stream)
(setf (sm charpos stream) -1) ;; set to 0 "if reading" ???
character))
(defvar *std-control-out-table*
(make-control-table #\Newline #'std-newline-out-handler
#\Tab #'std-tab-out-handler))
(defvar *default-external-format* :iso8859-1)
(defvar *external-formats* (make-hash-table))
(defvar *external-format-aliases* (make-hash-table))
(defstruct (external-format
(:conc-name ef-)
(:print-function %print-external-format)
(:constructor make-external-format (name octets-to-char
char-to-octets)))
(name (sb-int:missing-arg) :type keyword :read-only t)
(octets-to-char (sb-int:missing-arg) :type function :read-only t)
(char-to-octets (sb-int:missing-arg) :type function :read-only t))
(defun %print-external-format (ef stream depth)
(declare (ignore depth))
(print-unreadable-object (ef stream :type t :identity t)
(princ (ef-name ef) stream)))
(defmacro define-external-format (name octets-to-char char-to-octets)
`(macrolet ((octets-to-char ((state input unput) &body body)
`(lambda (,state ,input ,unput)
(declare (type (function () (unsigned-byte 8)) ,input)
(type (function (sb-int:index) t) ,unput)
(ignorable ,state ,input ,unput)
(values character sb-int:index t))
,@body))
(char-to-octets ((char state output) &body body)
`(lambda (,char ,state ,output)
(declare (type character ,char)
(type (function ((unsigned-byte 8)) t) ,output)
(ignorable state ,output)
(values t))
,@body)))
(setf (gethash ,name *external-formats*)
(make-external-format ,name ,octets-to-char ,char-to-octets))))
;;; TODO: make this work
(defun load-external-format-aliases ()
(let ((*package* (find-package "KEYWORD")))
(with-open-file (stm "ef:aliases" :if-does-not-exist nil)
(when stm
(do ((alias (read stm nil stm) (read stm nil stm))
(value (read stm nil stm) (read stm nil stm)))
((or (eq alias stm) (eq value stm))
(unless (eq alias stm)
(warn "External-format aliases file ends early.")))
(if (and (keywordp alias) (keywordp value))
(setf (gethash alias *external-format-aliases*) value)
(warn "Bad entry in external-format aliases file: ~S => ~S."
alias value)))))))
(defun find-external-format (name &optional (error-p t))
(when (external-format-p name)
(return-from find-external-format name))
(when (eq name :default)
(setq name *default-external-format*))
;; TODO: make this work
#+nil
(unless (ext:search-list-defined-p "ef:")
(setf (ext:search-list "ef:") '("library:ef/")))
(when (zerop (hash-table-count *external-format-aliases*))
(setf (gethash :latin1 *external-format-aliases*) :iso8859-1)
(setf (gethash :latin-1 *external-format-aliases*) :iso8859-1)
(setf (gethash :iso-8859-1 *external-format-aliases*) :iso8859-1)
(load-external-format-aliases))
(do ((tmp (gethash name *external-format-aliases*)
(gethash tmp *external-format-aliases*))
(cnt 0 (1+ cnt)))
((or (null tmp) (= cnt 50))
(unless (null tmp)
(error "External-format aliasing depth exceeded.")))
(setq name tmp))
(or (gethash name *external-formats*)
(and (let ((*package* (find-package "SB-SIMPLE-STREAMS")))
(load (format nil "ef:~(~A~)" name) :if-does-not-exist nil))
(gethash name *external-formats*))
(if error-p (error "External format ~S not found." name) nil)))
(define-condition void-external-format (error)
()
(:report
(lambda (condition stream)
(declare (ignore condition))
(format stream "Attempting I/O through void external-format."))))
(define-external-format :void
(octets-to-char (state input unput)
(declare (ignore state input unput))
(error 'void-external-format))
(char-to-octets (char state output)
(declare (ignore char state output))
(error 'void-external-format)))
(define-external-format :iso8859-1
(octets-to-char (state input unput)
(declare (optimize (speed 3) (space 0) (safety 0) (debug 0)))
(values (code-char (funcall input)) 1 state))
(char-to-octets (char state output)
(declare (optimize (speed 3) (space 0) (safety 0) (debug 0)))
(let ((code (char-code char)))
#-(or)
(funcall output code)
#+(or)
(if (< code 256)
(funcall output code)
(funcall output (char-code #\?))))
state))
(defmacro octets-to-char (external-format state count input unput)
(let ((tmp1 (gensym)) (tmp2 (gensym)) (tmp3 (gensym)))
`(multiple-value-bind (,tmp1 ,tmp2 ,tmp3)
(funcall (ef-octets-to-char ,external-format) ,state ,input ,unput)
(setf ,state ,tmp3 ,count ,tmp2)
,tmp1)))
(defmacro char-to-octets (external-format char state output)
`(progn
(setf ,state (funcall (ef-char-to-octets ,external-format)
,char ,state ,output))
nil))
(defun string-to-octets (string &key (start 0) end (external-format :default))
(declare (type string string)
(type sb-int:index start)
(type (or null sb-int:index) end))
(let ((ef (find-external-format external-format))
(buffer (make-array (length string) :element-type '(unsigned-byte 8)))
(ptr 0)
(state nil))
(flet ((out (b)
(setf (aref buffer ptr) b)
(when (= (incf ptr) (length buffer))
(setq buffer (adjust-array buffer (* 2 ptr))))))
(dotimes (i (- (or end (length string)) start))
(declare (type sb-int:index i))
(char-to-octets ef (char string (+ start i)) state #'out))
(sb-kernel:shrink-vector buffer ptr))))
(defun octets-to-string (octets &key (start 0) end (external-format :default))
(declare (type vector octets)
(type sb-int:index start)
(type (or null sb-int:index) end))
(let ((ef (find-external-format external-format))
(end (1- (or end (length octets))))
(string (make-string (length octets)))
(ptr (1- start))
(pos -1)
(count 0)
(state nil))
(flet ((input ()
(aref octets (incf ptr)))
(unput (n)
(decf ptr n)))
(loop until (>= ptr end)
do (setf (schar string (incf pos))
(octets-to-char ef state count #'input #'unput))))
(sb-kernel:shrink-vector string (1+ pos))))
(defun vector-elt-width (vector)
;; Return octet-width of vector elements
(etypecase vector
;; (simple-array fixnum (*)) not supported
;; (simple-array base-char (*)) treated specially; don't call this
((simple-array bit (*)) 1)
((simple-array (unsigned-byte 2) (*)) 1)
((simple-array (unsigned-byte 4) (*)) 1)
((simple-array (signed-byte 8) (*)) 1)
((simple-array (unsigned-byte 8) (*)) 1)
((simple-array (signed-byte 16) (*)) 2)
((simple-array (unsigned-byte 16) (*)) 2)
((simple-array (signed-byte 32) (*)) 4)
((simple-array (unsigned-byte 32) (*)) 4)
((simple-array single-float (*)) 4)
((simple-array double-float (*)) 8)
((simple-array (complex single-float) (*)) 8)
((simple-array (complex double-float) (*)) 16)))
#-(or big-endian little-endian)
(eval-when (:compile-toplevel)
(push sb-c::*backend-byte-order* *features*))
(defun endian-swap-value (vector endian-swap)
#+big-endian (declare (ignore vector))
(case endian-swap
(:network-order #+big-endian 0
#+little-endian (1- (vector-elt-width vector)))
(:byte-8 0)
(:byte-16 1)
(:byte-32 3)
(:byte-64 7)
(:byte-128 15)
(otherwise endian-swap)))
#+(or)
(defun %read-vector (vector stream start end endian-swap blocking)
(declare (type (kernel:simple-unboxed-array (*)) vector)
(type stream stream))
;; move code from read-vector
)
#+(or)
(defun %write-vector (... blocking)
;; implement me
)
(defun read-octets (stream buffer start end blocking)
(declare (type simple-stream stream)
(type (or null simple-stream-buffer) buffer)
(type fixnum start)
(type (or null fixnum) end)
(optimize (speed 3) (space 2) (safety 0) (debug 0)))
(with-stream-class (simple-stream stream)
(let ((fd (sm input-handle stream))
(end (or end (sm buf-len stream)))
(buffer (or buffer (sm buffer stream))))
(declare (fixnum end))
(typecase fd
(fixnum
(let ((flag (sb-sys:wait-until-fd-usable fd :input
(if blocking nil 0))))
(cond
((and (not blocking) (= start end)) (if flag -3 0))
((and (not blocking) (not flag)) 0)
(t (block nil
(let ((count 0))
(declare (type fixnum count))
(tagbody
again
;; Avoid CMUCL gengc write barrier
(do ((i start (+ i (the fixnum #.(sb-posix:getpagesize)))))
((>= i end))
(declare (type fixnum i))
(setf (bref buffer i) 0))
(setf (bref buffer (1- end)) 0)
(multiple-value-bind (bytes errno)
(sb-unix:unix-read fd (buffer-sap buffer start)
(the fixnum (- end start)))
(declare (type (or null fixnum) bytes)
(type (integer 0 100) errno))
(when bytes
(incf count bytes)
(incf start bytes))
(cond ((null bytes)
(format *debug-io* "~&;; UNIX-READ: errno=~D~%" errno)
(cond ((= errno sb-unix:eintr) (go again))
((and blocking
(or (= errno ;;sb-unix:eagain
;; TODO: move
;; eagain into
;; sb-unix
11)
(= errno sb-unix:ewouldblock)))
(sb-sys:wait-until-fd-usable fd :input nil)
(go again))
(t (return (- -10 errno)))))
((zerop count) (return -1))
(t (return count)))))))))))
(t (%read-vector buffer fd start end :byte-8
(if blocking :bnb nil)))))))
(defun write-octets (stream buffer start end blocking)
(declare (type simple-stream stream)
(type simple-stream-buffer buffer)
(type fixnum start)
(type (or null fixnum) end))
(with-stream-class (simple-stream stream)
(when (sm handler stream)
(do ()
((null (sm pending stream)))
(sb-sys:serve-all-events)))
(let ((fd (sm output-handle stream))
(end (or end (length buffer))))
(typecase fd
(fixnum
(let ((flag (sb-sys:wait-until-fd-usable fd :output
(if blocking nil 0))))
(cond
((and (not blocking) (= start end)) (if flag -3 0))
((and (not blocking) (not flag)) 0)
(t
(block nil
(let ((count 0))
(tagbody again
(multiple-value-bind (bytes errno)
(sb-unix:unix-write fd (buffer-sap buffer) start
(- end start))
(when bytes
(incf count bytes)
(incf start bytes))
(cond ((null bytes)
(format *debug-io* "~&;; UNIX-WRITE: errno=~D~%" errno)
(cond ((= errno sb-unix:eintr) (go again))
;; don't block for subsequent chars
(t (return (- -10 errno)))))
(t (return count)))))))))))
(t (error "implement me"))))))
(defun do-some-output (stream)
;; Do some pending output; return T if completed, NIL if more to do
(with-stream-class (simple-stream stream)
(let ((fd (sm output-handle stream)))
(loop
(let ((list (pop (sm pending stream))))
(unless list
(sb-sys:remove-fd-handler (sm handler stream))
(setf (sm handler stream) nil)
(return t))
(let* ((buffer (first list))
(start (second list))
(end (third list))
(len (- end start)))
(declare (type simple-stream-buffer buffer)
(type sb-int:index start end len))
(tagbody again
(multiple-value-bind (bytes errno)
(sb-unix:unix-write fd (buffer-sap buffer) start len)
(cond ((null bytes)
(if (= errno sb-unix:eintr)
(go again)
(progn (push list (sm pending stream))
(return nil))))
((< bytes len)
(setf (second list) (+ start bytes))
(push list (sm pending stream))
(return nil))
((= bytes len)
(free-buffer buffer)))))))))))
(defun queue-write (stream buffer start end)
;; Queue a write; return T if buffer needs changing, NIL otherwise
(declare (type simple-stream stream)
(type simple-stream-buffer buffer)
(type sb-int:index start end))
(with-stream-class (simple-stream stream)
(when (sm handler stream)
(unless (do-some-output stream)
(let ((last (last (sm pending stream))))
(setf (cdr last) (list (list buffer start end)))
(return-from queue-write t))))
(let ((bytes (write-octets stream buffer start end nil)))
(unless (or (= bytes (- end start)) ; completed
(= bytes -3)) ; empty buffer; shouldn't happen
(setf (sm pending stream) (list (list buffer start end)))
(setf (sm handler stream)
(sb-sys:add-fd-handler (sm output-handle stream) :output
(lambda (fd)
(declare (ignore fd))
(do-some-output stream))))
t))))
(defun %fd-open (pathname direction if-exists if-exists-given
if-does-not-exist if-does-not-exist-given)
(declare (type pathname pathname)

View file

@ -1,22 +1,166 @@
;;; -*- lisp -*-
;;;
;;; **********************************************************************
;;; This code was written by Paul Foley and has been placed in the public
;;; domain.
;;;
;;; This code is in the public domain.
;;; Sbcl port by Rudi Schlatte.
;;; The cmucl implementation of simple-streams was done by Paul Foley,
;;; who placed the code in the public domain. Sbcl port by Rudi
;;; Schlatte.
;;;
;;; **********************************************************************
;;;
;;; Macros needed by the simple-streams implementation
;;; This is just for compatibility with Franz demo code
(in-package "SB-SIMPLE-STREAMS")
(defmacro def-stream-class (name superclasses slots &rest options)
`(defclass ,name ,superclasses ,slots ,@options))
;; All known stream flags. Note that the position in the constant
;; list is significant (cf. %flags below).
(sb-int:defconstant-eqx +flag-bits+
'(:simple ; instance is valid
:input :output ; direction
:dual :string ; type of stream
:eof ; latched EOF
:dirty ; output buffer needs write
:interactive) ; interactive stream
#'equal)
(eval-when (:compile-toplevel :load-toplevel :execute)
(defun %flags (flags)
(loop for flag in flags
as pos = (position flag +flag-bits+)
when (eq flag :gray) do
(error "Gray streams not supported.")
if pos
sum (ash 1 pos) into bits
else
collect flag into unused
finally (when unused
(warn "Invalid stream instance flag~P: ~{~S~^, ~}"
(length unused) unused))
(return bits))))
;;; Setup an environment where sm, funcall-stm-handler and
;;; funcall-stm-handler-2 are valid and efficient for a stream of type
;;; class-name or for the stream argument (in which case the
;;; class-name argument is ignored). In nested with-stream-class
;;; forms, the inner with-stream-class form must specify a stream
;;; argument if the outer one specifies one, or the wrong object will
;;; be accessed.
(defmacro with-stream-class ((class-name &optional stream) &body body)
(if stream
(let ((stm (gensym "STREAM"))
(slt (gensym "SV")))
`(let* ((,stm ,stream)
(,slt (sb-kernel:%instance-ref ,stm 1)))
(declare (type ,class-name ,stm)
(type simple-vector ,slt)
(ignorable ,slt))
(macrolet ((sm (slot-name stream)
(declare (ignore stream))
#-count-sm
`(slot-value ,',stm ',slot-name)
#+count-sm
`(%sm ',slot-name ,',stm))
(add-stream-instance-flags (stream &rest flags)
(declare (ignore stream))
`(setf (sm %flags ,',stm) (logior (the fixnum (sm %flags ,',stm))
,(%flags flags))))
(remove-stream-instance-flags (stream &rest flags)
(declare (ignore stream))
`(setf (sm %flags ,',stm) (logandc2 (the fixnum (sm %flags ,',stm))
,(%flags flags))))
(any-stream-instance-flags (stream &rest flags)
(declare (ignore stream))
`(not (zerop (logand (the fixnum (sm %flags ,',stm))
,(%flags flags))))))
,@body)))
`(macrolet ((sm (slot-name stream)
#-count-sm
`(slot-value ,stream ',slot-name)
#+count-sm
`(%sm ',slot-name ,stream)))
,@body)))
(defmacro sm (slot-name stream)
"Access the named slot in Stream."
(warn "Using ~S macro outside ~S." 'sm 'with-stream-class)
`(slot-value ,stream ',slot-name))
(defmacro funcall-stm-handler (slot-name stream &rest args)
"Call the strategy function named by Slot-Name on Stream."
(let ((s (gensym)))
`(let ((,s ,stream))
(funcall (sm ,slot-name ,s) ,s ,@args))))
(defmacro funcall-stm-handler-2 (slot-name arg1 stream &rest args)
"Call the strategy function named by Slot-Name on Stream."
(let ((s (gensym)))
`(let ((,s ,stream))
(funcall (sm ,slot-name ,s) ,arg1 ,s ,@args))))
(defmacro add-stream-instance-flags (stream &rest flags)
"Set the given Flags in Stream."
(let ((s (gensym "STREAM")))
`(let ((,s ,stream))
(with-stream-class (simple-stream ,s)
(add-stream-instance-flags ,s ,@flags)))))
(defmacro remove-stream-instance-flags (stream &rest flags)
"Clear the given Flags in Stream."
(let ((s (gensym "STREAM")))
`(let ((,s ,stream))
(with-stream-class (simple-stream ,s)
(remove-stream-instance-flags ,s ,@flags)))))
(defmacro any-stream-instance-flags (stream &rest flags)
"Determine whether any one of the Flags is set in Stream."
(let ((s (gensym "STREAM")))
`(let ((,s ,stream))
(with-stream-class (simple-stream ,s)
(any-stream-instance-flags ,s ,@flags)))))
(defmacro simple-stream-dispatch (stream single dual string)
(let ((s (gensym "STREAM")))
`(let ((,s ,stream))
(with-stream-class (simple-stream ,s)
(let ((%flags (sm %flags ,s)))
(cond ((zerop (logand %flags ,(%flags '(:string :dual))))
,single)
((zerop (logand %flags ,(%flags '(:string))))
,dual)
(t
,string)))))))
(defmacro simple-stream-dispatch-2 (stream non-string string)
(let ((s (gensym "STREAM")))
`(let ((,s ,stream))
(with-stream-class (simple-stream ,s)
(let ((%flags (sm %flags ,s)))
(cond ((zerop (logand %flags ,(%flags '(:string))))
,non-string)
(t
,string)))))))
;;;; The following two forms are for Franz source-compatibility,
;;;; disabled at the moment.
#+nil
(defpackage "EXCL"
(:use "SB-SIMPLE-STREAM")
(:import-from "SB-SIMPLE-STREAM"
(:use "SB-SIMPLE-STREAMS")
(:import-from "SB-SIMPLE-STREAMS"
"BUFFER" "BUFFPOS" "BUFFER-PTR"
"OUT-BUFFER" "MAX-OUT-POS"
"INPUT-HANDLE" "OUTPUT-HANDLE"
"MELDED-STREAM"
"J-READ-CHARS"))
(use-package "SB-SIMPLE-STREAMS")
#+nil
(provide :iodefs)

View file

@ -0,0 +1,73 @@
;;; -*- lisp -*-
;;;
;;; **********************************************************************
;;; This code was written by Paul Foley and has been placed in the public
;;; domain.
;;;
;;; Sbcl port by Rudi Schlatte.
(in-package "SB-SIMPLE-STREAMS")
;;;
;;; **********************************************************************
;;;
;;; Definition of Null-Simple-Stream
(def-stream-class null-simple-stream (single-channel-simple-stream)
())
(declaim (ftype j-read-char-fn null-read-char))
(defun null-read-char (stream eof-error-p eof-value blocking)
(declare (ignore blocking))
(sb-impl::eof-or-lose stream eof-error-p eof-value))
(declaim (ftype j-read-chars-fn null-read-chars))
(defun null-read-chars (stream string search start end blocking)
(declare (ignore stream string search start end blocking))
(values 0 :eof))
(declaim (ftype j-unread-char-fn null-unread-char))
(defun null-unread-char (stream relaxed)
(declare (ignore stream relaxed)))
(declaim (ftype j-write-char-fn null-write-char))
(defun null-write-char (character stream)
(declare (ignore stream))
character)
(declaim (ftype j-write-chars-fn null-write-chars))
(defun null-write-chars (string stream start end)
(declare (ignore string stream))
(- end start))
(declaim (ftype j-listen-fn null-listen))
(defun null-listen (stream)
(declare (ignore stream))
nil)
(defmethod device-open ((stream null-simple-stream) options)
(with-stream-class (null-simple-stream stream)
(add-stream-instance-flags stream :simple :input :output)
;;(install-single-channel-character-strategy
;; stream (getf options :external-format :default) nil)
(setf (sm j-read-char stream) #'null-read-char
(sm j-read-chars stream) #'null-read-chars
(sm j-unread-char stream) #'null-unread-char
(sm j-write-char stream) #'null-write-char
(sm j-write-chars stream) #'null-write-chars
(sm j-listen stream) #'null-listen))
stream)
(defmethod device-buffer-length ((stream null-simple-stream))
256)
(defmethod device-read ((stream null-simple-stream) buffer
start end blocking)
(declare (ignore buffer start end blocking))
-1)
(defmethod device-write ((stream null-simple-stream) buffer
start end blocking)
(declare (ignore buffer blocking))
(- end start))

View file

@ -8,15 +8,20 @@
:depends-on (sb-bsd-sockets sb-posix)
:components ((:file "package")
(:file "fndb")
(:file "iodefs" :depends-on ("package"))
;;(:file "pcl")
;;(:file "ext-format" :depends-on ("package"))
(:file "classes" :depends-on ("package"))
(:file "classes" :depends-on ("iodefs"))
(:file "internal" :depends-on ("classes"))
(:file "strategy" :depends-on ("internal"))
(:file "cl" :depends-on ("internal" "fndb"))
(:file "simple-streams" :depends-on ("cl" "strategy"))
(:file "impl" :depends-on ("internal" "fndb"))
(:file "file" :depends-on ("strategy"))
(:file "direct" :depends-on ("strategy"))
(:file "null" :depends-on ("strategy"))
(:file "socket" :depends-on ("strategy"))
(:file "string" :depends-on ("strategy"))
(:file "terminal" :depends-on ("strategy"))
;;(:file "gray-compat" :depends-on ("package"))
;;(:file "iodefs" :depends-on ("package"))
))
(defmethod perform :after ((o load-op)

View file

@ -14,7 +14,7 @@
*load-truename*)
"Directory for temporary test files.")
(eval-when (:load-toplevel) (ensure-directories-exist *test-path*))
(eval-when (:load-toplevel) (ensure-directories-exist *test-path* :verbose t))
;;; Non-destructive functional analog of REMF
(defun remove-key (key list)

View file

@ -0,0 +1,94 @@
;;; -*- lisp -*-
;;;
;;; **********************************************************************
;;; This code was written by Paul Foley and has been placed in the public
;;; domain.
;;;
;;; Sbcl port by Rudi Schlatte.
(in-package "SB-SIMPLE-STREAMS")
;;;
;;; **********************************************************************
;;;
;;; Socket-simple-stream and socket-base-simple-stream
(def-stream-class socket-simple-stream (dual-channel-simple-stream)
(;; keep the socket around; it could be handy e.g. for querying peer
;; host/port
(socket :initform nil :type (or sb-bsd-sockets:socket null)
:initarg :socket)))
(def-stream-class socket-base-simple-stream (dual-channel-simple-stream)
())
(defmethod device-open ((stream socket-simple-stream) options)
(let* ((remote-host (getf options :remote-host))
(remote-port (getf options :remote-port))
(socket (make-instance 'sb-bsd-sockets:inet-socket
:type :stream :protocol :tcp)))
(unless (and remote-host remote-port)
(error "~S requires :remote-host and :remote-port arguments"
'socket-simple-stream))
(with-stream-class (socket-simple-stream stream)
(ecase (getf options :direction :input)
(:input (add-stream-instance-flags stream :input))
(:output (add-stream-instance-flags stream :output))
(:io (add-stream-instance-flags stream :input :output)))
(setf (sm socket stream) socket)
(sb-bsd-sockets:socket-connect socket remote-host remote-port)
(let ((fd (sb-bsd-sockets:socket-file-descriptor socket)))
(when fd
(add-stream-instance-flags stream :dual :simple)
(when (any-stream-instance-flags stream :input)
(setf (sm input-handle stream) fd)
(unless (sm buffer stream)
(let ((length (device-buffer-length stream)))
(setf (sm buffer stream) (allocate-buffer length)
(sm buffpos stream) 0
(sm buffer-ptr stream) 0
(sm buf-len stream) length))))
(when (any-stream-instance-flags stream :output)
(setf (sm output-handle stream) fd)
(unless (sm out-buffer stream)
(let ((length (device-buffer-length stream)))
(setf (sm out-buffer stream) (allocate-buffer length)
(sm outpos stream) 0
(sm max-out-pos stream) length)))
(setf (sm control-out stream) *std-control-out-table*))
(sb-ext:cancel-finalization socket)
(sb-ext:finalize stream
(lambda ()
(sb-unix:unix-close fd)
(format *debug-io*
"~&;;; ** closed socket (fd ~D)~%" fd)))
;; this should be done with (setf stream-external-format)
(let ((efmt (getf options :external-format :default)))
(compose-encapsulating-streams stream efmt)
(install-dual-channel-character-strategy (melding-stream stream)
efmt))
stream)))))
(defmethod device-close ((stream socket-simple-stream) abort)
(with-stream-class (socket-simple-stream stream)
(sb-unix:unix-close (or (sm input-handle stream)
(sm output-handle stream)))
(when (sm buffer stream)
(free-buffer (sm buffer stream))
(setf (sm buffer stream) nil))
(when (sm out-buffer stream)
(free-buffer (sm out-buffer stream))
(setf (sm out-buffer stream) nil))
(sb-ext:cancel-finalization stream)
t))
(defmethod device-open ((stream socket-base-simple-stream) options)
#| do something |#
stream)
(defmethod device-write ((stream socket-base-simple-stream) buffer
start end blocking)
;; @@2
(call-next-method))

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,118 @@
;;; -*- lisp -*-
;;;
;;; **********************************************************************
;;; This code was written by Paul Foley and has been placed in the public
;;; domain.
;;;
;;; Sbcl port by Rudi Schlatte.
(in-package "SB-SIMPLE-STREAMS")
;;;
;;; **********************************************************************
;;;
;;; String-Simple-Stream and relatives
(def-stream-class string-input-simple-stream (string-simple-stream)
())
(def-stream-class string-output-simple-stream (string-simple-stream)
((out-buffer :initform nil :type (or simple-stream-buffer null))
(outpos :initform 0 :type fixnum)
(max-out-pos :initform 0 :type fixnum)))
(def-stream-class composing-stream (string-simple-stream)
())
(def-stream-class fill-pointer-output-simple-stream
(string-output-simple-stream)
())
(def-stream-class xp-simple-stream (string-output-simple-stream)
())
(def-stream-class annotation-output-simple-stream (string-output-simple-stream)
())
(defmethod device-open :before ((stream string-input-simple-stream) options)
;; Taken with permission from ftp://ftp.franz.com/pub/duane/Simp-stms.ppt
(with-stream-class (string-input-simple-stream stream)
(let ((string (getf options :string)))
(when (and string (null (sm buffer stream)))
(let ((start (getf options :start))
(end (or (getf options :end) (length string))))
(setf (sm buffer stream) string
(sm buffpos stream) start
(sm buffer-ptr stream) end))))
(install-string-input-character-strategy stream)
(add-stream-instance-flags stream :string :input :simple)))
(defmethod device-open :before ((stream string-output-simple-stream) options)
;; Taken with permission from ftp://ftp.franz.com/pub/duane/Simp-stms.ppt
(with-stream-class (string-output-simple-stream stream)
(unless (sm out-buffer stream)
(let ((string (getf options :string)))
(if string
(setf (sm out-buffer stream) string
(sm max-out-pos stream) (length string))
(let ((buflen (max (device-buffer-length stream) 16)))
(setf (sm out-buffer stream) (make-string buflen)
(sm max-out-pos stream) buflen)))))
(unless (sm control-out stream)
(setf (sm control-out stream) *std-control-out-table*))
(install-string-output-character-strategy stream)
(add-stream-instance-flags stream :string :output :simple)))
(defmethod device-open ((stream string-simple-stream) options)
(declare (ignore options))
(with-stream-class (string-simple-stream stream)
(if (and (any-stream-instance-flags stream :simple)
(any-stream-instance-flags stream :input :output))
t
nil)))
(defmethod device-file-position ((stream string-simple-stream))
(with-stream-class (simple-stream stream)
(sm buffpos stream)))
(defmethod (setf device-file-position) (value (stream string-simple-stream))
(with-stream-class (simple-stream stream)
(cond ((or (> value (sm buffer-ptr stream))
(< value (- -1 (sm buffer-ptr stream))))
nil)
((>= value 0)
(setf (sm buffpos stream) value)
t)
(t
(setf (sm buffpos stream) (+ (sm buffer-ptr stream) value 1))
t))))
(defmethod device-file-length ((stream string-simple-stream))
(with-stream-class (simple-stream stream)
(sm buffer-ptr stream)))
(defmethod device-open ((stream fill-pointer-output-simple-stream) options)
#| do something |#
stream)
(defmethod device-file-position ((stream fill-pointer-output-simple-stream))
(with-stream-class (fill-pointer-output-simple-stream stream)
(fill-pointer (sm out-buffer stream))))
(defmethod (setf device-file-position)
(value (stream fill-pointer-output-simple-stream))
(with-stream-class (fill-pointer-output-simple-stream stream)
(let ((buffer (sm out-buffer stream)))
(cond ((or (> value (array-total-size buffer))
(< value (- -1 (array-total-size buffer))))
nil)
((>= value 0)
(setf (fill-pointer buffer) value))
(t
(setf (fill-pointer buffer)
(+ (array-total-size buffer) value 1)))))))
(defmethod device-open ((stream xp-simple-stream) options)
#| do something |#
stream)

View file

@ -0,0 +1,61 @@
;;; -*- lisp -*-
;;;
;;; **********************************************************************
;;; This code was written by Paul Foley and has been placed in the public
;;; domain.
;;;
;;; Sbcl port by Rudi Schlatte.
(in-package "SB-SIMPLE-STREAMS")
;;;
;;; **********************************************************************
;;;
;;; Terminal-Simple-Stream
(defvar *terminal-control-in-table*
(make-control-table #\Newline #'std-dc-newline-in-handler))
(def-stream-class terminal-simple-stream (dual-channel-simple-stream)
())
(defmethod device-open ((stream terminal-simple-stream) options)
(with-stream-class (terminal-simple-stream stream)
(when (getf options :input-handle)
(setf (sm input-handle stream) (getf options :input-handle))
(add-stream-instance-flags stream :simple :dual :input)
(when (sb-unix:unix-isatty (sm input-handle stream))
(add-stream-instance-flags stream :interactive))
(unless (sm buffer stream)
(let ((length (device-buffer-length stream)))
(setf (sm buffer stream) (allocate-buffer length)
(sm buf-len stream) length)))
(setf (sm control-in stream) *terminal-control-in-table*))
(when (getf options :output-handle)
(setf (sm output-handle stream) (getf options :output-handle))
(add-stream-instance-flags stream :simple :dual :output)
(unless (sm out-buffer stream)
(let ((length (device-buffer-length stream)))
(setf (sm out-buffer stream) (make-string length)
(sm max-out-pos stream) length)))
(setf (sm control-out stream) *std-control-out-table*))
(let ((efmt (getf options :external-format :default)))
(compose-encapsulating-streams stream efmt)
(install-dual-channel-character-strategy
(melding-stream stream) efmt)))
stream)
(defmethod device-read ((stream terminal-simple-stream) buffer
start end blocking)
(let ((result (call-next-method)))
(if (= result -1) -2 result)))
(defmethod device-clear-input ((stream terminal-simple-stream) buffer-only)
(unless buffer-only
(let ((buffer (allocate-buffer sb-impl::bytes-per-buffer)))
(unwind-protect
(loop until (<= (read-octets stream buffer
0 sb-impl::bytes-per-buffer nil)
0))
(free-buffer buffer)))))

View file

@ -17,4 +17,4 @@
;;; checkins which aren't released. (And occasionally for internal
;;; versions, especially for internal versions off the main CVS
;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
"0.8.3.40"
"0.8.3.41"