1.0.30.41: Octets support for ebcdic-us
[sbcl.git] / src / code / fd-stream.lisp
index 6c73160..4c978bc 100644 (file)
 
 (in-package "SB!IMPL")
 
 
 (in-package "SB!IMPL")
 
-;;;; buffer manipulation routines
+;;;; BUFFER
+;;;;
+;;;; Streams hold BUFFER objects, which contain a SAP, size of the
+;;;; memory area the SAP stands for (LENGTH bytes), and HEAD and TAIL
+;;;; indexes which delimit the "valid", or "active" area of the
+;;;; memory. HEAD is inclusive, TAIL is exclusive.
+;;;;
+;;;; Buffers get allocated lazily, and are recycled by returning them
+;;;; to the *AVAILABLE-BUFFERS* list. Every buffer has it's own
+;;;; finalizer, to take care of releasing the SAP memory when a stream
+;;;; is not properly closed.
+;;;;
+;;;; The code aims to provide a limited form of thread and interrupt
+;;;; safety: parallel writes and reads may lose output or input, cause
+;;;; interleaved IO, etc -- but they should not corrupt memory. The
+;;;; key to doing this is to read buffer state once, and update the
+;;;; state based on the read state:
+;;;;
+;;;; (let ((tail (buffer-tail buffer)))
+;;;;   ...
+;;;;   (setf (buffer-tail buffer) (+ tail n)))
+;;;;
+;;;; NOT
+;;;;
+;;;; (let ((tail (buffer-tail buffer)))
+;;;;   ...
+;;;;  (incf (buffer-tail buffer) n))
+;;;;
+
+(declaim (inline buffer-sap buffer-length buffer-head buffer-tail
+                 (setf buffer-head) (setf buffer-tail)))
+(defstruct (buffer (:constructor %make-buffer (sap length)))
+  (sap (missing-arg) :type system-area-pointer :read-only t)
+  (length (missing-arg) :type index :read-only t)
+  (head 0 :type index)
+  (tail 0 :type index))
 
 
-;;; FIXME: Is it really good to maintain this pool separate from the
-;;; GC and the C malloc logic?
 (defvar *available-buffers* ()
   #!+sb-doc
 (defvar *available-buffers* ()
   #!+sb-doc
-  "List of available buffers. Each buffer is an sap pointing to
-  bytes-per-buffer of memory.")
+  "List of available buffers.")
 
 
-#!+sb-thread
-(defvar *available-buffers-mutex* (sb!thread:make-mutex
-                                   :name "lock for *AVAILABLE-BUFFERS*")
+(defvar *available-buffers-spinlock* (sb!thread::make-spinlock
+                                      :name "lock for *AVAILABLE-BUFFERS*")
   #!+sb-doc
   "Mutex for access to *AVAILABLE-BUFFERS*.")
 
 (defmacro with-available-buffers-lock ((&optional) &body body)
   #!+sb-doc
   "Mutex for access to *AVAILABLE-BUFFERS*.")
 
 (defmacro with-available-buffers-lock ((&optional) &body body)
-  ;; WITHOUT-INTERRUPTS because streams are low-level enough to be
-  ;; async signal safe, and in particular a C-c that brings up the
-  ;; debugger while holding the mutex would lose badly
-  `(without-interrupts
-    (sb!thread:with-mutex (*available-buffers-mutex*)
-      ,@body)))
-
-(defconstant bytes-per-buffer (* 4 1024)
+  ;; CALL-WITH-SYSTEM-SPINLOCK because
+  ;;
+  ;; 1. streams are low-level enough to be async signal safe, and in
+  ;;    particular a C-c that brings up the debugger while holding the
+  ;;    mutex would lose badly
+  ;;
+  ;; 2. this can potentially be a fairly busy (but also probably
+  ;;    uncontended) lock, so we don't want to pay the syscall per
+  ;;    release -- hence a spinlock.
+  ;;
+  ;; ...again, once we have smarted locks the spinlock here can become
+  ;; a mutex.
+  `(sb!thread::with-system-spinlock (*available-buffers-spinlock*)
+     ,@body))
+
+(defconstant +bytes-per-buffer+ (* 4 1024)
   #!+sb-doc
   #!+sb-doc
-  "Number of bytes per buffer.")
+  "Default number of bytes per buffer.")
 
 
-;;; Return the next available buffer, creating one if necessary.
-#!-sb-fluid (declaim (inline next-available-buffer))
-(defun next-available-buffer ()
+(defun alloc-buffer (&optional (size +bytes-per-buffer+))
+  ;; Don't want to allocate & unwind before the finalizer is in place.
+  (without-interrupts
+    (let* ((sap (allocate-system-memory size))
+           (buffer (%make-buffer sap size)))
+      (when (zerop (sap-int sap))
+        (error "Could not allocate ~D bytes for buffer." size))
+      (finalize buffer (lambda ()
+                         (deallocate-system-memory sap size))
+                :dont-save t)
+      buffer)))
+
+(defun get-buffer ()
+  ;; Don't go for the lock if there is nothing to be had -- sure,
+  ;; another thread might just release one before we get it, but that
+  ;; is not worth the cost of locking. Also release the lock before
+  ;; allocation, since it's going to take a while.
+  (if *available-buffers*
+      (or (with-available-buffers-lock ()
+            (pop *available-buffers*))
+          (alloc-buffer))
+      (alloc-buffer)))
+
+(declaim (inline reset-buffer))
+(defun reset-buffer (buffer)
+  (setf (buffer-head buffer) 0
+        (buffer-tail buffer) 0)
+  buffer)
+
+(defun release-buffer (buffer)
+  (reset-buffer buffer)
   (with-available-buffers-lock ()
   (with-available-buffers-lock ()
-    (if *available-buffers*
-        (pop *available-buffers*)
-        (allocate-system-memory bytes-per-buffer))))
+    (push buffer *available-buffers*)))
+
+;;; This is a separate buffer management function, as it wants to be
+;;; clever about locking -- grabbing the lock just once.
+(defun release-fd-stream-buffers (fd-stream)
+  (let ((ibuf (fd-stream-ibuf fd-stream))
+        (obuf (fd-stream-obuf fd-stream))
+        (queue (loop for item in (fd-stream-output-queue fd-stream)
+                       when (buffer-p item)
+                       collect (reset-buffer item))))
+    (when ibuf
+      (push (reset-buffer ibuf) queue))
+    (when obuf
+      (push (reset-buffer obuf) queue))
+    ;; ...so, anything found?
+    (when queue
+      ;; detach from stream
+      (setf (fd-stream-ibuf fd-stream) nil
+            (fd-stream-obuf fd-stream) nil
+            (fd-stream-output-queue fd-stream) nil)
+      ;; splice to *available-buffers*
+      (with-available-buffers-lock ()
+        (setf *available-buffers* (nconc queue *available-buffers*))))))
 \f
 ;;;; the FD-STREAM structure
 
 \f
 ;;;; the FD-STREAM structure
 
 
   ;; the input buffer
   (unread nil)
 
   ;; the input buffer
   (unread nil)
-  (ibuf-sap nil :type (or system-area-pointer null))
-  (ibuf-length nil :type (or index null))
-  (ibuf-head 0 :type index)
-  (ibuf-tail 0 :type index)
+  (ibuf nil :type (or buffer null))
 
   ;; the output buffer
 
   ;; the output buffer
-  (obuf-sap nil :type (or system-area-pointer null))
-  (obuf-length nil :type (or index null))
-  (obuf-tail 0 :type index)
+  (obuf nil :type (or buffer null))
 
   ;; output flushed, but not written due to non-blocking io?
 
   ;; output flushed, but not written due to non-blocking io?
-  (output-later nil)
+  (output-queue nil)
   (handler nil)
   ;; timeout specified for this stream as seconds or NIL if none
   (timeout nil :type (or single-float null))
   ;; pathname of the file this stream is opened to (returned by PATHNAME)
   (pathname nil :type (or pathname null))
   (external-format :default)
   (handler nil)
   ;; timeout specified for this stream as seconds or NIL if none
   (timeout nil :type (or single-float null))
   ;; pathname of the file this stream is opened to (returned by PATHNAME)
   (pathname nil :type (or pathname null))
   (external-format :default)
-  (output-bytes #'ill-out :type function))
+  ;; fixed width, or function to call with a character
+  (char-size 1 :type (or fixnum function))
+  (output-bytes #'ill-out :type function)
+  ;; a boolean indicating whether the stream is bivalent.  For
+  ;; internal use only.
+  (bivalent-p nil :type boolean))
 (def!method print-object ((fd-stream fd-stream) stream)
   (declare (type stream stream))
   (print-unreadable-object (fd-stream stream :type t :identity t)
     (format stream "for ~S" (fd-stream-name fd-stream))))
 \f
 (def!method print-object ((fd-stream fd-stream) stream)
   (declare (type stream stream))
   (print-unreadable-object (fd-stream stream :type t :identity t)
     (format stream "for ~S" (fd-stream-name fd-stream))))
 \f
+;;;; CORE OUTPUT FUNCTIONS
+
+;;; Buffer the section of THING delimited by START and END by copying
+;;; to output buffer(s) of stream.
+(defun buffer-output (stream thing start end)
+  (declare (index start end))
+  (when (< end start)
+    (error ":END before :START!"))
+  (when (> end start)
+    ;; Copy bytes from THING to buffers.
+    (flet ((copy-to-buffer (buffer tail count)
+             (declare (buffer buffer) (index tail count))
+             (aver (plusp count))
+             (let ((sap (buffer-sap buffer)))
+               (etypecase thing
+                 (system-area-pointer
+                  (system-area-ub8-copy thing start sap tail count))
+                 ((simple-unboxed-array (*))
+                  (copy-ub8-to-system-area thing start sap tail count))))
+             ;; Not INCF! If another thread has moved tail from under
+             ;; us, we don't want to accidentally increment tail
+             ;; beyond buffer-length.
+             (setf (buffer-tail buffer) (+ count tail))
+             (incf start count)))
+      (tagbody
+         ;; First copy is special: the buffer may already contain
+         ;; something, or be even full.
+         (let* ((obuf (fd-stream-obuf stream))
+                (tail (buffer-tail obuf))
+                (space (- (buffer-length obuf) tail)))
+           (when (plusp space)
+             (copy-to-buffer obuf tail (min space (- end start)))
+             (go :more-output-p)))
+       :flush-and-fill
+         ;; Later copies should always have an empty buffer, since
+         ;; they are freshly flushed, but if another thread is
+         ;; stomping on the same buffer that might not be the case.
+         (let* ((obuf (flush-output-buffer stream))
+                (tail (buffer-tail obuf))
+                (space (- (buffer-length obuf) tail)))
+           (copy-to-buffer obuf tail (min space (- end start))))
+       :more-output-p
+         (when (> end start)
+           (go :flush-and-fill))))))
+
+;;; Flush the current output buffer of the stream, ensuring that the
+;;; new buffer is empty. Returns (for convenience) the new output
+;;; buffer -- which may or may not be EQ to the old one. If the is no
+;;; queued output we try to write the buffer immediately -- otherwise
+;;; we queue it for later.
+(defun flush-output-buffer (stream)
+  (let ((obuf (fd-stream-obuf stream)))
+    (when obuf
+      (let ((head (buffer-head obuf))
+            (tail (buffer-tail obuf)))
+        (cond ((eql head tail)
+               ;; Buffer is already empty -- just ensure that is is
+               ;; set to zero as well.
+               (reset-buffer obuf))
+              ((fd-stream-output-queue stream)
+               ;; There is already stuff on the queue -- go directly
+               ;; there.
+               (aver (< head tail))
+               (%queue-and-replace-output-buffer stream))
+              (t
+               ;; Try a non-blocking write, queue whatever is left over.
+               (aver (< head tail))
+               (synchronize-stream-output stream)
+               (let ((length (- tail head)))
+                 (multiple-value-bind (count errno)
+                     (sb!unix:unix-write (fd-stream-fd stream) (buffer-sap obuf)
+                                         head length)
+                   (cond ((eql count length)
+                          ;; Complete write -- we can use the same buffer.
+                          (reset-buffer obuf))
+                         (count
+                          ;; Partial write -- update buffer status and queue.
+                          ;; Do not use INCF! Another thread might have moved
+                          ;; head...
+                          (setf (buffer-head obuf) (+ count head))
+                          (%queue-and-replace-output-buffer stream))
+                         #!-win32
+                         ((eql errno sb!unix:ewouldblock)
+                          ;; Blocking, queue.
+                          (%queue-and-replace-output-buffer stream))
+                         (t
+                          (simple-stream-perror "Couldn't write to ~s"
+                                                stream errno)))))))))))
+
+;;; Helper for FLUSH-OUTPUT-BUFFER -- returns the new buffer.
+(defun %queue-and-replace-output-buffer (stream)
+  (let ((queue (fd-stream-output-queue stream))
+        (later (list (or (fd-stream-obuf stream) (bug "Missing obuf."))))
+        (new (get-buffer)))
+    ;; Important: before putting the buffer on queue, give the stream
+    ;; a new one. If we get an interrupt and unwind losing the buffer
+    ;; is relatively OK, but having the same buffer in two places
+    ;; would be bad.
+    (setf (fd-stream-obuf stream) new)
+    (cond (queue
+           (nconc queue later))
+          (t
+           (setf (fd-stream-output-queue stream) later)))
+    (unless (fd-stream-handler stream)
+      (setf (fd-stream-handler stream)
+            (add-fd-handler (fd-stream-fd stream)
+                            :output
+                            (lambda (fd)
+                              (declare (ignore fd))
+                              (write-output-from-queue stream)))))
+    new))
+
+;;; This is called by the FD-HANDLER for the stream when output is
+;;; possible.
+(defun write-output-from-queue (stream)
+  (synchronize-stream-output stream)
+  (let (not-first-p)
+    (tagbody
+     :pop-buffer
+       (let* ((buffer (pop (fd-stream-output-queue stream)))
+              (head (buffer-head buffer))
+              (length (- (buffer-tail buffer) head)))
+         (declare (index head length))
+         (aver (>= length 0))
+         (multiple-value-bind (count errno)
+             (sb!unix:unix-write (fd-stream-fd stream) (buffer-sap buffer)
+                                 head length)
+           (cond ((eql count length)
+                  ;; Complete write, see if we can do another right
+                  ;; away, or remove the handler if we're done.
+                  (release-buffer buffer)
+                  (cond ((fd-stream-output-queue stream)
+                         (setf not-first-p t)
+                         (go :pop-buffer))
+                        (t
+                         (let ((handler (fd-stream-handler stream)))
+                           (aver handler)
+                           (setf (fd-stream-handler stream) nil)
+                           (remove-fd-handler handler)))))
+                 (count
+                  ;; Partial write. Update buffer status and requeue.
+                  (aver (< count length))
+                  ;; Do not use INCF! Another thread might have moved head.
+                  (setf (buffer-head buffer) (+ head count))
+                  (push buffer (fd-stream-output-queue stream)))
+                 (not-first-p
+                  ;; We tried to do multiple writes, and finally our
+                  ;; luck ran out. Requeue.
+                  (push buffer (fd-stream-output-queue stream)))
+                 (t
+                  ;; Could not write on the first try at all!
+                  #!+win32
+                  (simple-stream-perror "Couldn't write to ~S." stream errno)
+                  #!-win32
+                  (if (= errno sb!unix:ewouldblock)
+                      (bug "Unexpected blocking in WRITE-OUTPUT-FROM-QUEUE.")
+                      (simple-stream-perror "Couldn't write to ~S"
+                                            stream errno))))))))
+  nil)
+
+;;; Try to write THING directly to STREAM without buffering, if
+;;; possible. If direct write doesn't happen, buffer.
+(defun write-or-buffer-output (stream thing start end)
+  (declare (index start end))
+  (cond ((fd-stream-output-queue stream)
+         (buffer-output stream thing start end))
+        ((< end start)
+         (error ":END before :START!"))
+        ((> end start)
+         (let ((length (- end start)))
+           (synchronize-stream-output stream)
+           (multiple-value-bind (count errno)
+               (sb!unix:unix-write (fd-stream-fd stream) thing start length)
+             (cond ((eql count length)
+                    ;; Complete write -- done!
+                    )
+                   (count
+                    (aver (< count length))
+                    ;; Partial write -- buffer the rest.
+                    (buffer-output stream thing (+ start count) end))
+                   (t
+                    ;; Could not write -- buffer or error.
+                    #!+win32
+                    (simple-stream-perror "couldn't write to ~s" stream errno)
+                    #!-win32
+                    (if (= errno sb!unix:ewouldblock)
+                        (buffer-output stream thing start end)
+                        (simple-stream-perror "couldn't write to ~s" stream errno)))))))))
+
+;;; Deprecated -- can go away after 1.1 or so. Deprecated because
+;;; this is not something we want to export. Nikodemus thinks the
+;;; right thing is to support a low-level non-stream like IO layer,
+;;; akin to java.nio.
+(defun output-raw-bytes (stream thing &optional start end)
+  (write-or-buffer-output stream thing (or start 0) (or end (length thing))))
+
+(define-compiler-macro output-raw-bytes (stream thing &optional start end)
+  (deprecation-warning 'output-raw-bytes)
+  (let ((x (gensym "THING")))
+    `(let ((,x ,thing))
+       (write-or-buffer-output ,stream ,x (or ,start 0) (or ,end (length ,x))))))
+\f
 ;;;; output routines and related noise
 
 (defvar *output-routines* ()
 ;;;; output routines and related noise
 
 (defvar *output-routines* ()
 
 (defun stream-decoding-error (stream octets)
   (error 'stream-decoding-error
 
 (defun stream-decoding-error (stream octets)
   (error 'stream-decoding-error
+         :external-format (stream-external-format stream)
          :stream stream
          ;; FIXME: dunno how to get at OCTETS currently, or even if
          ;; that's the right thing to report.
          :octets octets))
 (defun stream-encoding-error (stream code)
   (error 'stream-encoding-error
          :stream stream
          ;; FIXME: dunno how to get at OCTETS currently, or even if
          ;; that's the right thing to report.
          :octets octets))
 (defun stream-encoding-error (stream code)
   (error 'stream-encoding-error
+         :external-format (stream-external-format stream)
          :stream stream
          :code code))
 
          :stream stream
          :code code))
 
 (defun stream-decoding-error-and-handle (stream octet-count)
   (restart-case
       (stream-decoding-error stream
 (defun stream-decoding-error-and-handle (stream octet-count)
   (restart-case
       (stream-decoding-error stream
-                             (let ((sap (fd-stream-ibuf-sap stream))
-                                   (head (fd-stream-ibuf-head stream)))
+                             (let* ((buffer (fd-stream-ibuf stream))
+                                    (sap (buffer-sap buffer))
+                                    (head (buffer-head buffer)))
                                (loop for i from 0 below octet-count
                                      collect (sap-ref-8 sap (+ head i)))))
     (attempt-resync ()
       :report (lambda (stream)
                 (format stream
                                (loop for i from 0 below octet-count
                                      collect (sap-ref-8 sap (+ head i)))))
     (attempt-resync ()
       :report (lambda (stream)
                 (format stream
-                        "~@<Attempt to resync the stream at a character ~
+                        "~@<Attempt to resync the stream at a ~
                         character boundary and continue.~@:>"))
       (fd-stream-resync stream)
       nil)
                         character boundary and continue.~@:>"))
       (fd-stream-resync stream)
       nil)
       (stream-decoding-error stream octet-count)
       (c-string-decoding-error stream octet-count)))
 
       (stream-decoding-error stream octet-count)
       (c-string-decoding-error stream octet-count)))
 
-;;; This is called by the server when we can write to the given file
-;;; descriptor. Attempt to write the data again. If it worked, remove
-;;; the data from the OUTPUT-LATER list. If it didn't work, something
-;;; is wrong.
-(defun frob-output-later (stream)
-  (let* ((stuff (pop (fd-stream-output-later stream)))
-         (base (car stuff))
-         (start (cadr stuff))
-         (end (caddr stuff))
-         (reuse-sap (cadddr stuff))
-         (length (- end start)))
-    (declare (type index start end length))
-    (multiple-value-bind (count errno)
-        (sb!unix:unix-write (fd-stream-fd stream)
-                            base
-                            start
-                            length)
-      (cond ((not count)
-             #!+win32
-             (simple-stream-perror "couldn't write to ~S" stream errno)
-             #!-win32
-             (if (= errno sb!unix:ewouldblock)
-                 (error "Write would have blocked, but SERVER told us to go.")
-                 (simple-stream-perror "couldn't write to ~S" stream errno)))
-            ((eql count length) ; Hot damn, it worked.
-             (when reuse-sap
-               (with-available-buffers-lock ()
-                 (push base *available-buffers*))))
-            ((not (null count)) ; sorta worked..
-             (push (list base
-                         (the index (+ start count))
-                         end)
-                   (fd-stream-output-later stream))))))
-  (unless (fd-stream-output-later stream)
-    (remove-fd-handler (fd-stream-handler stream))
-    (setf (fd-stream-handler stream) nil)))
-
-;;; Arange to output the string when we can write on the file descriptor.
-(defun output-later (stream base start end reuse-sap)
-  (cond ((null (fd-stream-output-later stream))
-         (setf (fd-stream-output-later stream)
-               (list (list base start end reuse-sap)))
-         (setf (fd-stream-handler stream)
-               (add-fd-handler (fd-stream-fd stream)
-                                      :output
-                                      (lambda (fd)
-                                        (declare (ignore fd))
-                                        (frob-output-later stream)))))
-        (t
-         (nconc (fd-stream-output-later stream)
-                (list (list base start end reuse-sap)))))
-  (when reuse-sap
-    (let ((new-buffer (next-available-buffer)))
-      (setf (fd-stream-obuf-sap stream) new-buffer)
-      (setf (fd-stream-obuf-length stream) bytes-per-buffer))))
-
-;;; Output the given noise. Check to see whether there are any pending
-;;; writes. If so, just queue this one. Otherwise, try to write it. If
-;;; this would block, queue it.
-(defun frob-output (stream base start end reuse-sap)
-  (declare (type fd-stream stream)
-           (type (or system-area-pointer (simple-array * (*))) base)
-           (type index start end))
-  (if (not (null (fd-stream-output-later stream))) ; something buffered.
-      (output-later stream base start end reuse-sap)
-      ;; ### check to see whether any of this noise can be output
-      (let ((length (- end start)))
-        (multiple-value-bind (count errno)
-            (sb!unix:unix-write (fd-stream-fd stream) base start length)
-          (cond ((not count)
-                 #!+win32
-                 (simple-stream-perror "Couldn't write to ~S" stream errno)
-                 #!-win32
-                 (if (= errno sb!unix:ewouldblock)
-                     (output-later stream base start end reuse-sap)
-                     (simple-stream-perror "Couldn't write to ~S"
-                                           stream errno)))
-                ((not (eql count length))
-                 (output-later stream base (the index (+ start count))
-                               end reuse-sap)))))))
-
-;;; Flush any data in the output buffer.
-(defun flush-output-buffer (stream)
-  (let ((length (fd-stream-obuf-tail stream)))
-    (unless (= length 0)
-      (frob-output stream (fd-stream-obuf-sap stream) 0 length t)
-      (setf (fd-stream-obuf-tail stream) 0))))
+(defun synchronize-stream-output (stream)
+  ;; If we're reading and writing on the same file, flush buffered
+  ;; input and rewind file position accordingly.
+  (unless (fd-stream-dual-channel-p stream)
+    (let ((adjust (nth-value 1 (flush-input-buffer stream))))
+      (unless (eql 0 adjust)
+        (sb!unix:unix-lseek (fd-stream-fd stream) (- adjust) sb!unix:l_incr)))))
 
 (defun fd-stream-output-finished-p (stream)
 
 (defun fd-stream-output-finished-p (stream)
-  (and (zerop (fd-stream-obuf-tail stream))
-       (not (fd-stream-output-later stream))))
+  (let ((obuf (fd-stream-obuf stream)))
+    (or (not obuf)
+        (and (zerop (buffer-tail obuf))
+             (not (fd-stream-output-queue stream))))))
 
 (defmacro output-wrapper/variable-width ((stream size buffering restart)
                                          &body body)
 
 (defmacro output-wrapper/variable-width ((stream size buffering restart)
                                          &body body)
-  (let ((stream-var (gensym)))
-    `(let ((,stream-var ,stream)
-           (size ,size))
+  (let ((stream-var (gensym "STREAM")))
+    `(let* ((,stream-var ,stream)
+            (obuf (fd-stream-obuf ,stream-var))
+            (tail (buffer-tail obuf))
+            (size ,size))
       ,(unless (eq (car buffering) :none)
       ,(unless (eq (car buffering) :none)
-         `(when (< (fd-stream-obuf-length ,stream-var)
-                   (+ (fd-stream-obuf-tail ,stream-var)
-                       size))
-            (flush-output-buffer ,stream-var)))
+         `(when (<= (buffer-length obuf) (+ tail size))
+            (setf obuf (flush-output-buffer ,stream-var)
+                  tail (buffer-tail obuf))))
       ,(unless (eq (car buffering) :none)
       ,(unless (eq (car buffering) :none)
-         `(when (and (not (fd-stream-dual-channel-p ,stream-var))
-                     (> (fd-stream-ibuf-tail ,stream-var)
-                        (fd-stream-ibuf-head ,stream-var)))
-            (file-position ,stream-var (file-position ,stream-var))))
+         ;; FIXME: Why this here? Doesn't seem necessary.
+         `(synchronize-stream-output ,stream-var))
       ,(if restart
            `(catch 'output-nothing
               ,@body
       ,(if restart
            `(catch 'output-nothing
               ,@body
-              (incf (fd-stream-obuf-tail ,stream-var) size))
+              (setf (buffer-tail obuf) (+ tail size)))
            `(progn
              ,@body
            `(progn
              ,@body
-             (incf (fd-stream-obuf-tail ,stream-var) size)))
+             (setf (buffer-tail obuf) (+ tail size))))
       ,(ecase (car buffering)
          (:none
           `(flush-output-buffer ,stream-var))
          (:line
       ,(ecase (car buffering)
          (:none
           `(flush-output-buffer ,stream-var))
          (:line
-          `(when (eq (char-code byte) (char-code #\Newline))
+          `(when (eql byte #\Newline)
              (flush-output-buffer ,stream-var)))
          (:full))
     (values))))
 
 (defmacro output-wrapper ((stream size buffering restart) &body body)
              (flush-output-buffer ,stream-var)))
          (:full))
     (values))))
 
 (defmacro output-wrapper ((stream size buffering restart) &body body)
-  (let ((stream-var (gensym)))
-    `(let ((,stream-var ,stream))
+  (let ((stream-var (gensym "STREAM")))
+    `(let* ((,stream-var ,stream)
+            (obuf (fd-stream-obuf ,stream-var))
+            (tail (buffer-tail obuf)))
       ,(unless (eq (car buffering) :none)
       ,(unless (eq (car buffering) :none)
-         `(when (< (fd-stream-obuf-length ,stream-var)
-                   (+ (fd-stream-obuf-tail ,stream-var)
-                       ,size))
-            (flush-output-buffer ,stream-var)))
+         `(when (<= (buffer-length obuf) (+ tail ,size))
+            (setf obuf (flush-output-buffer ,stream-var)
+                  tail (buffer-tail obuf))))
+      ;; FIXME: Why this here? Doesn't seem necessary.
       ,(unless (eq (car buffering) :none)
       ,(unless (eq (car buffering) :none)
-         `(when (and (not (fd-stream-dual-channel-p ,stream-var))
-                     (> (fd-stream-ibuf-tail ,stream-var)
-                        (fd-stream-ibuf-head ,stream-var)))
-            (file-position ,stream-var (file-position ,stream-var))))
+         `(synchronize-stream-output ,stream-var))
       ,(if restart
            `(catch 'output-nothing
               ,@body
       ,(if restart
            `(catch 'output-nothing
               ,@body
-              (incf (fd-stream-obuf-tail ,stream-var) ,size))
+              (setf (buffer-tail obuf) (+ tail ,size)))
            `(progn
              ,@body
            `(progn
              ,@body
-             (incf (fd-stream-obuf-tail ,stream-var) ,size)))
+             (setf (buffer-tail obuf) (+ tail ,size))))
       ,(ecase (car buffering)
          (:none
           `(flush-output-buffer ,stream-var))
          (:line
       ,(ecase (car buffering)
          (:none
           `(flush-output-buffer ,stream-var))
          (:line
-          `(when (eq (char-code byte) (char-code #\Newline))
+          `(when (eql byte #\Newline)
              (flush-output-buffer ,stream-var)))
          (:full))
     (values))))
              (flush-output-buffer ,stream-var)))
          (:full))
     (values))))
                       (:none character)
                       (:line character)
                       (:full character))
                       (:none character)
                       (:line character)
                       (:full character))
-  (if (char= byte #\Newline)
+  (if (eql byte #\Newline)
       (setf (fd-stream-char-pos stream) 0)
       (incf (fd-stream-char-pos stream)))
       (setf (fd-stream-char-pos stream) 0)
       (incf (fd-stream-char-pos stream)))
-  (setf (sap-ref-8 (fd-stream-obuf-sap stream) (fd-stream-obuf-tail stream))
+  (setf (sap-ref-8 (buffer-sap obuf) tail)
         (char-code byte)))
 
 (def-output-routines ("OUTPUT-UNSIGNED-BYTE-~A-BUFFERED"
         (char-code byte)))
 
 (def-output-routines ("OUTPUT-UNSIGNED-BYTE-~A-BUFFERED"
                       nil
                       (:none (unsigned-byte 8))
                       (:full (unsigned-byte 8)))
                       nil
                       (:none (unsigned-byte 8))
                       (:full (unsigned-byte 8)))
-  (setf (sap-ref-8 (fd-stream-obuf-sap stream) (fd-stream-obuf-tail stream))
+  (setf (sap-ref-8 (buffer-sap obuf) tail)
         byte))
 
 (def-output-routines ("OUTPUT-SIGNED-BYTE-~A-BUFFERED"
         byte))
 
 (def-output-routines ("OUTPUT-SIGNED-BYTE-~A-BUFFERED"
                       nil
                       (:none (signed-byte 8))
                       (:full (signed-byte 8)))
                       nil
                       (:none (signed-byte 8))
                       (:full (signed-byte 8)))
-  (setf (signed-sap-ref-8 (fd-stream-obuf-sap stream)
-                          (fd-stream-obuf-tail stream))
+  (setf (signed-sap-ref-8 (buffer-sap obuf) tail)
         byte))
 
 (def-output-routines ("OUTPUT-UNSIGNED-SHORT-~A-BUFFERED"
         byte))
 
 (def-output-routines ("OUTPUT-UNSIGNED-SHORT-~A-BUFFERED"
                       nil
                       (:none (unsigned-byte 16))
                       (:full (unsigned-byte 16)))
                       nil
                       (:none (unsigned-byte 16))
                       (:full (unsigned-byte 16)))
-  (setf (sap-ref-16 (fd-stream-obuf-sap stream) (fd-stream-obuf-tail stream))
+  (setf (sap-ref-16 (buffer-sap obuf) tail)
         byte))
 
 (def-output-routines ("OUTPUT-SIGNED-SHORT-~A-BUFFERED"
         byte))
 
 (def-output-routines ("OUTPUT-SIGNED-SHORT-~A-BUFFERED"
                       nil
                       (:none (signed-byte 16))
                       (:full (signed-byte 16)))
                       nil
                       (:none (signed-byte 16))
                       (:full (signed-byte 16)))
-  (setf (signed-sap-ref-16 (fd-stream-obuf-sap stream)
-                           (fd-stream-obuf-tail stream))
+  (setf (signed-sap-ref-16 (buffer-sap obuf) tail)
         byte))
 
 (def-output-routines ("OUTPUT-UNSIGNED-LONG-~A-BUFFERED"
         byte))
 
 (def-output-routines ("OUTPUT-UNSIGNED-LONG-~A-BUFFERED"
                       nil
                       (:none (unsigned-byte 32))
                       (:full (unsigned-byte 32)))
                       nil
                       (:none (unsigned-byte 32))
                       (:full (unsigned-byte 32)))
-  (setf (sap-ref-32 (fd-stream-obuf-sap stream) (fd-stream-obuf-tail stream))
+  (setf (sap-ref-32 (buffer-sap obuf) tail)
         byte))
 
 (def-output-routines ("OUTPUT-SIGNED-LONG-~A-BUFFERED"
         byte))
 
 (def-output-routines ("OUTPUT-SIGNED-LONG-~A-BUFFERED"
                       nil
                       (:none (signed-byte 32))
                       (:full (signed-byte 32)))
                       nil
                       (:none (signed-byte 32))
                       (:full (signed-byte 32)))
-  (setf (signed-sap-ref-32 (fd-stream-obuf-sap stream)
-                           (fd-stream-obuf-tail stream))
+  (setf (signed-sap-ref-32 (buffer-sap obuf) tail)
         byte))
 
 #+#.(cl:if (cl:= sb!vm:n-word-bits 64) '(and) '(or))
         byte))
 
 #+#.(cl:if (cl:= sb!vm:n-word-bits 64) '(and) '(or))
                         nil
                         (:none (unsigned-byte 64))
                         (:full (unsigned-byte 64)))
                         nil
                         (:none (unsigned-byte 64))
                         (:full (unsigned-byte 64)))
-    (setf (sap-ref-64 (fd-stream-obuf-sap stream) (fd-stream-obuf-tail stream))
+    (setf (sap-ref-64 (buffer-sap obuf) tail)
           byte))
   (def-output-routines ("OUTPUT-SIGNED-LONG-LONG-~A-BUFFERED"
                         8
                         nil
                         (:none (signed-byte 64))
                         (:full (signed-byte 64)))
           byte))
   (def-output-routines ("OUTPUT-SIGNED-LONG-LONG-~A-BUFFERED"
                         8
                         nil
                         (:none (signed-byte 64))
                         (:full (signed-byte 64)))
-    (setf (signed-sap-ref-64 (fd-stream-obuf-sap stream)
-                             (fd-stream-obuf-tail stream))
+    (setf (signed-sap-ref-64 (buffer-sap obuf) tail)
           byte)))
 
           byte)))
 
-;;; Do the actual output. If there is space to buffer the string,
-;;; buffer it. If the string would normally fit in the buffer, but
-;;; doesn't because of other stuff in the buffer, flush the old noise
-;;; out of the buffer and put the string in it. Otherwise we have a
-;;; very long string, so just send it directly (after flushing the
-;;; buffer, of course).
-(defun output-raw-bytes (fd-stream thing &optional start end)
-  #!+sb-doc
-  "Output THING to FD-STREAM. THING can be any kind of vector or a SAP. If
-  THING is a SAP, END must be supplied (as length won't work)."
-  (let ((start (or start 0))
-        (end (or end (length (the (simple-array * (*)) thing)))))
-    (declare (type index start end))
-    (when (and (not (fd-stream-dual-channel-p fd-stream))
-               (> (fd-stream-ibuf-tail fd-stream)
-                  (fd-stream-ibuf-head fd-stream)))
-      (file-position fd-stream (file-position fd-stream)))
-    (let* ((len (fd-stream-obuf-length fd-stream))
-           (tail (fd-stream-obuf-tail fd-stream))
-           (space (- len tail))
-           (bytes (- end start))
-           (newtail (+ tail bytes)))
-      (cond ((minusp bytes) ; error case
-             (error ":END before :START!"))
-            ((zerop bytes)) ; easy case
-            ((<= bytes space)
-             (if (system-area-pointer-p thing)
-                 (system-area-ub8-copy thing start
-                                       (fd-stream-obuf-sap fd-stream)
-                                       tail
-                                       bytes)
-                 ;; FIXME: There should be some type checking somewhere to
-                 ;; verify that THING here is a vector, not just <not a SAP>.
-                 (copy-ub8-to-system-area thing start
-                                          (fd-stream-obuf-sap fd-stream)
-                                          tail
-                                          bytes))
-             (setf (fd-stream-obuf-tail fd-stream) newtail))
-            ((<= bytes len)
-             (flush-output-buffer fd-stream)
-             (if (system-area-pointer-p thing)
-                 (system-area-ub8-copy thing
-                                       start
-                                       (fd-stream-obuf-sap fd-stream)
-                                       0
-                                       bytes)
-                 ;; FIXME: There should be some type checking somewhere to
-                 ;; verify that THING here is a vector, not just <not a SAP>.
-                 (copy-ub8-to-system-area thing
-                                          start
-                                          (fd-stream-obuf-sap fd-stream)
-                                          0
-                                          bytes))
-             (setf (fd-stream-obuf-tail fd-stream) bytes))
-            (t
-             (flush-output-buffer fd-stream)
-             (frob-output fd-stream thing start end nil))))))
-
 ;;; the routine to use to output a string. If the stream is
 ;;; unbuffered, slam the string down the file descriptor, otherwise
 ;;; use OUTPUT-RAW-BYTES to buffer the string. Update charpos by
 ;;; the routine to use to output a string. If the stream is
 ;;; unbuffered, slam the string down the file descriptor, otherwise
 ;;; use OUTPUT-RAW-BYTES to buffer the string. Update charpos by
                (eq (fd-stream-external-format stream) :latin-1))
           (ecase (fd-stream-buffering stream)
             (:full
                (eq (fd-stream-external-format stream) :latin-1))
           (ecase (fd-stream-buffering stream)
             (:full
-             (output-raw-bytes stream thing start end))
+             (buffer-output stream thing start end))
             (:line
             (:line
-             (output-raw-bytes stream thing start end)
+             (buffer-output stream thing start end)
              (when last-newline
                (flush-output-buffer stream)))
             (:none
              (when last-newline
                (flush-output-buffer stream)))
             (:none
-             (frob-output stream thing start end nil)))
+             (write-or-buffer-output stream thing start end)))
           (ecase (fd-stream-buffering stream)
             (:full (funcall (fd-stream-output-bytes stream)
                             stream thing nil start end))
           (ecase (fd-stream-buffering stream)
             (:full (funcall (fd-stream-output-bytes stream)
                             stream thing nil start end))
                  (lambda (stream byte)
                    (output-wrapper (stream (/ i 8) (:none) nil)
                      (loop for j from 0 below (/ i 8)
                  (lambda (stream byte)
                    (output-wrapper (stream (/ i 8) (:none) nil)
                      (loop for j from 0 below (/ i 8)
-                           do (setf (sap-ref-8
-                                     (fd-stream-obuf-sap stream)
-                                     (+ j (fd-stream-obuf-tail stream)))
+                           do (setf (sap-ref-8 (buffer-sap obuf)
+                                               (+ j tail))
                                     (ldb (byte 8 (- i 8 (* j 8))) byte))))))
                 (:full
                  (lambda (stream byte)
                    (output-wrapper (stream (/ i 8) (:full) nil)
                      (loop for j from 0 below (/ i 8)
                                     (ldb (byte 8 (- i 8 (* j 8))) byte))))))
                 (:full
                  (lambda (stream byte)
                    (output-wrapper (stream (/ i 8) (:full) nil)
                      (loop for j from 0 below (/ i 8)
-                           do (setf (sap-ref-8
-                                     (fd-stream-obuf-sap stream)
-                                     (+ j (fd-stream-obuf-tail stream)))
+                           do (setf (sap-ref-8 (buffer-sap obuf)
+                                               (+ j tail))
                                     (ldb (byte 8 (- i 8 (* j 8))) byte)))))))
               `(unsigned-byte ,i)
               (/ i 8))))
                                     (ldb (byte 8 (- i 8 (* j 8))) byte)))))))
               `(unsigned-byte ,i)
               (/ i 8))))
                  (lambda (stream byte)
                    (output-wrapper (stream (/ i 8) (:none) nil)
                      (loop for j from 0 below (/ i 8)
                  (lambda (stream byte)
                    (output-wrapper (stream (/ i 8) (:none) nil)
                      (loop for j from 0 below (/ i 8)
-                           do (setf (sap-ref-8
-                                     (fd-stream-obuf-sap stream)
-                                     (+ j (fd-stream-obuf-tail stream)))
+                           do (setf (sap-ref-8 (buffer-sap obuf)
+                                               (+ j tail))
                                     (ldb (byte 8 (- i 8 (* j 8))) byte))))))
                 (:full
                  (lambda (stream byte)
                    (output-wrapper (stream (/ i 8) (:full) nil)
                      (loop for j from 0 below (/ i 8)
                                     (ldb (byte 8 (- i 8 (* j 8))) byte))))))
                 (:full
                  (lambda (stream byte)
                    (output-wrapper (stream (/ i 8) (:full) nil)
                      (loop for j from 0 below (/ i 8)
-                           do (setf (sap-ref-8
-                                     (fd-stream-obuf-sap stream)
-                                     (+ j (fd-stream-obuf-tail stream)))
+                           do (setf (sap-ref-8 (buffer-sap obuf)
+                                               (+ j tail))
                                     (ldb (byte 8 (- i 8 (* j 8))) byte)))))))
               `(signed-byte ,i)
               (/ i 8)))))
                                     (ldb (byte 8 (- i 8 (* j 8))) byte)))))))
               `(signed-byte ,i)
               (/ i 8)))))
 ;;; correct on win32.  However, none of the places that use it require
 ;;; further assurance than "may" versus "will definitely not".
 (defun sysread-may-block-p (stream)
 ;;; correct on win32.  However, none of the places that use it require
 ;;; further assurance than "may" versus "will definitely not".
 (defun sysread-may-block-p (stream)
-  #+win32
+  #!+win32
   ;; This answers T at EOF on win32, I think.
   (not (sb!win32:fd-listen (fd-stream-fd stream)))
   ;; This answers T at EOF on win32, I think.
   (not (sb!win32:fd-listen (fd-stream-fd stream)))
-  #-win32
+  #!-win32
   (sb!unix:with-restarted-syscall (count errno)
     (sb!alien:with-alien ((read-fds (sb!alien:struct sb!unix:fd-set)))
       (sb!unix:fd-zero read-fds)
   (sb!unix:with-restarted-syscall (count errno)
     (sb!alien:with-alien ((read-fds (sb!alien:struct sb!unix:fd-set)))
       (sb!unix:fd-zero read-fds)
 ;;; If the read would block wait (using SERVE-EVENT) till input is available,
 ;;; then fill the input buffer, and return the number of bytes read. Throws
 ;;; to EOF-INPUT-CATCHER if the eof was reached.
 ;;; If the read would block wait (using SERVE-EVENT) till input is available,
 ;;; then fill the input buffer, and return the number of bytes read. Throws
 ;;; to EOF-INPUT-CATCHER if the eof was reached.
-(defun refill-buffer/fd (stream)
-  (let ((fd (fd-stream-fd stream))
-        (errno 0)
-        (count 0))
+(defun refill-input-buffer (stream)
+  (dx-let ((fd (fd-stream-fd stream))
+           (errno 0)
+           (count 0))
     (tagbody
        ;; Check for blocking input before touching the stream, as if
        ;; we happen to wait we are liable to be interrupted, and the
     (tagbody
        ;; Check for blocking input before touching the stream, as if
        ;; we happen to wait we are liable to be interrupted, and the
        ;; Since the read should not block, we'll disable the
        ;; interrupts here, so that we don't accidentally unwind and
        ;; leave the stream in an inconsistent state.
        ;; Since the read should not block, we'll disable the
        ;; interrupts here, so that we don't accidentally unwind and
        ;; leave the stream in an inconsistent state.
-       (without-interrupts
-         (let ((ibuf-sap (fd-stream-ibuf-sap stream))
-               (buflen (fd-stream-ibuf-length stream))
-               (head (fd-stream-ibuf-head stream))
-               (tail (fd-stream-ibuf-tail stream)))
-           (declare (type index head tail))
-           ;; Check the SAP: if it is null, then someone has closed
-           ;; the stream from underneath us. This is not ment to fix
-           ;; multithreaded races, but to deal with interrupt handlers
-           ;; closing the stream.
-           (unless ibuf-sap
-             (go :closed-flame))
-           (unless (zerop head)
-             (cond ((eql head tail)
-                    (setf head 0
-                          tail 0
-                          (fd-stream-ibuf-head stream) 0
-                          (fd-stream-ibuf-tail stream) 0))
-                   (t
-                    (decf tail head)
-                    (system-area-ub8-copy ibuf-sap head
-                                          ibuf-sap 0 tail)
-                    (setf head 0
-                          (fd-stream-ibuf-head stream) 0
-                          (fd-stream-ibuf-tail stream) tail))))
-           (setf (fd-stream-listen stream) nil)
-           (setf (values count errno)
-                 (sb!unix:unix-read fd (int-sap (+ (sap-int ibuf-sap) tail))
-                                    (- buflen tail)))
-           (cond ((null count)
-                  #!+win32
-                  (go :read-error)
-                  #!-win32
-                  (if (eql errno sb!unix:ewouldblock)
-                      (go :wait-for-input)
-                      (go :read-error)))
-                 ((zerop count)
-                  (setf (fd-stream-listen stream) :eof)
-                  (/show0 "THROWing EOF-INPUT-CATCHER")
-                  (throw 'eof-input-catcher nil))
-                 (t
-                  ;; Success!
-                  (incf (fd-stream-ibuf-tail stream) count))))))
+
+       ;; Execute the nlx outside without-interrupts to ensure the
+       ;; resulting thunk is stack-allocatable.
+       ((lambda (return-reason)
+          (ecase return-reason
+            ((nil))             ; fast path normal cases
+            ((:wait-for-input) (go :wait-for-input))
+            ((:closed-flame)   (go :closed-flame))
+            ((:read-error)     (go :read-error))))
+        (without-interrupts
+          ;; Check the buffer: if it is null, then someone has closed
+          ;; the stream from underneath us. This is not ment to fix
+          ;; multithreaded races, but to deal with interrupt handlers
+          ;; closing the stream.
+          (block nil
+            (prog1 nil
+              (let* ((ibuf (or (fd-stream-ibuf stream) (return :closed-flame)))
+                     (sap (buffer-sap ibuf))
+                     (length (buffer-length ibuf))
+                     (head (buffer-head ibuf))
+                     (tail (buffer-tail ibuf)))
+                (declare (index length head tail)
+                         (inline sb!unix:unix-read))
+                (unless (zerop head)
+                  (cond ((eql head tail)
+                         ;; Buffer is empty, but not at yet reset -- make it so.
+                         (setf head 0
+                               tail 0)
+                         (reset-buffer ibuf))
+                        (t
+                         ;; Buffer has things in it, but they are not at the
+                         ;; head -- move them there.
+                         (let ((n (- tail head)))
+                           (system-area-ub8-copy sap head sap 0 n)
+                           (setf head 0
+                                 (buffer-head ibuf) head
+                                 tail n
+                                 (buffer-tail ibuf) tail)))))
+                (setf (fd-stream-listen stream) nil)
+                (setf (values count errno)
+                      (sb!unix:unix-read fd (sap+ sap tail) (- length tail)))
+                (cond ((null count)
+                       #!+win32
+                       (return :read-error)
+                       #!-win32
+                       (if (eql errno sb!unix:ewouldblock)
+                           (return :wait-for-input)
+                           (return :read-error)))
+                      ((zerop count)
+                       (setf (fd-stream-listen stream) :eof)
+                       (/show0 "THROWing EOF-INPUT-CATCHER")
+                       (throw 'eof-input-catcher nil))
+                      (t
+                       ;; Success! (Do not use INCF, for sake of other threads.)
+                       (setf (buffer-tail ibuf) (+ count tail))))))))))
     count))
 
 ;;; Make sure there are at least BYTES number of bytes in the input
     count))
 
 ;;; Make sure there are at least BYTES number of bytes in the input
-;;; buffer. Keep calling REFILL-BUFFER/FD until that condition is met.
+;;; buffer. Keep calling REFILL-INPUT-BUFFER until that condition is met.
 (defmacro input-at-least (stream bytes)
 (defmacro input-at-least (stream bytes)
-  (let ((stream-var (gensym))
-        (bytes-var (gensym)))
-    `(let ((,stream-var ,stream)
-           (,bytes-var ,bytes))
+  (let ((stream-var (gensym "STREAM"))
+        (bytes-var (gensym "BYTES"))
+        (buffer-var (gensym "IBUF")))
+    `(let* ((,stream-var ,stream)
+            (,bytes-var ,bytes)
+            (,buffer-var (fd-stream-ibuf ,stream-var)))
        (loop
        (loop
-         (when (>= (- (fd-stream-ibuf-tail ,stream-var)
-                      (fd-stream-ibuf-head ,stream-var))
+         (when (>= (- (buffer-tail ,buffer-var)
+                      (buffer-head ,buffer-var))
                    ,bytes-var)
            (return))
                    ,bytes-var)
            (return))
-         (refill-buffer/fd ,stream-var)))))
+         (refill-input-buffer ,stream-var)))))
 
 (defmacro input-wrapper/variable-width ((stream bytes eof-error eof-value)
                                         &body read-forms)
 
 (defmacro input-wrapper/variable-width ((stream bytes eof-error eof-value)
                                         &body read-forms)
-  (let ((stream-var (gensym))
-        (retry-var (gensym))
-        (element-var (gensym)))
-    `(let ((,stream-var ,stream)
-           (size nil))
+  (let ((stream-var (gensym "STREAM"))
+        (retry-var (gensym "RETRY"))
+        (element-var (gensym "ELT")))
+    `(let* ((,stream-var ,stream)
+            (ibuf (fd-stream-ibuf ,stream-var))
+            (size nil))
        (if (fd-stream-unread ,stream-var)
            (prog1
                (fd-stream-unread ,stream-var)
        (if (fd-stream-unread ,stream-var)
            (prog1
                (fd-stream-unread ,stream-var)
                      (setf decode-break-reason
                            (block decode-break-reason
                              (input-at-least ,stream-var 1)
                      (setf decode-break-reason
                            (block decode-break-reason
                              (input-at-least ,stream-var 1)
-                             (let* ((byte (sap-ref-8 (fd-stream-ibuf-sap
-                                                      ,stream-var)
-                                                     (fd-stream-ibuf-head
-                                                      ,stream-var))))
+                             (let* ((byte (sap-ref-8 (buffer-sap ibuf)
+                                                     (buffer-head ibuf))))
                                (declare (ignorable byte))
                                (setq size ,bytes)
                                (input-at-least ,stream-var size)
                                (declare (ignorable byte))
                                (setq size ,bytes)
                                (input-at-least ,stream-var size)
                        (stream-decoding-error-and-handle stream
                                                          decode-break-reason))
                      t)
                        (stream-decoding-error-and-handle stream
                                                          decode-break-reason))
                      t)
-                 (let ((octet-count (- (fd-stream-ibuf-tail ,stream-var)
-                                      (fd-stream-ibuf-head ,stream-var))))
+                 (let ((octet-count (- (buffer-tail ibuf)
+                                       (buffer-head ibuf))))
                    (when (or (zerop octet-count)
                              (and (not ,element-var)
                                   (not decode-break-reason)
                    (when (or (zerop octet-count)
                              (and (not ,element-var)
                                   (not decode-break-reason)
                                    stream octet-count)))
                      (setq ,retry-var nil)))))
              (cond (,element-var
                                    stream octet-count)))
                      (setq ,retry-var nil)))))
              (cond (,element-var
-                    (incf (fd-stream-ibuf-head ,stream-var) size)
+                    (incf (buffer-head ibuf) size)
                     ,element-var)
                    (t
                     (eof-or-lose ,stream-var ,eof-error ,eof-value))))))))
 
 ;;; a macro to wrap around all input routines to handle EOF-ERROR noise
 (defmacro input-wrapper ((stream bytes eof-error eof-value) &body read-forms)
                     ,element-var)
                    (t
                     (eof-or-lose ,stream-var ,eof-error ,eof-value))))))))
 
 ;;; a macro to wrap around all input routines to handle EOF-ERROR noise
 (defmacro input-wrapper ((stream bytes eof-error eof-value) &body read-forms)
-  (let ((stream-var (gensym))
-        (element-var (gensym)))
-    `(let ((,stream-var ,stream))
+  (let ((stream-var (gensym "STREAM"))
+        (element-var (gensym "ELT")))
+    `(let* ((,stream-var ,stream)
+            (ibuf (fd-stream-ibuf ,stream-var)))
        (if (fd-stream-unread ,stream-var)
            (prog1
                (fd-stream-unread ,stream-var)
        (if (fd-stream-unread ,stream-var)
            (prog1
                (fd-stream-unread ,stream-var)
                     (input-at-least ,stream-var ,bytes)
                     (locally ,@read-forms))))
              (cond (,element-var
                     (input-at-least ,stream-var ,bytes)
                     (locally ,@read-forms))))
              (cond (,element-var
-                    (incf (fd-stream-ibuf-head ,stream-var) ,bytes)
+                    (incf (buffer-head (fd-stream-ibuf ,stream-var)) ,bytes)
                     ,element-var)
                    (t
                     (eof-or-lose ,stream-var ,eof-error ,eof-value))))))))
                     ,element-var)
                    (t
                     (eof-or-lose ,stream-var ,eof-error ,eof-value))))))))
   `(progn
      (defun ,name (stream eof-error eof-value)
        (input-wrapper/variable-width (stream ,size eof-error eof-value)
   `(progn
      (defun ,name (stream eof-error eof-value)
        (input-wrapper/variable-width (stream ,size eof-error eof-value)
-         (let ((,sap (fd-stream-ibuf-sap stream))
-               (,head (fd-stream-ibuf-head stream)))
+         (let ((,sap (buffer-sap ibuf))
+               (,head (buffer-head ibuf)))
            ,@body)))
      (setf *input-routines*
            (nconc *input-routines*
            ,@body)))
      (setf *input-routines*
            (nconc *input-routines*
   `(progn
      (defun ,name (stream eof-error eof-value)
        (input-wrapper (stream ,size eof-error eof-value)
   `(progn
      (defun ,name (stream eof-error eof-value)
        (input-wrapper (stream ,size eof-error eof-value)
-         (let ((,sap (fd-stream-ibuf-sap stream))
-               (,head (fd-stream-ibuf-head stream)))
+         (let ((,sap (buffer-sap ibuf))
+               (,head (buffer-head ibuf)))
            ,@body)))
      (setf *input-routines*
            (nconc *input-routines*
            ,@body)))
      (setf *input-routines*
            (nconc *input-routines*
              (values
               (lambda (stream eof-error eof-value)
                 (input-wrapper (stream (/ i 8) eof-error eof-value)
              (values
               (lambda (stream eof-error eof-value)
                 (input-wrapper (stream (/ i 8) eof-error eof-value)
-                  (let ((sap (fd-stream-ibuf-sap stream))
-                        (head (fd-stream-ibuf-head stream)))
+                  (let ((sap (buffer-sap ibuf))
+                        (head (buffer-head ibuf)))
                     (loop for j from 0 below (/ i 8)
                           with result = 0
                           do (setf result
                     (loop for j from 0 below (/ i 8)
                           with result = 0
                           do (setf result
              (values
               (lambda (stream eof-error eof-value)
                 (input-wrapper (stream (/ i 8) eof-error eof-value)
              (values
               (lambda (stream eof-error eof-value)
                 (input-wrapper (stream (/ i 8) eof-error eof-value)
-                  (let ((sap (fd-stream-ibuf-sap stream))
-                        (head (fd-stream-ibuf-head stream)))
+                  (let ((sap (buffer-sap ibuf))
+                        (head (buffer-head ibuf)))
                     (loop for j from 0 below (/ i 8)
                           with result = 0
                           do (setf result
                     (loop for j from 0 below (/ i 8)
                           with result = 0
                           do (setf result
               `(signed-byte ,i)
               (/ i 8)))))
 
               `(signed-byte ,i)
               (/ i 8)))))
 
-;;; Return a string constructed from SAP, START, and END.
-(defun string-from-sap (sap start end)
-  (declare (type index start end))
-  (let* ((length (- end start))
-         (string (make-string length)))
-    (copy-ub8-from-system-area sap start
-                               string 0
-                               length)
-    string))
-
 ;;; the N-BIN method for FD-STREAMs
 ;;;
 ;;; Note that this blocks in UNIX-READ. It is generally used where
 ;;; the N-BIN method for FD-STREAMs
 ;;;
 ;;; Note that this blocks in UNIX-READ. It is generally used where
   (do ()
       (nil)
     (let* ((remaining-request (- requested total-copied))
   (do ()
       (nil)
     (let* ((remaining-request (- requested total-copied))
-           (head (fd-stream-ibuf-head stream))
-           (tail (fd-stream-ibuf-tail stream))
+           (ibuf (fd-stream-ibuf stream))
+           (head (buffer-head ibuf))
+           (tail (buffer-tail ibuf))
            (available (- tail head))
            (n-this-copy (min remaining-request available))
            (this-start (+ start total-copied))
            (this-end (+ this-start n-this-copy))
            (available (- tail head))
            (n-this-copy (min remaining-request available))
            (this-start (+ start total-copied))
            (this-end (+ this-start n-this-copy))
-           (sap (fd-stream-ibuf-sap stream)))
+           (sap (buffer-sap ibuf)))
       (declare (type index remaining-request head tail available))
       (declare (type index n-this-copy))
       ;; Copy data from stream buffer into user's buffer.
       (%byte-blt sap head buffer this-start this-end)
       (declare (type index remaining-request head tail available))
       (declare (type index n-this-copy))
       ;; Copy data from stream buffer into user's buffer.
       (%byte-blt sap head buffer this-start this-end)
-      (incf (fd-stream-ibuf-head stream) n-this-copy)
+      (incf (buffer-head ibuf) n-this-copy)
       (incf total-copied n-this-copy)
       ;; Maybe we need to refill the stream buffer.
       (cond (;; If there were enough data in the stream buffer, we're done.
       (incf total-copied n-this-copy)
       ;; Maybe we need to refill the stream buffer.
       (cond (;; If there were enough data in the stream buffer, we're done.
-             (= total-copied requested)
+             (eql total-copied requested)
              (return total-copied))
             (;; If EOF, we're done in another way.
              (return total-copied))
             (;; If EOF, we're done in another way.
-             (null (catch 'eof-input-catcher (refill-buffer/fd stream)))
+             (null (catch 'eof-input-catcher (refill-input-buffer stream)))
              (if eof-error-p
                  (error 'end-of-file :stream stream)
                  (return total-copied)))
              (if eof-error-p
                  (error 'end-of-file :stream stream)
                  (return total-copied)))
         (let ((start (or start 0))
               (end (or end (length string))))
           (declare (type index start end))
         (let ((start (or start 0))
               (end (or end (length string))))
           (declare (type index start end))
-          (when (and (not (fd-stream-dual-channel-p stream))
-                     (> (fd-stream-ibuf-tail stream)
-                        (fd-stream-ibuf-head stream)))
-            (file-position stream (file-position stream)))
+          (synchronize-stream-output stream)
           (unless (<= 0 start end (length string))
           (unless (<= 0 start end (length string))
-            (signal-bounding-indices-bad-error string start end))
+            (sequence-bounding-indices-bad-error string start end))
           (do ()
               ((= end start))
           (do ()
               ((= end start))
-            (setf (fd-stream-obuf-tail stream)
-                  (string-dispatch (simple-base-string
-                                    #!+sb-unicode
-                                    (simple-array character)
-                                    string)
-                      string
-                    (let ((len (fd-stream-obuf-length stream))
-                          (sap (fd-stream-obuf-sap stream))
-                          (tail (fd-stream-obuf-tail stream)))
-                      (declare (type index tail)
-                               ;; STRING bounds have already been checked.
-                               (optimize (safety 0)))
-                      (loop
+            (let ((obuf (fd-stream-obuf stream)))
+              (setf (buffer-tail obuf)
+                    (string-dispatch (simple-base-string
+                                      #!+sb-unicode
+                                      (simple-array character (*))
+                                      string)
+                        string
+                      (let ((sap (buffer-sap obuf))
+                            (len (buffer-length obuf))
+                            ;; FIXME: rename
+                            (tail (buffer-tail obuf)))
+                       (declare (type index tail)
+                                ;; STRING bounds have already been checked.
+                                (optimize (safety 0)))
+                       (loop
                          (,@(if output-restart
                                 `(catch 'output-nothing)
                                 `(progn))
                          (,@(if output-restart
                                 `(catch 'output-nothing)
                                 `(progn))
                             (return tail))
                          ;; Exited via CATCH. Skip the current character
                          ;; and try the inner loop again.
                             (return tail))
                          ;; Exited via CATCH. Skip the current character
                          ;; and try the inner loop again.
-                         (incf start)))))
+                         (incf start))))))
             (when (< start end)
               (flush-output-buffer stream)))
           (when flush-p
             (when (< start end)
               (flush-output-buffer stream)))
           (when flush-p
                             (:none character)
                             (:line character)
                             (:full character))
                             (:none character)
                             (:line character)
                             (:full character))
-          (if (char= byte #\Newline)
+          (if (eql byte #\Newline)
               (setf (fd-stream-char-pos stream) 0)
               (incf (fd-stream-char-pos stream)))
               (setf (fd-stream-char-pos stream) 0)
               (incf (fd-stream-char-pos stream)))
-        (let ((bits (char-code byte))
-              (sap (fd-stream-obuf-sap stream))
-              (tail (fd-stream-obuf-tail stream)))
-          ,out-expr))
+          (let* ((obuf (fd-stream-obuf stream))
+                 (bits (char-code byte))
+                 (sap (buffer-sap obuf))
+                 (tail (buffer-tail obuf)))
+            ,out-expr))
       (defun ,in-function (stream buffer start requested eof-error-p
                            &aux (index start) (end (+ start requested)))
         (declare (type fd-stream stream)
       (defun ,in-function (stream buffer start requested eof-error-p
                            &aux (index start) (end (+ start requested)))
         (declare (type fd-stream stream)
             (incf index)))
         (do ()
             (nil)
             (incf index)))
         (do ()
             (nil)
-          (let* ((head (fd-stream-ibuf-head stream))
-                 (tail (fd-stream-ibuf-tail stream))
-                 (sap (fd-stream-ibuf-sap stream)))
+          (let* ((ibuf (fd-stream-ibuf stream))
+                 (head (buffer-head ibuf))
+                 (tail (buffer-tail ibuf))
+                 (sap (buffer-sap ibuf)))
             (declare (type index head tail)
                      (type system-area-pointer sap))
             ;; Copy data from stream buffer into user's buffer.
             (declare (type index head tail)
                      (type system-area-pointer sap))
             ;; Copy data from stream buffer into user's buffer.
                 (setf (aref buffer index) ,in-expr)
                 (incf index)
                 (incf head ,size)))
                 (setf (aref buffer index) ,in-expr)
                 (incf index)
                 (incf head ,size)))
-            (setf (fd-stream-ibuf-head stream) head)
+            (setf (buffer-head ibuf) head)
             ;; Maybe we need to refill the stream buffer.
             (cond ( ;; If there was enough data in the stream buffer, we're done.
                    (= index end)
                    (return (- index start)))
                   ( ;; If EOF, we're done in another way.
             ;; Maybe we need to refill the stream buffer.
             (cond ( ;; If there was enough data in the stream buffer, we're done.
                    (= index end)
                    (return (- index start)))
                   ( ;; If EOF, we're done in another way.
-                   (null (catch 'eof-input-catcher (refill-buffer/fd stream)))
+                   (null (catch 'eof-input-catcher (refill-input-buffer stream)))
                    (if eof-error-p
                        (error 'end-of-file :stream stream)
                        (return (- index start))))
                    (if eof-error-p
                        (error 'end-of-file :stream stream)
                        (return (- index start))))
             (let* ((length (length string))
                    (,n-buffer (make-array (* (1+ length) ,size)
                                           :element-type '(unsigned-byte 8)))
             (let* ((length (length string))
                    (,n-buffer (make-array (* (1+ length) ,size)
                                           :element-type '(unsigned-byte 8)))
-                   ;; This SAP-taking may seem unsafe without pinning,
-                   ;; but since the variable name is a gensym OUT-EXPR
-                   ;; cannot close over it even if it tried, so the buffer
-                   ;; will always be either in a register or on stack.
-                   ;; FIXME: But ...this is true on x86oids only!
-                   (sap (vector-sap ,n-buffer))
                    (tail 0)
                    (stream ,name))
                    (tail 0)
                    (stream ,name))
-              (declare (type index length tail)
-                       (type system-area-pointer sap))
-              (dotimes (i length)
-                (let* ((byte (aref string i))
-                       (bits (char-code byte)))
-                  (declare (ignorable byte bits))
-                  ,out-expr)
-                (incf tail ,size))
-              (let* ((bits 0)
-                     (byte (code-char bits)))
-                (declare (ignorable bits byte))
-                ,out-expr)
+              (declare (type index length tail))
+              (with-pinned-objects (,n-buffer)
+                (let ((sap (vector-sap ,n-buffer)))
+                  (declare (system-area-pointer sap))
+                  (dotimes (i length)
+                    (let* ((byte (aref string i))
+                           (bits (char-code byte)))
+                      (declare (ignorable byte bits))
+                      ,out-expr)
+                    (incf tail ,size))
+                  (let* ((bits 0)
+                         (byte (code-char bits)))
+                    (declare (ignorable bits byte))
+                    ,out-expr)))
               ,n-buffer)))
       (setf *external-formats*
        (cons '(,external-format ,in-function ,in-char-function ,out-function
               ,n-buffer)))
       (setf *external-formats*
        (cons '(,external-format ,in-function ,in-char-function ,out-function
         (let ((start (or start 0))
               (end (or end (length string))))
           (declare (type index start end))
         (let ((start (or start 0))
               (end (or end (length string))))
           (declare (type index start end))
-          (when (and (not (fd-stream-dual-channel-p stream))
-                     (> (fd-stream-ibuf-tail stream)
-                        (fd-stream-ibuf-head stream)))
-            (file-position stream (file-position stream)))
+          (synchronize-stream-output stream)
           (unless (<= 0 start end (length string))
           (unless (<= 0 start end (length string))
-            (signal-bounding-indices-bad-error string start end))
+            (sequence-bounding-indices-bad string start end))
           (do ()
               ((= end start))
           (do ()
               ((= end start))
-            (setf (fd-stream-obuf-tail stream)
-                  (string-dispatch (simple-base-string
-                                    #!+sb-unicode
-                                    (simple-array character)
-                                    string)
-                      string
-                    (let ((len (fd-stream-obuf-length stream))
-                          (sap (fd-stream-obuf-sap stream))
-                          (tail (fd-stream-obuf-tail stream)))
-                      (declare (type index tail)
-                               ;; STRING bounds have already been checked.
-                               (optimize (safety 0)))
-                      (loop
-                         (,@(if output-restart
-                                `(catch 'output-nothing)
-                                `(progn))
-                            (do* ()
-                                 ((or (= start end) (< (- len tail) 4)))
-                              (let* ((byte (aref string start))
-                                     (bits (char-code byte))
-                                     (size ,out-size-expr))
-                                ,out-expr
-                                (incf tail size)
-                                (incf start)))
-                            ;; Exited from the loop normally
-                            (return tail))
-                         ;; Exited via CATCH. Skip the current character
-                         ;; and try the inner loop again.
-                         (incf start)))))
+            (let ((obuf (fd-stream-obuf stream)))
+              (setf (buffer-tail obuf)
+                    (string-dispatch (simple-base-string
+                                      #!+sb-unicode
+                                      (simple-array character (*))
+                                      string)
+                        string
+                      (let ((len (buffer-length obuf))
+                            (sap (buffer-sap obuf))
+                            ;; FIXME: Rename
+                            (tail (buffer-tail obuf)))
+                        (declare (type index tail)
+                                 ;; STRING bounds have already been checked.
+                                 (optimize (safety 0)))
+                        (loop
+                          (,@(if output-restart
+                                 `(catch 'output-nothing)
+                                 `(progn))
+                             (do* ()
+                                  ((or (= start end) (< (- len tail) 4)))
+                               (let* ((byte (aref string start))
+                                      (bits (char-code byte))
+                                      (size ,out-size-expr))
+                                 ,out-expr
+                                 (incf tail size)
+                                 (incf start)))
+                             ;; Exited from the loop normally
+                             (return tail))
+                          ;; Exited via CATCH. Skip the current character
+                          ;; and try the inner loop again.
+                          (incf start))))))
             (when (< start end)
               (flush-output-buffer stream)))
           (when flush-p
             (when (< start end)
               (flush-output-buffer stream)))
           (when flush-p
                                            (:none character)
                                            (:line character)
                                            (:full character))
                                            (:none character)
                                            (:line character)
                                            (:full character))
-          (if (char= byte #\Newline)
+          (if (eql byte #\Newline)
               (setf (fd-stream-char-pos stream) 0)
               (incf (fd-stream-char-pos stream)))
         (let ((bits (char-code byte))
               (setf (fd-stream-char-pos stream) 0)
               (incf (fd-stream-char-pos stream)))
         (let ((bits (char-code byte))
-              (sap (fd-stream-obuf-sap stream))
-              (tail (fd-stream-obuf-tail stream)))
+              (sap (buffer-sap obuf))
+              (tail (buffer-tail obuf)))
           ,out-expr))
       (defun ,in-function (stream buffer start requested eof-error-p
                            &aux (total-copied 0))
           ,out-expr))
       (defun ,in-function (stream buffer start requested eof-error-p
                            &aux (total-copied 0))
             (incf total-copied)))
         (do ()
             (nil)
             (incf total-copied)))
         (do ()
             (nil)
-          (let* ((head (fd-stream-ibuf-head stream))
-                 (tail (fd-stream-ibuf-tail stream))
-                 (sap (fd-stream-ibuf-sap stream))
+          (let* ((ibuf (fd-stream-ibuf stream))
+                 (head (buffer-head ibuf))
+                 (tail (buffer-tail ibuf))
+                 (sap (buffer-sap ibuf))
                  (decode-break-reason nil))
             (declare (type index head tail))
             ;; Copy data from stream buffer into user's buffer.
                  (decode-break-reason nil))
             (declare (type index head tail))
             ;; Copy data from stream buffer into user's buffer.
                         (incf total-copied)
                         (incf head size))
                       nil))
                         (incf total-copied)
                         (incf head size))
                       nil))
-              (setf (fd-stream-ibuf-head stream) head)
+              (setf (buffer-head ibuf) head)
               (when decode-break-reason
                 ;; If we've already read some characters on when the invalid
                 ;; code sequence is detected, we return immediately. The
               (when decode-break-reason
                 ;; If we've already read some characters on when the invalid
                 ;; code sequence is detected, we return immediately. The
                   (if eof-error-p
                       (error 'end-of-file :stream stream)
                       (return-from ,in-function total-copied)))
                   (if eof-error-p
                       (error 'end-of-file :stream stream)
                       (return-from ,in-function total-copied)))
-                (setf head (fd-stream-ibuf-head stream))
-                (setf tail (fd-stream-ibuf-tail stream))))
-            (setf (fd-stream-ibuf-head stream) head)
+                (setf head (buffer-head ibuf))
+                (setf tail (buffer-tail ibuf))))
+            (setf (buffer-head ibuf) head)
             ;; Maybe we need to refill the stream buffer.
             (cond ( ;; If there were enough data in the stream buffer, we're done.
                    (= total-copied requested)
             ;; Maybe we need to refill the stream buffer.
             (cond ( ;; If there were enough data in the stream buffer, we're done.
                    (= total-copied requested)
                   ( ;; If EOF, we're done in another way.
                    (or (eq decode-break-reason 'eof)
                        (null (catch 'eof-input-catcher
                   ( ;; If EOF, we're done in another way.
                    (or (eq decode-break-reason 'eof)
                        (null (catch 'eof-input-catcher
-                               (refill-buffer/fd stream))))
+                               (refill-input-buffer stream))))
                    (if eof-error-p
                        (error 'end-of-file :stream stream)
                        (return total-copied)))
                    (if eof-error-p
                        (error 'end-of-file :stream stream)
                        (return total-copied)))
           (declare (ignorable byte))
           ,in-expr))
       (defun ,resync-function (stream)
           (declare (ignorable byte))
           ,in-expr))
       (defun ,resync-function (stream)
-        (loop (input-at-least stream 2)
-              (incf (fd-stream-ibuf-head stream))
-              (unless (block decode-break-reason
-                        (let* ((sap (fd-stream-ibuf-sap stream))
-                               (head (fd-stream-ibuf-head stream))
-                               (byte (sap-ref-8 sap head))
-                               (size ,in-size-expr))
-                          (declare (ignorable byte))
-                          (input-at-least stream size)
-                          (let ((sap (fd-stream-ibuf-sap stream))
-                                (head (fd-stream-ibuf-head stream)))
-                            ,in-expr))
-                        nil)
-                (return))))
+        (let ((ibuf (fd-stream-ibuf stream)))
+          (loop
+            (input-at-least stream 2)
+            (incf (buffer-head ibuf))
+            (unless (block decode-break-reason
+                      (let* ((sap (buffer-sap ibuf))
+                             (head (buffer-head ibuf))
+                             (byte (sap-ref-8 sap head))
+                             (size ,in-size-expr))
+                        (declare (ignorable byte))
+                        (input-at-least stream size)
+                        (setf head (buffer-head ibuf))
+                        ,in-expr)
+                     nil)
+             (return)))))
       (defun ,read-c-string-function (sap element-type)
         (declare (type system-area-pointer sap))
         (locally
       (defun ,read-c-string-function (sap element-type)
         (declare (type system-area-pointer sap))
         (locally
                  (tail 0)
                  (,n-buffer (make-array buffer-length
                                         :element-type '(unsigned-byte 8)))
                  (tail 0)
                  (,n-buffer (make-array buffer-length
                                         :element-type '(unsigned-byte 8)))
-                 ;; This SAP-taking may seem unsafe without pinning,
-                 ;; but since the variable name is a gensym OUT-EXPR
-                 ;; cannot close over it even if it tried, so the buffer
-                 ;; will always be either in a register or on stack.
-                 ;; FIXME: But ...this is true on x86oids only!
-                 (sap (vector-sap ,n-buffer))
                  stream)
             (declare (type index length buffer-length tail)
                  stream)
             (declare (type index length buffer-length tail)
-                     (type system-area-pointer sap)
                      (type null stream)
                      (ignorable stream))
                      (type null stream)
                      (ignorable stream))
-            (loop for i of-type index below length
-                  for byte of-type character = (aref string i)
-                  for bits = (char-code byte)
-                  for size of-type index = (aref char-length i)
-                  do (prog1
-                         ,out-expr
-                       (incf tail size)))
-            (let* ((bits 0)
-                   (byte (code-char bits))
-                   (size (aref char-length length)))
-              (declare (ignorable bits byte size))
-              ,out-expr)
+            (with-pinned-objects (,n-buffer)
+              (let ((sap (vector-sap ,n-buffer)))
+                (declare (system-area-pointer sap))
+                (loop for i of-type index below length
+                      for byte of-type character = (aref string i)
+                      for bits = (char-code byte)
+                      for size of-type index = (aref char-length i)
+                      do (prog1
+                             ,out-expr
+                           (incf tail size)))
+                (let* ((bits 0)
+                       (byte (code-char bits))
+                       (size (aref char-length length)))
+                  (declare (ignorable bits byte size))
+                  ,out-expr)))
             ,n-buffer)))
 
       (setf *external-formats*
             ,n-buffer)))
 
       (setf *external-formats*
       (setf (sap-ref-8 sap tail) bits))
   (code-char byte))
 
       (setf (sap-ref-8 sap tail) bits))
   (code-char byte))
 
-(let* ((table (let ((s (make-string 256)))
-                (map-into s #'code-char
-                          '(#x00 #x01 #x02 #x03 #x9c #x09 #x86 #x7f #x97 #x8d #x8e #x0b #x0c #x0d #x0e #x0f
-                            #x10 #x11 #x12 #x13 #x9d #x85 #x08 #x87 #x18 #x19 #x92 #x8f #x1c #x1d #x1e #x1f
-                            #x80 #x81 #x82 #x83 #x84 #x0a #x17 #x1b #x88 #x89 #x8a #x8b #x8c #x05 #x06 #x07
-                            #x90 #x91 #x16 #x93 #x94 #x95 #x96 #x04 #x98 #x99 #x9a #x9b #x14 #x15 #x9e #x1a
-                            #x20 #xa0 #xe2 #xe4 #xe0 #xe1 #xe3 #xe5 #xe7 #xf1 #xa2 #x2e #x3c #x28 #x2b #x7c
-                            #x26 #xe9 #xea #xeb #xe8 #xed #xee #xef #xec #xdf #x21 #x24 #x2a #x29 #x3b #xac
-                            #x2d #x2f #xc2 #xc4 #xc0 #xc1 #xc3 #xc5 #xc7 #xd1 #xa6 #x2c #x25 #x5f #x3e #x3f
-                            #xf8 #xc9 #xca #xcb #xc8 #xcd #xce #xcf #xcc #x60 #x3a #x23 #x40 #x27 #x3d #x22
-                            #xd8 #x61 #x62 #x63 #x64 #x65 #x66 #x67 #x68 #x69 #xab #xbb #xf0 #xfd #xfe #xb1
-                            #xb0 #x6a #x6b #x6c #x6d #x6e #x6f #x70 #x71 #x72 #xaa #xba #xe6 #xb8 #xc6 #xa4
-                            #xb5 #x7e #x73 #x74 #x75 #x76 #x77 #x78 #x79 #x7a #xa1 #xbf #xd0 #xdd #xde #xae
-                            #x5e #xa3 #xa5 #xb7 #xa9 #xa7 #xb6 #xbc #xbd #xbe #x5b #x5d #xaf #xa8 #xb4 #xd7
-                            #x7b #x41 #x42 #x43 #x44 #x45 #x46 #x47 #x48 #x49 #xad #xf4 #xf6 #xf2 #xf3 #xf5
-                            #x7d #x4a #x4b #x4c #x4d #x4e #x4f #x50 #x51 #x52 #xb9 #xfb #xfc #xf9 #xfa #xff
-                            #x5c #xf7 #x53 #x54 #x55 #x56 #x57 #x58 #x59 #x5a #xb2 #xd4 #xd6 #xd2 #xd3 #xd5
-                            #x30 #x31 #x32 #x33 #x34 #x35 #x36 #x37 #x38 #x39 #xb3 #xdb #xdc #xd9 #xda #x9f))
-                s))
-       (reverse-table (let ((rt (make-array 256 :element-type '(unsigned-byte 8) :initial-element 0)))
-                          (loop for char across table for i from 0
-                               do (aver (= 0 (aref rt (char-code char))))
-                               do (setf (aref rt (char-code char)) i))
-                          rt)))
-  (define-external-format (:ebcdic-us :ibm-037 :ibm037)
-      1 t
-    (if (>= bits 256)
-        (external-format-encoding-error stream bits)
-        (setf (sap-ref-8 sap tail) (aref reverse-table bits)))
-    (aref table byte)))
-
-
 #!+sb-unicode
 (let ((latin-9-table (let ((table (make-string 256)))
                        (do ((i 0 (1+ i)))
 #!+sb-unicode
 (let ((latin-9-table (let ((table (make-string 256)))
                        (do ((i 0 (1+ i)))
           (t 4)))
   (ecase size
     (1 (setf (sap-ref-8 sap tail) bits))
           (t 4)))
   (ecase size
     (1 (setf (sap-ref-8 sap tail) bits))
-    (2 (setf (sap-ref-8 sap tail) (logior #xc0 (ldb (byte 5 6) bits))
-             (sap-ref-8 sap (1+ tail)) (logior #x80 (ldb (byte 6 0) bits))))
-    (3 (setf (sap-ref-8 sap tail) (logior #xe0 (ldb (byte 4 12) bits))
-             (sap-ref-8 sap (1+ tail)) (logior #x80 (ldb (byte 6 6) bits))
+    (2 (setf (sap-ref-8 sap tail)       (logior #xc0 (ldb (byte 5 6) bits))
+             (sap-ref-8 sap (+ 1 tail)) (logior #x80 (ldb (byte 6 0) bits))))
+    (3 (setf (sap-ref-8 sap tail)       (logior #xe0 (ldb (byte 4 12) bits))
+             (sap-ref-8 sap (+ 1 tail)) (logior #x80 (ldb (byte 6 6) bits))
              (sap-ref-8 sap (+ 2 tail)) (logior #x80 (ldb (byte 6 0) bits))))
              (sap-ref-8 sap (+ 2 tail)) (logior #x80 (ldb (byte 6 0) bits))))
-    (4 (setf (sap-ref-8 sap tail) (logior #xf0 (ldb (byte 3 18) bits))
-             (sap-ref-8 sap (1+ tail)) (logior #x80 (ldb (byte 6 12) bits))
+    (4 (setf (sap-ref-8 sap tail)       (logior #xf0 (ldb (byte 3 18) bits))
+             (sap-ref-8 sap (+ 1 tail)) (logior #x80 (ldb (byte 6 12) bits))
              (sap-ref-8 sap (+ 2 tail)) (logior #x80 (ldb (byte 6 6) bits))
              (sap-ref-8 sap (+ 3 tail)) (logior #x80 (ldb (byte 6 0) bits)))))
   (cond ((< byte #x80) 1)
              (sap-ref-8 sap (+ 2 tail)) (logior #x80 (ldb (byte 6 6) bits))
              (sap-ref-8 sap (+ 3 tail)) (logior #x80 (ldb (byte 6 0) bits)))))
   (cond ((< byte #x80) 1)
          (output-size nil)
          (output-bytes #'ill-bout))
 
          (output-size nil)
          (output-bytes #'ill-bout))
 
-    ;; drop buffers when direction changes
-    (when (and (fd-stream-obuf-sap fd-stream) (not output-p))
-      (with-available-buffers-lock ()
-        (push (fd-stream-obuf-sap fd-stream) *available-buffers*)
-        (setf (fd-stream-obuf-sap fd-stream) nil)))
-    (when (and (fd-stream-ibuf-sap fd-stream) (not input-p))
-      (with-available-buffers-lock ()
-        (push (fd-stream-ibuf-sap fd-stream) *available-buffers*)
-        (setf (fd-stream-ibuf-sap fd-stream) nil)))
-    (when input-p
-      (setf (fd-stream-ibuf-sap fd-stream) (next-available-buffer))
-      (setf (fd-stream-ibuf-length fd-stream) bytes-per-buffer)
-      (setf (fd-stream-ibuf-tail fd-stream) 0))
+    ;; Ensure that we have buffers in the desired direction(s) only,
+    ;; getting new ones and dropping/resetting old ones as necessary.
+    (let ((obuf (fd-stream-obuf fd-stream)))
+      (if output-p
+          (if obuf
+              (reset-buffer obuf)
+              (setf (fd-stream-obuf fd-stream) (get-buffer)))
+          (when obuf
+            (setf (fd-stream-obuf fd-stream) nil)
+            (release-buffer obuf))))
+
+    (let ((ibuf (fd-stream-ibuf fd-stream)))
+      (if input-p
+          (if ibuf
+              (reset-buffer ibuf)
+              (setf (fd-stream-ibuf fd-stream) (get-buffer)))
+          (when ibuf
+            (setf (fd-stream-ibuf fd-stream) nil)
+            (release-buffer ibuf))))
+
+    ;; FIXME: Why only for output? Why unconditionally?
     (when output-p
     (when output-p
-      (setf (fd-stream-obuf-sap fd-stream) (next-available-buffer))
-      (setf (fd-stream-obuf-length fd-stream) bytes-per-buffer)
-      (setf (fd-stream-obuf-tail fd-stream) 0)
       (setf (fd-stream-char-pos fd-stream) 0))
 
     (when (and character-stream-p
       (setf (fd-stream-char-pos fd-stream) 0))
 
     (when (and character-stream-p
                         input-type
                         output-type))))))
 
                         input-type
                         output-type))))))
 
+;;; Handles the resource-release aspects of stream closing, and marks
+;;; it as closed.
+(defun release-fd-stream-resources (fd-stream)
+  (handler-case
+      (without-interrupts
+        ;; Drop handlers first.
+        (when (fd-stream-handler fd-stream)
+          (remove-fd-handler (fd-stream-handler fd-stream))
+          (setf (fd-stream-handler fd-stream) nil))
+        ;; Disable interrupts so that a asynch unwind will not leave
+        ;; us with a dangling finalizer (that would close the same
+        ;; --possibly reassigned-- FD again), or a stream with a closed
+        ;; FD that appears open.
+        (sb!unix:unix-close (fd-stream-fd fd-stream))
+        (set-closed-flame fd-stream)
+        (when (fboundp 'cancel-finalization)
+          (cancel-finalization fd-stream)))
+    ;; On error unwind from WITHOUT-INTERRUPTS.
+    (serious-condition (e)
+      (error e)))
+  ;; Release all buffers. If this is undone, or interrupted,
+  ;; we're still safe: buffers have finalizers of their own.
+  (release-fd-stream-buffers fd-stream))
+
+;;; Flushes the current input buffer and unread chatacter, and returns
+;;; the input buffer, and the amount of of flushed input in bytes.
+(defun flush-input-buffer (stream)
+  (let ((unread (if (fd-stream-unread stream)
+                    1
+                    0)))
+    (setf (fd-stream-unread stream) nil)
+    (let ((ibuf (fd-stream-ibuf stream)))
+      (if ibuf
+          (let ((head (buffer-head ibuf))
+                (tail (buffer-tail ibuf)))
+            (values (reset-buffer ibuf) (- (+ unread tail) head)))
+          (values nil unread)))))
+
+(defun fd-stream-clear-input (stream)
+  (flush-input-buffer stream)
+  #!+win32
+  (progn
+    (sb!win32:fd-clear-input (fd-stream-fd stream))
+    (setf (fd-stream-listen stream) nil))
+  #!-win32
+  (catch 'eof-input-catcher
+    (loop until (sysread-may-block-p stream)
+          do
+          (refill-input-buffer stream)
+          (reset-buffer (fd-stream-ibuf stream)))
+    t))
+
 ;;; Handle miscellaneous operations on FD-STREAM.
 (defun fd-stream-misc-routine (fd-stream operation &optional arg1 arg2)
   (declare (ignore arg2))
   (case operation
     (:listen
      (labels ((do-listen ()
 ;;; Handle miscellaneous operations on FD-STREAM.
 (defun fd-stream-misc-routine (fd-stream operation &optional arg1 arg2)
   (declare (ignore arg2))
   (case operation
     (:listen
      (labels ((do-listen ()
-                (or (not (eql (fd-stream-ibuf-head fd-stream)
-                              (fd-stream-ibuf-tail fd-stream)))
-                    (fd-stream-listen fd-stream)
-                    #!+win32
-                    (sb!win32:fd-listen (fd-stream-fd fd-stream))
-                    #!-win32
-                    ;; If the read can block, LISTEN will certainly return NIL.
-                    (if (sysread-may-block-p fd-stream)
-                        nil
-                        ;; Otherwise select(2) and CL:LISTEN have slightly
-                        ;; different semantics.  The former returns that an FD
-                        ;; is readable when a read operation wouldn't block.
-                        ;; That includes EOF.  However, LISTEN must return NIL
-                        ;; at EOF.
-                        (progn (catch 'eof-input-catcher
-                                 ;; r-b/f too calls select, but it shouldn't
-                                 ;; block as long as read can return once w/o
-                                 ;; blocking
-                                 (refill-buffer/fd fd-stream))
-                               ;; At this point either IBUF-HEAD != IBUF-TAIL
-                               ;; and FD-STREAM-LISTEN is NIL, in which case
-                               ;; we should return T, or IBUF-HEAD ==
-                               ;; IBUF-TAIL and FD-STREAM-LISTEN is :EOF, in
-                               ;; which case we should return :EOF for this
-                               ;; call and all future LISTEN call on this stream.
-                               ;; Call ourselves again to determine which case
-                               ;; applies.
-                               (do-listen))))))
+                (let ((ibuf (fd-stream-ibuf fd-stream)))
+                  (or (not (eql (buffer-head ibuf) (buffer-tail ibuf)))
+                      (fd-stream-listen fd-stream)
+                      #!+win32
+                      (sb!win32:fd-listen (fd-stream-fd fd-stream))
+                      #!-win32
+                      ;; If the read can block, LISTEN will certainly return NIL.
+                      (if (sysread-may-block-p fd-stream)
+                          nil
+                          ;; Otherwise select(2) and CL:LISTEN have slightly
+                          ;; different semantics.  The former returns that an FD
+                          ;; is readable when a read operation wouldn't block.
+                          ;; That includes EOF.  However, LISTEN must return NIL
+                          ;; at EOF.
+                          (progn (catch 'eof-input-catcher
+                                   ;; r-b/f too calls select, but it shouldn't
+                                   ;; block as long as read can return once w/o
+                                   ;; blocking
+                                   (refill-input-buffer fd-stream))
+                                 ;; At this point either IBUF-HEAD != IBUF-TAIL
+                                 ;; and FD-STREAM-LISTEN is NIL, in which case
+                                 ;; we should return T, or IBUF-HEAD ==
+                                 ;; IBUF-TAIL and FD-STREAM-LISTEN is :EOF, in
+                                 ;; which case we should return :EOF for this
+                                 ;; call and all future LISTEN call on this stream.
+                                 ;; Call ourselves again to determine which case
+                                 ;; applies.
+                                 (do-listen)))))))
        (do-listen)))
     (:unread
        (do-listen)))
     (:unread
-     (setf (fd-stream-unread fd-stream) arg1)
+     ;; If the stream is bivalent, the user might follow an
+     ;; unread-char with a read-byte.  In this case, the bookkeeping
+     ;; is simpler if we adjust the buffer head by the number of code
+     ;; units in the character.
+     ;; FIXME: there has to be a proper way to check for bivalence,
+     ;; right?
+     (if (fd-stream-bivalent-p fd-stream)
+         (decf (buffer-head (fd-stream-ibuf fd-stream))
+               (fd-stream-character-size fd-stream arg1))
+         (setf (fd-stream-unread fd-stream) arg1))
      (setf (fd-stream-listen fd-stream) t))
     (:close
      (setf (fd-stream-listen fd-stream) t))
     (:close
-     (cond (arg1                    ; We got us an abort on our hands.
-            (when (fd-stream-handler fd-stream)
-              (remove-fd-handler (fd-stream-handler fd-stream))
-              (setf (fd-stream-handler fd-stream) nil))
-            ;; 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.
-            (when (and (fd-stream-file fd-stream)
-                       (fd-stream-obuf-sap fd-stream))
-              (if (fd-stream-original fd-stream)
-                  ;; If the original is EQ to file we are appending
-                  ;; and can just close the file without renaming.
-                  (unless (eq (fd-stream-original fd-stream)
-                              (fd-stream-file fd-stream))
-                    ;; We have a handle on the original, just revert.
+     ;; Drop input buffers
+     (setf (ansi-stream-in-index fd-stream) +ansi-stream-in-buffer-length+
+           (ansi-stream-cin-buffer fd-stream) nil
+           (ansi-stream-in-buffer fd-stream) nil)
+     (cond (arg1
+            ;; We got us an abort on our hands.
+            (let ((outputp (fd-stream-obuf fd-stream))
+                  (file (fd-stream-file fd-stream))
+                  (orig (fd-stream-original fd-stream)))
+              ;; This takes care of the important stuff -- everything
+              ;; rest is cleaning up the file-system, which we cannot
+              ;; do on some platforms as long as the file is open.
+              (release-fd-stream-resources fd-stream)
+              ;; 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.
+              (when (and outputp file)
+                (if orig
+                    ;; If the original is EQ to file we are appending to
+                    ;; and can just close the file without renaming.
+                    (unless (eq orig file)
+                      ;; We have a handle on the original, just revert.
+                      (multiple-value-bind (okay err)
+                          (sb!unix:unix-rename orig file)
+                        ;; FIXME: Why is this a SIMPLE-STREAM-ERROR, and the
+                        ;; others are SIMPLE-FILE-ERRORS? Surely they should
+                        ;; all be the same?
+                        (unless okay
+                          (error 'simple-stream-error
+                                 :format-control
+                                 "~@<Couldn't restore ~S to its original contents ~
+                                  from ~S while closing ~S: ~2I~_~A~:>"
+                                 :format-arguments
+                                 (list file orig fd-stream (strerror err))
+                                 :stream fd-stream))))
+                    ;; We can't restore the original, and aren't
+                    ;; appending, so nuke that puppy.
+                    ;;
+                    ;; FIXME: This is currently the fate of superseded
+                    ;; files, and according to the CLOSE spec this is
+                    ;; wrong. However, there seems to be no clean way to
+                    ;; do that that doesn't involve either copying the
+                    ;; data (bad if the :abort resulted from a full
+                    ;; disk), or renaming the old file temporarily
+                    ;; (probably bad because stream opening becomes more
+                    ;; racy).
                     (multiple-value-bind (okay err)
                     (multiple-value-bind (okay err)
-                        (sb!unix:unix-rename (fd-stream-original fd-stream)
-                                             (fd-stream-file fd-stream))
+                        (sb!unix:unix-unlink file)
                       (unless okay
                       (unless okay
-                        (simple-stream-perror
-                         "couldn't restore ~S to its original contents"
-                         fd-stream
-                         err))))
-                  ;; We can't restore the original, and aren't
-                  ;; appending, so nuke that puppy.
-                  ;;
-                  ;; FIXME: This is currently the fate of superseded
-                  ;; files, and according to the CLOSE spec this is
-                  ;; wrong. However, there seems to be no clean way to
-                  ;; do that that doesn't involve either copying the
-                  ;; data (bad if the :abort resulted from a full
-                  ;; disk), or renaming the old file temporarily
-                  ;; (probably bad because stream opening becomes more
-                  ;; racy).
-                  (multiple-value-bind (okay err)
-                      (sb!unix:unix-unlink (fd-stream-file fd-stream))
-                    (unless okay
-                      (error 'simple-file-error
-                             :pathname (fd-stream-file fd-stream)
-                             :format-control
-                             "~@<couldn't remove ~S: ~2I~_~A~:>"
-                             :format-arguments (list (fd-stream-file fd-stream)
-                                                     (strerror err))))))))
+                        (error 'simple-file-error
+                               :pathname file
+                               :format-control
+                               "~@<Couldn't remove ~S while closing ~S: ~2I~_~A~:>"
+                               :format-arguments
+                               (list file fd-stream (strerror err)))))))))
            (t
            (t
-            (fd-stream-misc-routine fd-stream :finish-output)
-            (when (and (fd-stream-original fd-stream)
-                       (fd-stream-delete-original fd-stream))
-              (multiple-value-bind (okay err)
-                  (sb!unix:unix-unlink (fd-stream-original fd-stream))
-                (unless okay
-                  (error 'simple-file-error
-                         :pathname (fd-stream-original fd-stream)
-                         :format-control
-                         "~@<couldn't delete ~S during close of ~S: ~
-                          ~2I~_~A~:>"
-                         :format-arguments
-                         (list (fd-stream-original fd-stream)
-                               fd-stream
-                               (strerror err))))))))
-     (when (fboundp 'cancel-finalization)
-       (cancel-finalization fd-stream))
-     (sb!unix:unix-close (fd-stream-fd fd-stream))
-     (when (fd-stream-obuf-sap fd-stream)
-       (with-available-buffers-lock ()
-         (push (fd-stream-obuf-sap fd-stream) *available-buffers*)
-         (setf (fd-stream-obuf-sap fd-stream) nil)))
-     (when (fd-stream-ibuf-sap fd-stream)
-       (with-available-buffers-lock ()
-         (push (fd-stream-ibuf-sap fd-stream) *available-buffers*)
-         (setf (fd-stream-ibuf-sap fd-stream) nil)))
-     (sb!impl::set-closed-flame fd-stream))
+            (finish-fd-stream-output fd-stream)
+            (let ((orig (fd-stream-original fd-stream)))
+              (when (and orig (fd-stream-delete-original fd-stream))
+                (multiple-value-bind (okay err) (sb!unix:unix-unlink orig)
+                  (unless okay
+                    (error 'simple-file-error
+                           :pathname orig
+                           :format-control
+                           "~@<couldn't delete ~S while closing ~S: ~2I~_~A~:>"
+                           :format-arguments
+                           (list orig fd-stream (strerror err)))))))
+            ;; In case of no-abort close, don't *really* close the
+            ;; stream until the last moment -- the cleaning up of the
+            ;; original can be done first.
+            (release-fd-stream-resources fd-stream))))
     (:clear-input
     (:clear-input
-     (setf (fd-stream-unread fd-stream) nil)
-     (setf (fd-stream-ibuf-head fd-stream) 0)
-     (setf (fd-stream-ibuf-tail fd-stream) 0)
-     #!+win32
-     (progn
-       (sb!win32:fd-clear-input (fd-stream-fd fd-stream))
-       (setf (fd-stream-listen fd-stream) nil))
-     #!-win32
-     (catch 'eof-input-catcher
-       (loop until (sysread-may-block-p fd-stream)
-             do
-             (refill-buffer/fd fd-stream)
-             (setf (fd-stream-ibuf-head fd-stream) 0)
-             (setf (fd-stream-ibuf-tail fd-stream) 0))
-       t))
+     (fd-stream-clear-input fd-stream))
     (:force-output
      (flush-output-buffer fd-stream))
     (:finish-output
     (:force-output
      (flush-output-buffer fd-stream))
     (:finish-output
 ;;
 ;; (defun finish-fd-stream-output (fd-stream)
 ;;   (let ((timeout (fd-stream-timeout fd-stream)))
 ;;
 ;; (defun finish-fd-stream-output (fd-stream)
 ;;   (let ((timeout (fd-stream-timeout fd-stream)))
-;;     (loop while (fd-stream-output-later fd-stream)
+;;     (loop while (fd-stream-output-queue fd-stream)
 ;;        ;; FIXME: SIGINT while waiting for a timeout will
 ;;        ;; cause a timeout here.
 ;;        do (when (and (not (serve-event timeout)) timeout)
 ;;        ;; FIXME: SIGINT while waiting for a timeout will
 ;;        ;; cause a timeout here.
 ;;        do (when (and (not (serve-event timeout)) timeout)
 (defun finish-fd-stream-output (stream)
   (flush-output-buffer stream)
   (do ()
 (defun finish-fd-stream-output (stream)
   (flush-output-buffer stream)
   (do ()
-      ((null (fd-stream-output-later stream)))
+      ((null (fd-stream-output-queue stream)))
     (serve-all-events)))
 
 (defun fd-stream-get-file-position (stream)
     (serve-all-events)))
 
 (defun fd-stream-get-file-position (stream)
         ;; than reported by lseek() because lseek() obviously
         ;; cannot take into account output we have not sent
         ;; yet.
         ;; than reported by lseek() because lseek() obviously
         ;; cannot take into account output we have not sent
         ;; yet.
-        (dolist (later (fd-stream-output-later stream))
-          (incf posn (- (caddr later) (cadr later))))
-        (incf posn (fd-stream-obuf-tail stream))
+        (dolist (buffer (fd-stream-output-queue stream))
+          (incf posn (- (buffer-tail buffer) (buffer-head buffer))))
+        (let ((obuf (fd-stream-obuf stream)))
+          (when obuf
+            (incf posn (buffer-tail obuf))))
         ;; Adjust for unread input: If there is any input
         ;; read from UNIX but not supplied to the user of the
         ;; stream, the *real* file position will smaller than
         ;; reported, because we want to look like the unread
         ;; stuff is still available.
         ;; Adjust for unread input: If there is any input
         ;; read from UNIX but not supplied to the user of the
         ;; stream, the *real* file position will smaller than
         ;; reported, because we want to look like the unread
         ;; stuff is still available.
-        (decf posn (- (fd-stream-ibuf-tail stream)
-                      (fd-stream-ibuf-head stream)))
+        (let ((ibuf (fd-stream-ibuf stream)))
+          (when ibuf
+            (decf posn (- (buffer-tail ibuf) (buffer-head ibuf)))))
         (when (fd-stream-unread stream)
           (decf posn))
         ;; Divide bytes by element size.
         (when (fd-stream-unread stream)
           (decf posn))
         ;; Divide bytes by element size.
          (go :again))
        ;; Clear out any pending input to force the next read to go to
        ;; the disk.
          (go :again))
        ;; Clear out any pending input to force the next read to go to
        ;; the disk.
-       (setf (fd-stream-unread stream) nil
-             (fd-stream-ibuf-head stream) 0
-             (fd-stream-ibuf-tail stream) 0)
+       (flush-input-buffer stream)
        ;; Trash cached value for listen, so that we check next time.
        (setf (fd-stream-listen stream) nil)
          ;; Now move it.
          (multiple-value-bind (offset origin)
              (case position-spec
        ;; Trash cached value for listen, so that we check next time.
        (setf (fd-stream-listen stream) nil)
          ;; Now move it.
          (multiple-value-bind (offset origin)
              (case position-spec
-           (:start
-            (values 0 sb!unix:l_set))
-           (:end
-            (values 0 sb!unix:l_xtnd))
-           (t
-            (values (* position-spec (fd-stream-element-size stream))
-                    sb!unix:l_set)))
+               (:start
+                (values 0 sb!unix:l_set))
+               (:end
+                (values 0 sb!unix:l_xtnd))
+               (t
+                (values (* position-spec (fd-stream-element-size stream))
+                        sb!unix:l_set)))
            (declare (type (alien sb!unix:off-t) offset))
            (let ((posn (sb!unix:unix-lseek (fd-stream-fd stream)
                                            offset origin)))
            (declare (type (alien sb!unix:off-t) offset))
            (let ((posn (sb!unix:unix-lseek (fd-stream-fd stream)
                                            offset origin)))
                                  :buffering buffering
                                  :dual-channel-p dual-channel-p
                                  :external-format external-format
                                  :buffering buffering
                                  :dual-channel-p dual-channel-p
                                  :external-format external-format
+                                 :bivalent-p (eq element-type :default)
+                                 :char-size (external-format-char-size external-format)
                                  :timeout
                                  (if timeout
                                      (coerce timeout 'single-float)
                                  :timeout
                                  (if timeout
                                      (coerce timeout 'single-float)
                   (sb!unix:unix-close fd)
                   #!+sb-show
                   (format *terminal-io* "** closed file descriptor ~W **~%"
                   (sb!unix:unix-close fd)
                   #!+sb-show
                   (format *terminal-io* "** closed file descriptor ~W **~%"
-                          fd))))
+                          fd))
+                :dont-save t))
     stream))
 
 ;;; Pick a name to use for the backup file for the :IF-EXISTS
     stream))
 
 ;;; Pick a name to use for the backup file for the :IF-EXISTS
 
   ;; Calculate useful stuff.
   (multiple-value-bind (input output mask)
 
   ;; Calculate useful stuff.
   (multiple-value-bind (input output mask)
-      (case direction
+      (ecase direction
         (:input  (values   t nil sb!unix:o_rdonly))
         (:output (values nil   t sb!unix:o_wronly))
         (:io     (values   t   t sb!unix:o_rdwr))
         (:probe  (values   t nil sb!unix:o_rdonly)))
     (declare (type index mask))
         (:input  (values   t nil sb!unix:o_rdonly))
         (:output (values nil   t sb!unix:o_wronly))
         (:io     (values   t   t sb!unix:o_rdwr))
         (:probe  (values   t nil sb!unix:o_rdonly)))
     (declare (type index mask))
-    (let* ((pathname (merge-pathnames filename))
-           (namestring
-            (cond ((unix-namestring pathname input))
-                  ((and input (eq if-does-not-exist :create))
-                   (unix-namestring pathname nil))
-                  ((and (eq direction :io) (not if-does-not-exist-given))
-                   (unix-namestring pathname nil)))))
+    (let* (;; PATHNAME is the pathname we associate with the stream.
+           (pathname (merge-pathnames filename))
+           (physical (physicalize-pathname pathname))
+           (truename (probe-file physical))
+           ;; NAMESTRING is the native namestring we open the file with.
+           (namestring (cond (truename
+                              (native-namestring truename :as-file t))
+                             ((or (not input)
+                                  (and input (eq if-does-not-exist :create))
+                                  (and (eq direction :io) (not if-does-not-exist-given)))
+                              (native-namestring physical :as-file t)))))
       ;; Process if-exists argument if we are doing any output.
       (cond (output
              (unless if-exists-given
       ;; Process if-exists argument if we are doing any output.
       (cond (output
              (unless if-exists-given
                           (when (and output (= (logand orig-mode #o170000)
                                                #o40000))
                             (error 'simple-file-error
                           (when (and output (= (logand orig-mode #o170000)
                                                #o40000))
                             (error 'simple-file-error
-                                   :pathname namestring
+                                   :pathname pathname
                                    :format-control
                                    "can't open ~S for output: is a directory"
                                    :format-arguments (list namestring)))
                                    :format-control
                                    "can't open ~S for output: is a directory"
                                    :format-arguments (list namestring)))
   (setf *trace-output* *standard-output*)
   (values))
 
   (setf *trace-output* *standard-output*)
   (values))
 
+(defun stream-deinit ()
+  ;; Unbind to make sure we're not accidently dealing with it
+  ;; before we're ready (or after we think it's been deinitialized).
+  (with-available-buffers-lock ()
+    (without-package-locks
+        (makunbound '*available-buffers*))))
+
 ;;; This is called whenever a saved core is restarted.
 ;;; This is called whenever a saved core is restarted.
-(defun stream-reinit ()
-  (setf *available-buffers* nil)
+(defun stream-reinit (&optional init-buffers-p)
+  (when init-buffers-p
+    (with-available-buffers-lock ()
+      (aver (not (boundp '*available-buffers*)))
+      (setf *available-buffers* nil)))
   (with-output-to-string (*error-output*)
     (setf *stdin*
           (make-fd-stream 0 :name "standard input" :input t :buffering :line
   (with-output-to-string (*error-output*)
     (setf *stdin*
           (make-fd-stream 0 :name "standard input" :input t :buffering :line
       (cond (new-name
              (setf (fd-stream-pathname stream) new-name)
              (setf (fd-stream-file stream)
       (cond (new-name
              (setf (fd-stream-pathname stream) new-name)
              (setf (fd-stream-file stream)
-                   (unix-namestring new-name nil))
+                   (native-namestring (physicalize-pathname new-name)
+                                      :as-file t))
              t)
             (t
              (fd-stream-pathname stream)))))
              t)
             (t
              (fd-stream-pathname stream)))))