file-position on non-files
Here's a can of worms. It involves a string-stream, but vector streams in Clozure CL (see l1-streams.lisp) work similarly.

The result varies by Common Lisp implementation:

CCL "w"
ACL "w"
ECL "w"
MKCL "w"
Clasp "w"
npt "w"
ABCL "woo"
SBCL "woo"
LispWorks "woo"
Corman "foow"

So for a few implementations, the output is truncated by the use of file-position (as if by setting the fill-pointer). SBCL's implementation (since version remembers the ending position and doesn't truncate the output. Which is less surprising, unless you actually wanted to truncate the output. Then you'd need a different approach. LispWorks appears to be in the same camp with SBCL. In Corman Common Lisp, the file-position is different from the actual output position.

Clozure CL

Since file-position in CCL operates this way with vector streams (i.e. having the effect of truncation), David Mullen often does low-level tricks like writing directly to the underlying vector. This gets into the internals (specifically: stuff that's private to the CCL package) and isn't exactly safe code, since it assumes there is already space in the buffer, and would otherwise need to call %extend-vector-output-stream (directly or indirectly):

(defun %vector-stream-write-u32 (u stream position) (let* ((ioblock (basic-stream-ioblock stream)) (origin (vector-output-stream-ioblock-displacement ioblock))) (declare (type (integer 0 #.array-dimension-limit) origin)) (declare (type (integer 0 #.array-dimension-limit) position)) (declare (optimize (safety 0) (speed 3))) (let* ((outbuf (ioblock-outbuf ioblock)) (buffer (io-buffer-buffer outbuf)) (index (+ origin position))) (declare (type (unsigned-byte 32) u)) (declare (type (integer 0 #.array-dimension-limit) index)) (declare (type (simple-array (unsigned-byte 8) (*)) buffer)) (setf (aref buffer (+ index 0)) (ldb (byte 8 0) u)) (setf (aref buffer (+ index 1)) (ldb (byte 8 8) u)) (setf (aref buffer (+ index 2)) (ldb (byte 8 16) u)) (setf (aref buffer (+ index 3)) (ldb (byte 8 24) u)))))

An example (possibly unwise):

(with-output-to-vector (stream) (write-string "time" stream) ;; Vector streams are bivalent, meaning they take octets and characters. ;; So we just wrote the string "time" and we'll overwrite it with the ;; value of (get-universal-time) encoded in little-endian 32-bit format. (%vector-stream-write-u32 (get-universal-time) stream 0))

As of this writing, that gives #(164 231 121 229) corresponding to the universal time 3849971620.

Which—the careful programmer will note—doesn't overflow 32 (unsigned) bits. That'll happen in 2036.

Allegro CL

ACL has with-output-to-buffer:

(with-output-to-buffer (stream) (write-string "foo" stream) (file-position stream 0) (write-char #\w stream))

Result of the above:


Which is similar to the result of with-output-to-string. The buffer wants to be a simple-array (and a vector, of course) with an element-type of either (unsigned-byte 8) or (signed-byte 8). Passing a non-simple array as the second argument can have peculiar effects. Consider this example, where the version of ACL is noted in the comment, and the default optimization settings are in effect:

;;; International Allegro CL Free Express Edition ;;; 10.1 [32-bit Windows] (Jan 27, 2021 17:58) ;;; Optimization settings: safety 1, space 1, speed 1, debug 2. (defvar *buffer* (make-array 20 :element-type '(unsigned-byte 8) :initial-element 0)) (defvar *displaced* (make-array 10 :element-type '(unsigned-byte 8) :displaced-index-offset 10 :displaced-to *buffer*)) (with-output-to-buffer (stream *displaced*) (write-string "0123456789" stream))

Then we have this output from ACL's Debug Window:

CG-USER(4): *buffer*
#(0 0 0 0 0 0 0 0 0 0 ...)
CG-USER(5): *displaced*
#<Printer Error, obj=#x21712102: Attempt to take the car of 231574861 which is not listp.>
CG-USER(6): (type-of *displaced*)
CG-USER(7): (aref *displaced* 0)
Error: Attempt to take the car of 231574861 which is not listp.
[condition type: TYPE-ERROR]
CG-USER(8): (length *displaced*)

As the saying goes: "Don't do that, then."

Programming Tips