Re: [Sbcl-commits] master: Fix file-position on copmosite streams

Richard M Kreuter via Sbcl-devel <[email protected]>
Newsgroups gmane.lisp.steel-bank.devel
Message-ID <[email protected]>
Stas Boukarev <[email protected]> wrote:

> It always used to return something, I'm not going to change that.
> It was just returning the wrong thing for buffered streams.
> Evidently, nobody cares about it, so the status quo should remain.

I care, but I try not to propose incompatible changes till I see
somebody else float them. You incompatibly changed the methods for
two-way and echo streams, AFAICT because the ancient CMUCL methods gave
an arbitrary result. So I thought it a reasonable moment to raise the
observation that the ancient CMUCL method for concatenated streams, if
superficially less arbitrary, gives probably-non-conforming results no
matter how hard it must work to get a position from the first component.

But I won't belabor this any further.

Regards,
Richard


> On Wed, Aug 5, 2026 at 9:55 PM Richard M Kreuter <[email protected]> wrote:
> >
> > Hi Stas,
> >
> > IMO there are few reasons why FILE-POSITION should either do nothing (or
> > in the 2-arg case, perhaps error) on concatenated streams, too:
> >
> > 1) For unary FILE-POSITION, simply returning the position from the first
> >    component is not conforming, because the succession of those
> >    positions will not always increase monotonically as the client takes
> >    input from the concatenated stream, e.g.,
> >
> >    ;; FILE-POSITION is not explicitly required to work on any type of
> >    ;; stream, so consider these string streams as stand-ins for streams
> >    ;; on which FILE-POSITION works.
> >    (with-input-from-string (s1 "ab")
> >      (with-input-from-string (s2 "xy")
> >        (with-open-stream (cs (make-concatenated-stream s1 s2))
> >          (loop for p = (file-position cs)
> >                collect p
> >                unless (read-char cs nil)
> >                do (loop-finish)))))
> >    => (0 1 2 1 2)
> >
> >    It'd be possible for FILE-POSITION to transform the first component's
> >    position to produce a monotonically increasing result, but I don't
> >    think it's worthwhile, because any numeric result will be of little
> >    use with binary FILE-POSITION.
> >
> > 2) For binary FILE-POSITION, if a file position is to supposed be usable
> >    for going backwards in a stream, it's unclear how that could work on
> >    a concatenated stream that has already discarded an input
> >    component. For the previous example, after reading #\x, there's no
> >    way to reposition the concatenated stream back to re-read #\a or #\b,
> >    and after reaching EOF, there's no way to re-read anything.
> >
> > 3) Forwarding binary FILE-POSITION to the current input component also
> >    gives concatenated streams unusual, probably non-conforming,
> >    semantics when trying to reposition ahead in a stream. For example,
> >
> >    (with-input-from-string (s1 "ab")
> >      (with-input-from-string (s2 "xy")
> >        (with-open-stream (cs (make-concatenated-stream s1 s2))
> >          (when (file-position cs :end)
> >            (read-char cs nil)))))
> >    => #\x
> >
> >    I can't think of a reason to try to try reading after positioning to
> >    EOF, but the glossary entry for "file position designator" says :END
> >    means the position following the last element of the stream, which
> >    isn't happening there.
> >
> > That is, given how ANSI describes concatenated streams, it's not clear
> > how a conforming and useful FILE-POSITION could be implemented.
> >
> > And FWIW, none of the versions of ABCL, CCL, Clisp, ECL, or Allegro I
> > have around behave exactly as SBCL does for both the two code fragments
> > above. So users who somehow need an operator with the semantics of
> > SBCL's current FILE-POSITION on concatenated streams must write it
> > themselves. (They can conformingly, I think.)
> >
> > So ISTM the unary form should just be a no-op. The binary form is
> > allowed to be a no-op or, I think, to signal an error (stipulating that
> > any file position designator is "inappropriate" for a concatenated
> > stream); IDK which is better, though I note that it errors on CCL, so I
> > guess portable code must be prepared for that, too.
> >
> > Regards,
> > Richard
> >
> > stassats via Sbcl-commits writes:
> > > The branch "master" has been updated in SBCL:
> > >        via  bf87e9ab2c426d2231c2bcc14f77ce35c112d20e (commit)
> > >       from  8ee5b24141f3e37a3cd56e9917d0027cd4695e2e (commit)
> > >
> > > - Log -----------------------------------------------------------------
> > > commit bf87e9ab2c426d2231c2bcc14f77ce35c112d20e
> > > Author: Stas Boukarev <[email protected]>
> > > Date:   Mon Aug 3 23:51:48 2026 +0300
> > >
> > >     Fix file-position on copmosite streams
> > >
> > >     concatenated-stream wasn't processing buffered input correctly.
> > >     two-way-stream and echo-stream should just return NIL, as
> > >     file-position can't apply to both streams.
> > > ---
> > >  contrib/sb-simple-streams/simple-stream-tests.lisp |  6 ++----
> > >  src/code/stream.lisp                               |  6 ++++++
> > >  src/code/target-stream.lisp                        |  3 +++
> > >  tests/input-manifest.lisp-expr                     |  6 ++++++
> > >  tests/stream.pure.lisp                             | 16 ++++++++++++++++
> > >  xperfecthash63.lisp-expr                           |  6 ++++++
> > >  6 files changed, 39 insertions(+), 4 deletions(-)
> > >
> > > diff --git a/contrib/sb-simple-streams/simple-stream-tests.lisp b/contrib/sb-
> > > simple-streams/simple-stream-tests.lisp
> > > index 577071b35..7d152dee5 100644
> > > --- a/contrib/sb-simple-streams/simple-stream-tests.lisp
> > > +++ b/contrib/sb-simple-streams/simple-stream-tests.lisp
> > > @@ -779,8 +779,7 @@ Nothing to see here, move along.")
> > >  (deftest two-way-stream-16
> > >      ;; FILE-POSITION (via STREAM-MISC-DISPATCH)
> > >      (with-sc-test-stream (synonym)
> > > -      (eql (file-position (make-two-way-stream synonym synonym))
> > > -           (file-position synonym)))
> > > +      (null (file-position (make-two-way-stream synonym synonym))))
> > >    T)
> > >
> > >  ;; SYNONYM-STREAM tests repeated for ECHO-STREAM, where applicable
> > > @@ -850,8 +849,7 @@ Nothing to see here, move along.")
> > >  (deftest echo-stream-16
> > >      ;; FILE-POSITION (via STREAM-MISC-DISPATCH)
> > >      (with-sc-test-stream (*synonym*)
> > > -      (eql (file-position (make-echo-stream *synonym* *synonym*))
> > > -           (file-position *synonym*)))
> > > +      (null (file-position (make-echo-stream *synonym* *synonym*))))
> > >    T)
> > >
> > >  ;; SYNONYM-STREAM tests repeated for CONCATENATED-STREAM, where applicable
> > > diff --git a/src/code/stream.lisp b/src/code/stream.lisp
> > > index eef4680bc..de84b2445 100644
> > > --- a/src/code/stream.lisp
> > > +++ b/src/code/stream.lisp
> > > @@ -1121,6 +1121,9 @@
> > >             in-mode)))
> > >        (:close
> > >         (set-closed-flame stream))
> > > +      ((:get-file-position :set-file-position)
> > > +       ;; Unclear to which stream it should apply
> > > +       nil)
> > >        (t
> > >         (or (if in-ansi-stream-p
> > >                 (call-ansi-stream-misc in operation arg1)
> > > @@ -1217,6 +1220,9 @@
> > >        (:unread (when left (unread-char arg1 current)))
> > >        (:close
> > >         (set-closed-flame stream))
> > > +      (:get-file-position
> > > +       (when current
> > > +         (file-position current)))
> > >        (t
> > >         (when left
> > >           (if (ansi-stream-p current)
> > > diff --git a/src/code/target-stream.lisp b/src/code/target-stream.lisp
> > > index 5b486e57b..7444cb450 100644
> > > --- a/src/code/target-stream.lisp
> > > +++ b/src/code/target-stream.lisp
> > > @@ -220,6 +220,9 @@
> > >             in-mode)))
> > >        (:close
> > >         (set-closed-flame stream))
> > > +      ((:get-file-position :set-file-position)
> > > +       ;; Unclear to which stream it should apply
> > > +       nil)
> > >        (t
> > >         (or (if in-ansi-stream-p
> > >                 (call-ansi-stream-misc in operation arg1)
> > > diff --git a/tests/input-manifest.lisp-expr b/tests/input-manifest.lisp-expr
> > > index 57b97448d..408030b73 100644
> > > --- a/tests/input-manifest.lisp-expr
> > > +++ b/tests/input-manifest.lisp-expr
> > > @@ -118,8 +118,14 @@
> > >    "../contrib/sb-simd/test-suite/utilities.lisp"
> > >    "../contrib/sb-simd/test-suite/test-suite.lisp"
> > >    "../contrib/sb-simd/test-suite/test-arefs.lisp"
> > > +  "../contrib/sb-simd/test-suite/test-arefs-arm64.lisp"
> > > +  "../contrib/sb-simd/test-suite/test-arefs-x86-64.lisp"
> > >    "../contrib/sb-simd/test-suite/test-simple-simd-functions.lisp"
> > > +  "../contrib/sb-simd/test-suite/test-simple-simd-functions-arm64.lisp"
> > > +  "../contrib/sb-simd/test-suite/test-simple-simd-functions-x86-64.lisp"
> > >    "../contrib/sb-simd/test-suite/test-horizontal-functions.lisp"
> > > +  "../contrib/sb-simd/test-suite/test-horizontal-functions-arm64.lisp"
> > > +  "../contrib/sb-simd/test-suite/test-horizontal-functions-x86-64.lisp"
> > >    "../contrib/sb-simd/test-suite/test-hairy-simd-functions.lisp"
> > >    "../contrib/sb-simd/test-suite/test-packages.lisp")
> > >   ("sb-simple-streams.impure.lisp" "contrib/sb-simple-streams.fasl" "contrib/
> > > sb-bsd-sockets.fasl"
> > > diff --git a/tests/stream.pure.lisp b/tests/stream.pure.lisp
> > > index 25d11c85b..83ac08f15 100644
> > > --- a/tests/stream.pure.lisp
> > > +++ b/tests/stream.pure.lisp
> > > @@ -545,3 +545,19 @@
> > >        (assert (= (read-sequence x s) 1))
> > >        (assert (equalp d #(1 0 1)))
> > >        (assert (equalp x #(0))))))
> > > +
> > > +(with-test (:name :composite-streams-file-position)
> > > +  (with-open-file (str (or #+win32 "zero" "/dev/zero"))
> > > +    (read-char str)
> > > +    (assert (= (file-position (make-concatenated-stream str))
> > > +               (file-position str))))
> > > +  (with-open-file (str (or #+win32 "/dev/null" "/dev/zero") :if-exists :appe
> > > nd :direction :output)
> > > +    (write-char #\a str)
> > > +    (assert (= (file-position (make-broadcast-stream str))
> > > +               (file-position str))))
> > > +  (with-open-file (i (or #+win32 "zero" "/dev/zero"))
> > > +    (with-open-file (o (or #+win32 "/dev/null" "/dev/zero") :if-exists :appe
> > > nd :direction :output)
> > > +      (assert (null (file-position (make-echo-stream i o))))))
> > > +  (with-open-file (i (or #+win32 "zero" "/dev/zero"))
> > > +    (with-open-file (o (or #+win32 "/dev/null" "/dev/zero") :if-exists :appe
> > > nd :direction :output)
> > > +      (assert (null (file-position (make-two-way-stream i o)))))))
> > > diff --git a/xperfecthash63.lisp-expr b/xperfecthash63.lisp-expr
> > > index 98a6ed4bc..cc861bb33 100644
> > > --- a/xperfecthash63.lisp-expr
> > > +++ b/xperfecthash63.lisp-expr
> > > @@ -1788,5 +1788,11 @@
> > >  (#(A49305EF D0241AE7 E55E7F8C EE9A5410)
> > >   "(SB-PCL::%CLASS SB-PCL::%PARAMETER SB-PCL::%VARIABLE-REBINDING SPECIAL)"
> > >   "((& (>> val 8) 3))")
> > > +(#(0 2 A 1A 1E)
> > > + "(5 15 1 13 0)"
> > > + "((let ((tab #a((4) (unsigned-byte 8) 2 0 0 7)))
> > > +  (let ((b (& (>> val 1) #x3)))
> > > +   (let ((a (>> (<< val 27) 30)))
> > > +    (^ a (aref tab b))))))")
> > >  )
> > >  ;; EOF
> > >
> > > -----------------------------------------------------------------------
> > >
> > >
> > > hooks/post-receive
> > > --
> > > SBCL
> > >
> > >
> > > _______________________________________________
> > > Sbcl-commits mailing list
> > > [email protected]
> > > https://lists.sourceforge.net/lists/listinfo/sbcl-commits


_______________________________________________
Sbcl-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/sbcl-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.