2022-01-24 11:56:27 +01:00
|
|
|
;;; streams.lisp --- En/De-coding Streams
|
2021-08-21 15:29:43 +02:00
|
|
|
|
2022-01-24 11:56:27 +01:00
|
|
|
;;; Copyright (C) 2012 by Walter C. Pelissero
|
refactor(mime4cl): replace *-input-adapter-stream with flexi-streams
The input adapter streams were input streams yielding either binary or
character data that could be constructed from a variable data source.
The stream would take care not to destroy the underlying data
source (i.e. not close it if it was a stream), so similar to with
FILE-PORTIONs, but simpler.
Unfortunately, the implementation was quite inefficient: They are
ultimately defined in terms of a function that retrieves the next
character in the source. This only allows for an implementation of
READ-CHAR (and READ-BYTE). Thanks to cl/8559, READ-SEQUENCE can be used
on e.g. FILE-PORTION, but this was still negated by a input adapter
based on one—then, READ-SEQUENCE would need to fall back on READ-CHAR or
READ-BYTE again.
Luckily, we can replace BINARY-INPUT-ADAPTER-STREAM and
CHARACTER-INPUT-ADAPTER-STREAM with a much simpler abstraction: Instead
of extra stream classes, we have a function, MAKE-INPUT-ADAPTER, which
returns an appropriate instance of FLEXI-STREAM based on a given source.
This way, the need for a distinction between binary and character input
adapter is eliminated, since FLEXI-STREAMS supports both binary and
character reads (external format is not yet handled, though).
Consequently, the :binary keyword argument to MIME-BODY-STREAM can be
dropped.
flexi-streams provides stream classes for everything except a stream
that doesn't close the underlying one. Since we have already implemented
this in POSITIONED-FLEXI-INPUT-STREAM, we can split this functionality
into a new superclass ADAPTER-FLEXI-INPUT-STREAM.
This change also allows addressing the performance regression
encountered in cl/8559: It seems that flexi-streams performs worse when
we are reading byte by byte or char by char. (After this change mblog is
still two times slower than on r/6150.) By eliminating the adapter
streams, we can start utilizing READ-SEQUENCE via decoding code that
supports it (i.e. qbase64) and bring performance on par with r/6150
again. Surely there are also ways to gain back even more performance
which has to be determined using profiling. Buffering more aggressively
seems like a sure bet, though.
Switching to flexi-streams still seems like a no-brainer, as it allows
us to drop a lot of code that was quite hacky (e.g. DELIMITED-INPUT-
STREAM) and implements en/decoding handling we did not support before,
but would need for improved correctness.
Change-Id: Ie2d1f4e42b47512a5660a1ccc0deeec2bff9788d
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8581
Autosubmit: sterni <sternenseemann@systemli.org>
Reviewed-by: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
2023-05-15 23:36:40 +02:00
|
|
|
;;; Copyright (C) 2021-2023 by the TVL Authors
|
2021-08-21 15:29:43 +02:00
|
|
|
|
2022-01-24 11:56:27 +01:00
|
|
|
;;; Author: Walter C. Pelissero <walter@pelissero.de>
|
|
|
|
;;; Project: mime4cl
|
2021-08-21 15:29:43 +02:00
|
|
|
|
|
|
|
;;; This library is free software; you can redistribute it and/or
|
|
|
|
;;; modify it under the terms of the GNU Lesser General Public License
|
|
|
|
;;; as published by the Free Software Foundation; either version 2.1
|
|
|
|
;;; of the License, or (at your option) any later version.
|
|
|
|
;;; This library is distributed in the hope that it will be useful,
|
|
|
|
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
;;; Lesser General Public License for more details.
|
|
|
|
;;; You should have received a copy of the GNU Lesser General Public
|
|
|
|
;;; License along with this library; if not, write to the Free
|
|
|
|
;;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
|
|
;;; 02111-1307 USA
|
|
|
|
|
|
|
|
(in-package :mime4cl)
|
|
|
|
|
2023-05-16 16:22:44 +02:00
|
|
|
(defun flexi-stream-root-stream (stream)
|
|
|
|
"Return the non FLEXI-STREAM stream a given chain of FLEXI-STREAMs is based on."
|
|
|
|
(if (typep stream 'flexi-stream)
|
|
|
|
(flexi-stream-root-stream (flexi-stream-stream stream))
|
|
|
|
stream))
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
2021-08-21 15:29:43 +02:00
|
|
|
(defclass coder-stream-mixin ()
|
|
|
|
((real-stream :type stream
|
2023-03-26 19:32:35 +02:00
|
|
|
:initarg :underlying-stream
|
2022-01-19 14:39:58 +01:00
|
|
|
:reader real-stream)
|
2021-08-21 15:29:43 +02:00
|
|
|
(dont-close :initform nil
|
2022-01-19 14:39:58 +01:00
|
|
|
:initarg :dont-close)))
|
2021-08-21 15:29:43 +02:00
|
|
|
|
2022-01-24 11:56:27 +01:00
|
|
|
(defmethod stream-file-position ((stream coder-stream-mixin))
|
|
|
|
(file-position (slot-value stream 'real-stream)))
|
|
|
|
|
|
|
|
(defmethod (setf stream-file-position) (newval (stream coder-stream-mixin))
|
|
|
|
(file-position (slot-value stream 'real-stream) newval))
|
2021-08-21 15:29:43 +02:00
|
|
|
|
|
|
|
(defclass coder-input-stream-mixin (fundamental-binary-input-stream coder-stream-mixin)
|
|
|
|
())
|
|
|
|
(defclass coder-output-stream-mixin (fundamental-binary-output-stream coder-stream-mixin)
|
|
|
|
())
|
|
|
|
|
refactor(mime4cl): replace *-input-adapter-stream with flexi-streams
The input adapter streams were input streams yielding either binary or
character data that could be constructed from a variable data source.
The stream would take care not to destroy the underlying data
source (i.e. not close it if it was a stream), so similar to with
FILE-PORTIONs, but simpler.
Unfortunately, the implementation was quite inefficient: They are
ultimately defined in terms of a function that retrieves the next
character in the source. This only allows for an implementation of
READ-CHAR (and READ-BYTE). Thanks to cl/8559, READ-SEQUENCE can be used
on e.g. FILE-PORTION, but this was still negated by a input adapter
based on one—then, READ-SEQUENCE would need to fall back on READ-CHAR or
READ-BYTE again.
Luckily, we can replace BINARY-INPUT-ADAPTER-STREAM and
CHARACTER-INPUT-ADAPTER-STREAM with a much simpler abstraction: Instead
of extra stream classes, we have a function, MAKE-INPUT-ADAPTER, which
returns an appropriate instance of FLEXI-STREAM based on a given source.
This way, the need for a distinction between binary and character input
adapter is eliminated, since FLEXI-STREAMS supports both binary and
character reads (external format is not yet handled, though).
Consequently, the :binary keyword argument to MIME-BODY-STREAM can be
dropped.
flexi-streams provides stream classes for everything except a stream
that doesn't close the underlying one. Since we have already implemented
this in POSITIONED-FLEXI-INPUT-STREAM, we can split this functionality
into a new superclass ADAPTER-FLEXI-INPUT-STREAM.
This change also allows addressing the performance regression
encountered in cl/8559: It seems that flexi-streams performs worse when
we are reading byte by byte or char by char. (After this change mblog is
still two times slower than on r/6150.) By eliminating the adapter
streams, we can start utilizing READ-SEQUENCE via decoding code that
supports it (i.e. qbase64) and bring performance on par with r/6150
again. Surely there are also ways to gain back even more performance
which has to be determined using profiling. Buffering more aggressively
seems like a sure bet, though.
Switching to flexi-streams still seems like a no-brainer, as it allows
us to drop a lot of code that was quite hacky (e.g. DELIMITED-INPUT-
STREAM) and implements en/decoding handling we did not support before,
but would need for improved correctness.
Change-Id: Ie2d1f4e42b47512a5660a1ccc0deeec2bff9788d
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8581
Autosubmit: sterni <sternenseemann@systemli.org>
Reviewed-by: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
2023-05-15 23:36:40 +02:00
|
|
|
;; TODO(sterni): temporary, ugly measure to make flexi-streams happy
|
|
|
|
(defmethod stream-element-type ((stream coder-input-stream-mixin))
|
|
|
|
(declare (ignore stream))
|
|
|
|
'(unsigned-byte 8))
|
2021-08-21 15:29:43 +02:00
|
|
|
|
|
|
|
(defclass quoted-printable-decoder-stream (coder-input-stream-mixin quoted-printable-decoder) ())
|
|
|
|
(defclass 8bit-decoder-stream (coder-input-stream-mixin 8bit-decoder) ())
|
|
|
|
|
|
|
|
(defclass quoted-printable-encoder-stream (coder-output-stream-mixin quoted-printable-encoder) ())
|
|
|
|
(defclass base64-encoder-stream (coder-output-stream-mixin base64-encoder) ())
|
|
|
|
(defclass 8bit-encoder-stream (coder-output-stream-mixin 8bit-encoder) ())
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
|
|
(defmethod initialize-instance :after ((stream coder-stream-mixin) &key &allow-other-keys)
|
|
|
|
(unless (slot-boundp stream 'real-stream)
|
2023-03-26 19:32:35 +02:00
|
|
|
(error "REAL-STREAM is unbound. Must provide a :UNDERLYING-STREAM argument.")))
|
2021-08-21 15:29:43 +02:00
|
|
|
|
|
|
|
(defmethod initialize-instance ((stream coder-output-stream-mixin) &key &allow-other-keys)
|
|
|
|
(call-next-method)
|
|
|
|
(unless (slot-boundp stream 'output-function)
|
|
|
|
(setf (slot-value stream 'output-function)
|
2022-01-19 14:39:58 +01:00
|
|
|
#'(lambda (char)
|
|
|
|
(write-char char (slot-value stream 'real-stream))))))
|
2021-08-21 15:29:43 +02:00
|
|
|
|
|
|
|
(defmethod initialize-instance ((stream coder-input-stream-mixin) &key &allow-other-keys)
|
|
|
|
(call-next-method)
|
|
|
|
(unless (slot-boundp stream 'input-function)
|
|
|
|
(setf (slot-value stream 'input-function)
|
2022-01-19 14:39:58 +01:00
|
|
|
#'(lambda ()
|
|
|
|
(read-char (slot-value stream 'real-stream) nil)))))
|
2021-08-21 15:29:43 +02:00
|
|
|
|
|
|
|
(defmethod stream-read-byte ((stream coder-input-stream-mixin))
|
|
|
|
(or (decoder-read-byte stream)
|
|
|
|
:eof))
|
|
|
|
|
|
|
|
(defmethod stream-write-byte ((stream coder-output-stream-mixin) byte)
|
|
|
|
(encoder-write-byte stream byte))
|
|
|
|
|
|
|
|
(defmethod close ((stream coder-stream-mixin) &key abort)
|
|
|
|
(with-slots (real-stream dont-close) stream
|
|
|
|
(unless dont-close
|
|
|
|
(close real-stream :abort abort))))
|
|
|
|
|
|
|
|
(defmethod close ((stream coder-output-stream-mixin) &key abort)
|
|
|
|
(unless abort
|
|
|
|
(encoder-finish-output stream))
|
|
|
|
(call-next-method))
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
|
|
(defclass encoder-input-stream (fundamental-character-input-stream coder-stream-mixin)
|
|
|
|
((encoder)
|
|
|
|
(buffer-queue :initform (make-queue)))
|
|
|
|
(:documentation
|
|
|
|
"This is the base class for encoders with the direction swapped. It
|
|
|
|
reads from REAL-STREAM a stream of bytes, encodes it and returnes it
|
|
|
|
in a stream of character."))
|
|
|
|
|
|
|
|
(defclass quoted-printable-encoder-input-stream (encoder-input-stream) ())
|
|
|
|
(defclass base64-encoder-input-stream (encoder-input-stream) ())
|
|
|
|
(defclass 8bit-encoder-input-stream (fundamental-character-input-stream coder-stream-mixin) ())
|
|
|
|
|
|
|
|
(defmethod initialize-instance ((stream quoted-printable-encoder-input-stream) &key &allow-other-keys)
|
|
|
|
(call-next-method)
|
|
|
|
(with-slots (encoder buffer-queue) stream
|
|
|
|
(setf encoder
|
2022-01-19 14:39:58 +01:00
|
|
|
(make-instance 'quoted-printable-encoder
|
|
|
|
:output-function #'(lambda (char)
|
|
|
|
(queue-append buffer-queue char))))))
|
2021-08-21 15:29:43 +02:00
|
|
|
|
|
|
|
(defmethod initialize-instance ((stream base64-encoder-input-stream) &key &allow-other-keys)
|
|
|
|
(call-next-method)
|
|
|
|
(with-slots (encoder buffer-queue) stream
|
|
|
|
(setf encoder
|
2022-01-19 14:39:58 +01:00
|
|
|
(make-instance 'base64-encoder
|
|
|
|
:output-function #'(lambda (char)
|
|
|
|
(queue-append buffer-queue char))))))
|
2021-08-21 15:29:43 +02:00
|
|
|
|
|
|
|
(defmethod stream-read-char ((stream encoder-input-stream))
|
|
|
|
(with-slots (encoder buffer-queue real-stream) stream
|
|
|
|
(loop
|
|
|
|
while (queue-empty-p buffer-queue)
|
2023-05-18 00:14:11 +02:00
|
|
|
do (let ((byte (read-byte real-stream nil)))
|
2022-01-19 14:39:58 +01:00
|
|
|
(if byte
|
|
|
|
(encoder-write-byte encoder byte)
|
|
|
|
(progn
|
|
|
|
(encoder-finish-output encoder)
|
|
|
|
(queue-append buffer-queue :eof)))))
|
2021-08-21 15:29:43 +02:00
|
|
|
(queue-pop buffer-queue)))
|
|
|
|
|
|
|
|
|
|
|
|
(defmethod stream-read-char ((stream 8bit-encoder-input-stream))
|
|
|
|
(with-slots (real-stream) stream
|
|
|
|
(aif (read-byte real-stream nil)
|
2022-01-19 14:39:58 +01:00
|
|
|
(code-char it)
|
|
|
|
:eof)))
|
2021-08-21 15:29:43 +02:00
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
refactor(mime4cl): replace *-input-adapter-stream with flexi-streams
The input adapter streams were input streams yielding either binary or
character data that could be constructed from a variable data source.
The stream would take care not to destroy the underlying data
source (i.e. not close it if it was a stream), so similar to with
FILE-PORTIONs, but simpler.
Unfortunately, the implementation was quite inefficient: They are
ultimately defined in terms of a function that retrieves the next
character in the source. This only allows for an implementation of
READ-CHAR (and READ-BYTE). Thanks to cl/8559, READ-SEQUENCE can be used
on e.g. FILE-PORTION, but this was still negated by a input adapter
based on one—then, READ-SEQUENCE would need to fall back on READ-CHAR or
READ-BYTE again.
Luckily, we can replace BINARY-INPUT-ADAPTER-STREAM and
CHARACTER-INPUT-ADAPTER-STREAM with a much simpler abstraction: Instead
of extra stream classes, we have a function, MAKE-INPUT-ADAPTER, which
returns an appropriate instance of FLEXI-STREAM based on a given source.
This way, the need for a distinction between binary and character input
adapter is eliminated, since FLEXI-STREAMS supports both binary and
character reads (external format is not yet handled, though).
Consequently, the :binary keyword argument to MIME-BODY-STREAM can be
dropped.
flexi-streams provides stream classes for everything except a stream
that doesn't close the underlying one. Since we have already implemented
this in POSITIONED-FLEXI-INPUT-STREAM, we can split this functionality
into a new superclass ADAPTER-FLEXI-INPUT-STREAM.
This change also allows addressing the performance regression
encountered in cl/8559: It seems that flexi-streams performs worse when
we are reading byte by byte or char by char. (After this change mblog is
still two times slower than on r/6150.) By eliminating the adapter
streams, we can start utilizing READ-SEQUENCE via decoding code that
supports it (i.e. qbase64) and bring performance on par with r/6150
again. Surely there are also ways to gain back even more performance
which has to be determined using profiling. Buffering more aggressively
seems like a sure bet, though.
Switching to flexi-streams still seems like a no-brainer, as it allows
us to drop a lot of code that was quite hacky (e.g. DELIMITED-INPUT-
STREAM) and implements en/decoding handling we did not support before,
but would need for improved correctness.
Change-Id: Ie2d1f4e42b47512a5660a1ccc0deeec2bff9788d
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8581
Autosubmit: sterni <sternenseemann@systemli.org>
Reviewed-by: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
2023-05-15 23:36:40 +02:00
|
|
|
(defun make-custom-flexi-stream (class stream other-args)
|
|
|
|
(apply #'make-instance
|
|
|
|
class
|
|
|
|
:stream stream
|
|
|
|
(mapcar (lambda (x)
|
|
|
|
;; make-flexi-stream has a discrepancy between :initarg of
|
|
|
|
;; make-instance and its &key which we mirror here.
|
|
|
|
(if (eq x :external-format) :flexi-stream-external-format x))
|
|
|
|
other-args)))
|
2021-08-21 15:29:43 +02:00
|
|
|
|
refactor(mime4cl): replace *-input-adapter-stream with flexi-streams
The input adapter streams were input streams yielding either binary or
character data that could be constructed from a variable data source.
The stream would take care not to destroy the underlying data
source (i.e. not close it if it was a stream), so similar to with
FILE-PORTIONs, but simpler.
Unfortunately, the implementation was quite inefficient: They are
ultimately defined in terms of a function that retrieves the next
character in the source. This only allows for an implementation of
READ-CHAR (and READ-BYTE). Thanks to cl/8559, READ-SEQUENCE can be used
on e.g. FILE-PORTION, but this was still negated by a input adapter
based on one—then, READ-SEQUENCE would need to fall back on READ-CHAR or
READ-BYTE again.
Luckily, we can replace BINARY-INPUT-ADAPTER-STREAM and
CHARACTER-INPUT-ADAPTER-STREAM with a much simpler abstraction: Instead
of extra stream classes, we have a function, MAKE-INPUT-ADAPTER, which
returns an appropriate instance of FLEXI-STREAM based on a given source.
This way, the need for a distinction between binary and character input
adapter is eliminated, since FLEXI-STREAMS supports both binary and
character reads (external format is not yet handled, though).
Consequently, the :binary keyword argument to MIME-BODY-STREAM can be
dropped.
flexi-streams provides stream classes for everything except a stream
that doesn't close the underlying one. Since we have already implemented
this in POSITIONED-FLEXI-INPUT-STREAM, we can split this functionality
into a new superclass ADAPTER-FLEXI-INPUT-STREAM.
This change also allows addressing the performance regression
encountered in cl/8559: It seems that flexi-streams performs worse when
we are reading byte by byte or char by char. (After this change mblog is
still two times slower than on r/6150.) By eliminating the adapter
streams, we can start utilizing READ-SEQUENCE via decoding code that
supports it (i.e. qbase64) and bring performance on par with r/6150
again. Surely there are also ways to gain back even more performance
which has to be determined using profiling. Buffering more aggressively
seems like a sure bet, though.
Switching to flexi-streams still seems like a no-brainer, as it allows
us to drop a lot of code that was quite hacky (e.g. DELIMITED-INPUT-
STREAM) and implements en/decoding handling we did not support before,
but would need for improved correctness.
Change-Id: Ie2d1f4e42b47512a5660a1ccc0deeec2bff9788d
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8581
Autosubmit: sterni <sternenseemann@systemli.org>
Reviewed-by: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
2023-05-15 23:36:40 +02:00
|
|
|
(defclass adapter-flexi-input-stream (flexi-input-stream)
|
refactor(3p/lisp/mime4cl): use flexi-streams and binary input
This refactor is driven by the following (ultimate) aims:
- Get rid of as much of the custom stream code in mime4cl which makes
less code to maintain in the future.
- Lay the groundwork for correct handling of 8bit transfer encoding:
The mime4cl we inherited assumes that any MIME message can be decoded
completely by the CL implementation (in SBCL's case using latin1)
into CHARACTERs. This is not necessarily the case. flexi-streams
allows changing how the stream is decoded on the fly and also has
support for reading the underlying bytes which is perfect for the
requirements decoding MIME has.
- Since flexi-streams uses trivial-gray-streams, it supports
READ-SEQUENCE. Taking advantage of this may improve decoding
performance significantly in the future.
This incurs the following changes:
- Naturally we now open given files as binary files in MIME-MESSAGE.
Given strings are encoded using STRING-TO-OCTETS and then passed on
to a new octet vector method. Instead of MY-STRING-INPUT-STREAM this
now uses flexi-streams' WITH-INPUT-FROM-SEQUENCE.
- OPEN-FILE-PORTION and OPEN-DECODED-FILE-PORTION need to be merged,
since the transfer encoding not only implies an extra decoder stream
that needs to be attached after file portion stream, but also imply a
certain encoding of the stream itself (mostly binary vs. ASCII).
As flexi-streams can change their encoding on the fly this could be
untangled again, but it is not strictly necessary.
As before, we use the DATA slot of the file portion to create a fresh
stream if possible. Instead of strings we now use an vector of octets
to match MIME-MESSAGE.
The actual portioned stream relies on POSITIONED-FLEXI-INPUT-STREAM, a
subclass of the stock FLEXI-INPUT-STREAM class, described below.
- POSITIONED-FLEXI-INPUT-STREAM replaces DELIMITED-INPUT-STREAM. It is
created using MAKE-POSITIONED-FLEXI-INPUT-STREAM which accepts the
same arguments as MAKE-FLEXI-STREAMS and, additionally, :IGNORE-CLOSE.
A POSITIONED-FLEXI-INPUT-STREAM works the same as an
FLEXI-INPUT-STREAM, but upon creation, the underlying stream is
rewinded or forwarded to the argument given by :POSITION using
FILE-POSITION.
If :IGNORE-CLOSE is T, a call to CLOSE is not forwarded to the
underlying stream.
Change-Id: I2d48c769bb110ca0b7cf52441bd63c1e1c2ccd04
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8559
Reviewed-by: sterni <sternenseemann@systemli.org>
Autosubmit: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
2023-05-03 18:19:41 +02:00
|
|
|
((ignore-close
|
|
|
|
:initform nil
|
|
|
|
:initarg :ignore-close
|
|
|
|
:documentation
|
|
|
|
"If T, calling CLOSE on the stream does nothing.
|
|
|
|
If NIL, the underlying stream is closed."))
|
refactor(mime4cl): replace *-input-adapter-stream with flexi-streams
The input adapter streams were input streams yielding either binary or
character data that could be constructed from a variable data source.
The stream would take care not to destroy the underlying data
source (i.e. not close it if it was a stream), so similar to with
FILE-PORTIONs, but simpler.
Unfortunately, the implementation was quite inefficient: They are
ultimately defined in terms of a function that retrieves the next
character in the source. This only allows for an implementation of
READ-CHAR (and READ-BYTE). Thanks to cl/8559, READ-SEQUENCE can be used
on e.g. FILE-PORTION, but this was still negated by a input adapter
based on one—then, READ-SEQUENCE would need to fall back on READ-CHAR or
READ-BYTE again.
Luckily, we can replace BINARY-INPUT-ADAPTER-STREAM and
CHARACTER-INPUT-ADAPTER-STREAM with a much simpler abstraction: Instead
of extra stream classes, we have a function, MAKE-INPUT-ADAPTER, which
returns an appropriate instance of FLEXI-STREAM based on a given source.
This way, the need for a distinction between binary and character input
adapter is eliminated, since FLEXI-STREAMS supports both binary and
character reads (external format is not yet handled, though).
Consequently, the :binary keyword argument to MIME-BODY-STREAM can be
dropped.
flexi-streams provides stream classes for everything except a stream
that doesn't close the underlying one. Since we have already implemented
this in POSITIONED-FLEXI-INPUT-STREAM, we can split this functionality
into a new superclass ADAPTER-FLEXI-INPUT-STREAM.
This change also allows addressing the performance regression
encountered in cl/8559: It seems that flexi-streams performs worse when
we are reading byte by byte or char by char. (After this change mblog is
still two times slower than on r/6150.) By eliminating the adapter
streams, we can start utilizing READ-SEQUENCE via decoding code that
supports it (i.e. qbase64) and bring performance on par with r/6150
again. Surely there are also ways to gain back even more performance
which has to be determined using profiling. Buffering more aggressively
seems like a sure bet, though.
Switching to flexi-streams still seems like a no-brainer, as it allows
us to drop a lot of code that was quite hacky (e.g. DELIMITED-INPUT-
STREAM) and implements en/decoding handling we did not support before,
but would need for improved correctness.
Change-Id: Ie2d1f4e42b47512a5660a1ccc0deeec2bff9788d
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8581
Autosubmit: sterni <sternenseemann@systemli.org>
Reviewed-by: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
2023-05-15 23:36:40 +02:00
|
|
|
(:documentation "FLEXI-STREAM that does not close the underlying stream on
|
|
|
|
CLOSE if :IGNORE-CLOSE is T."))
|
|
|
|
|
|
|
|
(defmethod close ((stream adapter-flexi-input-stream) &key abort)
|
|
|
|
(declare (ignore abort))
|
|
|
|
(with-slots (ignore-close) stream
|
|
|
|
(unless ignore-close
|
|
|
|
(call-next-method))))
|
|
|
|
|
|
|
|
(defun make-input-adapter (source)
|
|
|
|
(etypecase source
|
|
|
|
;; If it's already a stream, we need to make sure it's not closed by the adapter
|
|
|
|
(stream
|
|
|
|
(assert (input-stream-p source))
|
|
|
|
(if (and (typep source 'adapter-flexi-input-stream)
|
|
|
|
(slot-value source 'ignore-close))
|
|
|
|
source ; already ignores CLOSE
|
|
|
|
(make-adapter-flexi-input-stream source :ignore-close t)))
|
|
|
|
;; TODO(sterni): is this necessary? (maybe with (not *lazy-mime-decode*)?)
|
|
|
|
(string
|
|
|
|
(make-input-adapter (string-to-octets source)))
|
|
|
|
((vector (unsigned-byte 8))
|
|
|
|
(make-in-memory-input-stream source))
|
|
|
|
(pathname
|
|
|
|
(make-flexi-stream (open source :element-type '(unsigned-byte 8))))
|
|
|
|
(file-portion
|
|
|
|
(open-decoded-file-portion source))))
|
|
|
|
|
|
|
|
(defun make-adapter-flexi-input-stream (stream &rest args)
|
|
|
|
"Create a ADAPTER-FLEXI-INPUT-STREAM. Accepts the same keyword arguments as
|
|
|
|
MAKE-FLEXI-STREAM as well as :IGNORE-CLOSE. If T, the underlying stream is not
|
|
|
|
closed."
|
|
|
|
(make-custom-flexi-stream 'adapter-flexi-input-stream stream args))
|
|
|
|
|
|
|
|
(defclass positioned-flexi-input-stream (adapter-flexi-input-stream)
|
|
|
|
()
|
refactor(3p/lisp/mime4cl): use flexi-streams and binary input
This refactor is driven by the following (ultimate) aims:
- Get rid of as much of the custom stream code in mime4cl which makes
less code to maintain in the future.
- Lay the groundwork for correct handling of 8bit transfer encoding:
The mime4cl we inherited assumes that any MIME message can be decoded
completely by the CL implementation (in SBCL's case using latin1)
into CHARACTERs. This is not necessarily the case. flexi-streams
allows changing how the stream is decoded on the fly and also has
support for reading the underlying bytes which is perfect for the
requirements decoding MIME has.
- Since flexi-streams uses trivial-gray-streams, it supports
READ-SEQUENCE. Taking advantage of this may improve decoding
performance significantly in the future.
This incurs the following changes:
- Naturally we now open given files as binary files in MIME-MESSAGE.
Given strings are encoded using STRING-TO-OCTETS and then passed on
to a new octet vector method. Instead of MY-STRING-INPUT-STREAM this
now uses flexi-streams' WITH-INPUT-FROM-SEQUENCE.
- OPEN-FILE-PORTION and OPEN-DECODED-FILE-PORTION need to be merged,
since the transfer encoding not only implies an extra decoder stream
that needs to be attached after file portion stream, but also imply a
certain encoding of the stream itself (mostly binary vs. ASCII).
As flexi-streams can change their encoding on the fly this could be
untangled again, but it is not strictly necessary.
As before, we use the DATA slot of the file portion to create a fresh
stream if possible. Instead of strings we now use an vector of octets
to match MIME-MESSAGE.
The actual portioned stream relies on POSITIONED-FLEXI-INPUT-STREAM, a
subclass of the stock FLEXI-INPUT-STREAM class, described below.
- POSITIONED-FLEXI-INPUT-STREAM replaces DELIMITED-INPUT-STREAM. It is
created using MAKE-POSITIONED-FLEXI-INPUT-STREAM which accepts the
same arguments as MAKE-FLEXI-STREAMS and, additionally, :IGNORE-CLOSE.
A POSITIONED-FLEXI-INPUT-STREAM works the same as an
FLEXI-INPUT-STREAM, but upon creation, the underlying stream is
rewinded or forwarded to the argument given by :POSITION using
FILE-POSITION.
If :IGNORE-CLOSE is T, a call to CLOSE is not forwarded to the
underlying stream.
Change-Id: I2d48c769bb110ca0b7cf52441bd63c1e1c2ccd04
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8559
Reviewed-by: sterni <sternenseemann@systemli.org>
Autosubmit: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
2023-05-03 18:19:41 +02:00
|
|
|
(:documentation
|
|
|
|
"FLEXI-INPUT-STREAM that automatically advances the underlying :STREAM to
|
|
|
|
the location given by :POSITION. This uses FILE-POSITION internally, so it'll
|
|
|
|
only works if the underlying stream position is tracked in bytes. Note that
|
|
|
|
the underlying stream is still advanced, so having multiple instances of
|
|
|
|
POSITIONED-FLEXI-INPUT-STREAM based with the same underlying stream won't work
|
|
|
|
reliably.
|
refactor(mime4cl): replace *-input-adapter-stream with flexi-streams
The input adapter streams were input streams yielding either binary or
character data that could be constructed from a variable data source.
The stream would take care not to destroy the underlying data
source (i.e. not close it if it was a stream), so similar to with
FILE-PORTIONs, but simpler.
Unfortunately, the implementation was quite inefficient: They are
ultimately defined in terms of a function that retrieves the next
character in the source. This only allows for an implementation of
READ-CHAR (and READ-BYTE). Thanks to cl/8559, READ-SEQUENCE can be used
on e.g. FILE-PORTION, but this was still negated by a input adapter
based on one—then, READ-SEQUENCE would need to fall back on READ-CHAR or
READ-BYTE again.
Luckily, we can replace BINARY-INPUT-ADAPTER-STREAM and
CHARACTER-INPUT-ADAPTER-STREAM with a much simpler abstraction: Instead
of extra stream classes, we have a function, MAKE-INPUT-ADAPTER, which
returns an appropriate instance of FLEXI-STREAM based on a given source.
This way, the need for a distinction between binary and character input
adapter is eliminated, since FLEXI-STREAMS supports both binary and
character reads (external format is not yet handled, though).
Consequently, the :binary keyword argument to MIME-BODY-STREAM can be
dropped.
flexi-streams provides stream classes for everything except a stream
that doesn't close the underlying one. Since we have already implemented
this in POSITIONED-FLEXI-INPUT-STREAM, we can split this functionality
into a new superclass ADAPTER-FLEXI-INPUT-STREAM.
This change also allows addressing the performance regression
encountered in cl/8559: It seems that flexi-streams performs worse when
we are reading byte by byte or char by char. (After this change mblog is
still two times slower than on r/6150.) By eliminating the adapter
streams, we can start utilizing READ-SEQUENCE via decoding code that
supports it (i.e. qbase64) and bring performance on par with r/6150
again. Surely there are also ways to gain back even more performance
which has to be determined using profiling. Buffering more aggressively
seems like a sure bet, though.
Switching to flexi-streams still seems like a no-brainer, as it allows
us to drop a lot of code that was quite hacky (e.g. DELIMITED-INPUT-
STREAM) and implements en/decoding handling we did not support before,
but would need for improved correctness.
Change-Id: Ie2d1f4e42b47512a5660a1ccc0deeec2bff9788d
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8581
Autosubmit: sterni <sternenseemann@systemli.org>
Reviewed-by: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
2023-05-15 23:36:40 +02:00
|
|
|
Also supports :IGNORE-CLOSE of ADAPTER-FLEXI-INPUT-STREAM."))
|
refactor(3p/lisp/mime4cl): use flexi-streams and binary input
This refactor is driven by the following (ultimate) aims:
- Get rid of as much of the custom stream code in mime4cl which makes
less code to maintain in the future.
- Lay the groundwork for correct handling of 8bit transfer encoding:
The mime4cl we inherited assumes that any MIME message can be decoded
completely by the CL implementation (in SBCL's case using latin1)
into CHARACTERs. This is not necessarily the case. flexi-streams
allows changing how the stream is decoded on the fly and also has
support for reading the underlying bytes which is perfect for the
requirements decoding MIME has.
- Since flexi-streams uses trivial-gray-streams, it supports
READ-SEQUENCE. Taking advantage of this may improve decoding
performance significantly in the future.
This incurs the following changes:
- Naturally we now open given files as binary files in MIME-MESSAGE.
Given strings are encoded using STRING-TO-OCTETS and then passed on
to a new octet vector method. Instead of MY-STRING-INPUT-STREAM this
now uses flexi-streams' WITH-INPUT-FROM-SEQUENCE.
- OPEN-FILE-PORTION and OPEN-DECODED-FILE-PORTION need to be merged,
since the transfer encoding not only implies an extra decoder stream
that needs to be attached after file portion stream, but also imply a
certain encoding of the stream itself (mostly binary vs. ASCII).
As flexi-streams can change their encoding on the fly this could be
untangled again, but it is not strictly necessary.
As before, we use the DATA slot of the file portion to create a fresh
stream if possible. Instead of strings we now use an vector of octets
to match MIME-MESSAGE.
The actual portioned stream relies on POSITIONED-FLEXI-INPUT-STREAM, a
subclass of the stock FLEXI-INPUT-STREAM class, described below.
- POSITIONED-FLEXI-INPUT-STREAM replaces DELIMITED-INPUT-STREAM. It is
created using MAKE-POSITIONED-FLEXI-INPUT-STREAM which accepts the
same arguments as MAKE-FLEXI-STREAMS and, additionally, :IGNORE-CLOSE.
A POSITIONED-FLEXI-INPUT-STREAM works the same as an
FLEXI-INPUT-STREAM, but upon creation, the underlying stream is
rewinded or forwarded to the argument given by :POSITION using
FILE-POSITION.
If :IGNORE-CLOSE is T, a call to CLOSE is not forwarded to the
underlying stream.
Change-Id: I2d48c769bb110ca0b7cf52441bd63c1e1c2ccd04
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8559
Reviewed-by: sterni <sternenseemann@systemli.org>
Autosubmit: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
2023-05-03 18:19:41 +02:00
|
|
|
|
|
|
|
(defmethod initialize-instance ((stream positioned-flexi-input-stream)
|
|
|
|
&key &allow-other-keys)
|
2021-08-21 15:29:43 +02:00
|
|
|
(call-next-method)
|
refactor(3p/lisp/mime4cl): use flexi-streams and binary input
This refactor is driven by the following (ultimate) aims:
- Get rid of as much of the custom stream code in mime4cl which makes
less code to maintain in the future.
- Lay the groundwork for correct handling of 8bit transfer encoding:
The mime4cl we inherited assumes that any MIME message can be decoded
completely by the CL implementation (in SBCL's case using latin1)
into CHARACTERs. This is not necessarily the case. flexi-streams
allows changing how the stream is decoded on the fly and also has
support for reading the underlying bytes which is perfect for the
requirements decoding MIME has.
- Since flexi-streams uses trivial-gray-streams, it supports
READ-SEQUENCE. Taking advantage of this may improve decoding
performance significantly in the future.
This incurs the following changes:
- Naturally we now open given files as binary files in MIME-MESSAGE.
Given strings are encoded using STRING-TO-OCTETS and then passed on
to a new octet vector method. Instead of MY-STRING-INPUT-STREAM this
now uses flexi-streams' WITH-INPUT-FROM-SEQUENCE.
- OPEN-FILE-PORTION and OPEN-DECODED-FILE-PORTION need to be merged,
since the transfer encoding not only implies an extra decoder stream
that needs to be attached after file portion stream, but also imply a
certain encoding of the stream itself (mostly binary vs. ASCII).
As flexi-streams can change their encoding on the fly this could be
untangled again, but it is not strictly necessary.
As before, we use the DATA slot of the file portion to create a fresh
stream if possible. Instead of strings we now use an vector of octets
to match MIME-MESSAGE.
The actual portioned stream relies on POSITIONED-FLEXI-INPUT-STREAM, a
subclass of the stock FLEXI-INPUT-STREAM class, described below.
- POSITIONED-FLEXI-INPUT-STREAM replaces DELIMITED-INPUT-STREAM. It is
created using MAKE-POSITIONED-FLEXI-INPUT-STREAM which accepts the
same arguments as MAKE-FLEXI-STREAMS and, additionally, :IGNORE-CLOSE.
A POSITIONED-FLEXI-INPUT-STREAM works the same as an
FLEXI-INPUT-STREAM, but upon creation, the underlying stream is
rewinded or forwarded to the argument given by :POSITION using
FILE-POSITION.
If :IGNORE-CLOSE is T, a call to CLOSE is not forwarded to the
underlying stream.
Change-Id: I2d48c769bb110ca0b7cf52441bd63c1e1c2ccd04
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8559
Reviewed-by: sterni <sternenseemann@systemli.org>
Autosubmit: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
2023-05-03 18:19:41 +02:00
|
|
|
;; The :POSITION initarg is only informational for flexi-streams: It assumes
|
|
|
|
;; it is were the stream it got is already at and continuously updates it
|
|
|
|
;; for querying (via FLEXI-STREAM-POSITION) and bound checking.
|
|
|
|
;; Since we have streams that are not positioned correctly, we need to do this
|
|
|
|
;; here using FILE-POSITION. Note that assumes the underlying implementation
|
|
|
|
;; uses bytes for FILE-POSITION which is not guaranteed (probably some streams
|
|
|
|
;; even in SBCL don't).
|
|
|
|
(file-position (flexi-stream-stream stream) (flexi-stream-position stream)))
|
|
|
|
|
|
|
|
(defun make-positioned-flexi-input-stream (stream &rest args)
|
|
|
|
"Create a POSITIONED-FLEXI-INPUT-STREAM. Accepts the same keyword arguments as
|
|
|
|
MAKE-FLEXI-STREAM as well as :IGNORE-CLOSE. Causes the FILE-POSITION of STREAM to
|
|
|
|
be modified to match the :POSITION argument."
|
refactor(mime4cl): replace *-input-adapter-stream with flexi-streams
The input adapter streams were input streams yielding either binary or
character data that could be constructed from a variable data source.
The stream would take care not to destroy the underlying data
source (i.e. not close it if it was a stream), so similar to with
FILE-PORTIONs, but simpler.
Unfortunately, the implementation was quite inefficient: They are
ultimately defined in terms of a function that retrieves the next
character in the source. This only allows for an implementation of
READ-CHAR (and READ-BYTE). Thanks to cl/8559, READ-SEQUENCE can be used
on e.g. FILE-PORTION, but this was still negated by a input adapter
based on one—then, READ-SEQUENCE would need to fall back on READ-CHAR or
READ-BYTE again.
Luckily, we can replace BINARY-INPUT-ADAPTER-STREAM and
CHARACTER-INPUT-ADAPTER-STREAM with a much simpler abstraction: Instead
of extra stream classes, we have a function, MAKE-INPUT-ADAPTER, which
returns an appropriate instance of FLEXI-STREAM based on a given source.
This way, the need for a distinction between binary and character input
adapter is eliminated, since FLEXI-STREAMS supports both binary and
character reads (external format is not yet handled, though).
Consequently, the :binary keyword argument to MIME-BODY-STREAM can be
dropped.
flexi-streams provides stream classes for everything except a stream
that doesn't close the underlying one. Since we have already implemented
this in POSITIONED-FLEXI-INPUT-STREAM, we can split this functionality
into a new superclass ADAPTER-FLEXI-INPUT-STREAM.
This change also allows addressing the performance regression
encountered in cl/8559: It seems that flexi-streams performs worse when
we are reading byte by byte or char by char. (After this change mblog is
still two times slower than on r/6150.) By eliminating the adapter
streams, we can start utilizing READ-SEQUENCE via decoding code that
supports it (i.e. qbase64) and bring performance on par with r/6150
again. Surely there are also ways to gain back even more performance
which has to be determined using profiling. Buffering more aggressively
seems like a sure bet, though.
Switching to flexi-streams still seems like a no-brainer, as it allows
us to drop a lot of code that was quite hacky (e.g. DELIMITED-INPUT-
STREAM) and implements en/decoding handling we did not support before,
but would need for improved correctness.
Change-Id: Ie2d1f4e42b47512a5660a1ccc0deeec2bff9788d
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8581
Autosubmit: sterni <sternenseemann@systemli.org>
Reviewed-by: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
2023-05-15 23:36:40 +02:00
|
|
|
(make-custom-flexi-stream 'positioned-flexi-input-stream stream args))
|
2021-08-21 15:29:43 +02:00
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
refactor(3p/lisp/mime4cl): use flexi-streams and binary input
This refactor is driven by the following (ultimate) aims:
- Get rid of as much of the custom stream code in mime4cl which makes
less code to maintain in the future.
- Lay the groundwork for correct handling of 8bit transfer encoding:
The mime4cl we inherited assumes that any MIME message can be decoded
completely by the CL implementation (in SBCL's case using latin1)
into CHARACTERs. This is not necessarily the case. flexi-streams
allows changing how the stream is decoded on the fly and also has
support for reading the underlying bytes which is perfect for the
requirements decoding MIME has.
- Since flexi-streams uses trivial-gray-streams, it supports
READ-SEQUENCE. Taking advantage of this may improve decoding
performance significantly in the future.
This incurs the following changes:
- Naturally we now open given files as binary files in MIME-MESSAGE.
Given strings are encoded using STRING-TO-OCTETS and then passed on
to a new octet vector method. Instead of MY-STRING-INPUT-STREAM this
now uses flexi-streams' WITH-INPUT-FROM-SEQUENCE.
- OPEN-FILE-PORTION and OPEN-DECODED-FILE-PORTION need to be merged,
since the transfer encoding not only implies an extra decoder stream
that needs to be attached after file portion stream, but also imply a
certain encoding of the stream itself (mostly binary vs. ASCII).
As flexi-streams can change their encoding on the fly this could be
untangled again, but it is not strictly necessary.
As before, we use the DATA slot of the file portion to create a fresh
stream if possible. Instead of strings we now use an vector of octets
to match MIME-MESSAGE.
The actual portioned stream relies on POSITIONED-FLEXI-INPUT-STREAM, a
subclass of the stock FLEXI-INPUT-STREAM class, described below.
- POSITIONED-FLEXI-INPUT-STREAM replaces DELIMITED-INPUT-STREAM. It is
created using MAKE-POSITIONED-FLEXI-INPUT-STREAM which accepts the
same arguments as MAKE-FLEXI-STREAMS and, additionally, :IGNORE-CLOSE.
A POSITIONED-FLEXI-INPUT-STREAM works the same as an
FLEXI-INPUT-STREAM, but upon creation, the underlying stream is
rewinded or forwarded to the argument given by :POSITION using
FILE-POSITION.
If :IGNORE-CLOSE is T, a call to CLOSE is not forwarded to the
underlying stream.
Change-Id: I2d48c769bb110ca0b7cf52441bd63c1e1c2ccd04
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8559
Reviewed-by: sterni <sternenseemann@systemli.org>
Autosubmit: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
2023-05-03 18:19:41 +02:00
|
|
|
;; TODO(sterni): test correct behavior with END NIL
|
2021-08-21 15:29:43 +02:00
|
|
|
(defstruct file-portion
|
2023-03-26 19:32:35 +02:00
|
|
|
data ; string or a pathname
|
2021-08-21 15:29:43 +02:00
|
|
|
encoding
|
|
|
|
start
|
|
|
|
end)
|
|
|
|
|
|
|
|
(defun open-decoded-file-portion (file-portion)
|
refactor(3p/lisp/mime4cl): use flexi-streams and binary input
This refactor is driven by the following (ultimate) aims:
- Get rid of as much of the custom stream code in mime4cl which makes
less code to maintain in the future.
- Lay the groundwork for correct handling of 8bit transfer encoding:
The mime4cl we inherited assumes that any MIME message can be decoded
completely by the CL implementation (in SBCL's case using latin1)
into CHARACTERs. This is not necessarily the case. flexi-streams
allows changing how the stream is decoded on the fly and also has
support for reading the underlying bytes which is perfect for the
requirements decoding MIME has.
- Since flexi-streams uses trivial-gray-streams, it supports
READ-SEQUENCE. Taking advantage of this may improve decoding
performance significantly in the future.
This incurs the following changes:
- Naturally we now open given files as binary files in MIME-MESSAGE.
Given strings are encoded using STRING-TO-OCTETS and then passed on
to a new octet vector method. Instead of MY-STRING-INPUT-STREAM this
now uses flexi-streams' WITH-INPUT-FROM-SEQUENCE.
- OPEN-FILE-PORTION and OPEN-DECODED-FILE-PORTION need to be merged,
since the transfer encoding not only implies an extra decoder stream
that needs to be attached after file portion stream, but also imply a
certain encoding of the stream itself (mostly binary vs. ASCII).
As flexi-streams can change their encoding on the fly this could be
untangled again, but it is not strictly necessary.
As before, we use the DATA slot of the file portion to create a fresh
stream if possible. Instead of strings we now use an vector of octets
to match MIME-MESSAGE.
The actual portioned stream relies on POSITIONED-FLEXI-INPUT-STREAM, a
subclass of the stock FLEXI-INPUT-STREAM class, described below.
- POSITIONED-FLEXI-INPUT-STREAM replaces DELIMITED-INPUT-STREAM. It is
created using MAKE-POSITIONED-FLEXI-INPUT-STREAM which accepts the
same arguments as MAKE-FLEXI-STREAMS and, additionally, :IGNORE-CLOSE.
A POSITIONED-FLEXI-INPUT-STREAM works the same as an
FLEXI-INPUT-STREAM, but upon creation, the underlying stream is
rewinded or forwarded to the argument given by :POSITION using
FILE-POSITION.
If :IGNORE-CLOSE is T, a call to CLOSE is not forwarded to the
underlying stream.
Change-Id: I2d48c769bb110ca0b7cf52441bd63c1e1c2ccd04
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8559
Reviewed-by: sterni <sternenseemann@systemli.org>
Autosubmit: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
2023-05-03 18:19:41 +02:00
|
|
|
(with-slots (data encoding start end)
|
|
|
|
file-portion
|
|
|
|
(let* ((binary-stream
|
|
|
|
(etypecase data
|
|
|
|
(pathname
|
|
|
|
(open data :element-type '(unsigned-byte 8)))
|
|
|
|
((vector (unsigned-byte 8))
|
|
|
|
(flexi-streams:make-in-memory-input-stream data))
|
|
|
|
(stream
|
|
|
|
;; TODO(sterni): assert that bytes/flexi-stream
|
|
|
|
data)))
|
|
|
|
(params (ccase encoding
|
|
|
|
((:quoted-printable :base64) '(:external-format :us-ascii))
|
|
|
|
(:8bit '(:element-type (unsigned-byte 8)))
|
|
|
|
(:7bit '(:external-format :us-ascii))))
|
|
|
|
(portion-stream (apply #'make-positioned-flexi-input-stream
|
|
|
|
binary-stream
|
|
|
|
:position start
|
|
|
|
:bound end
|
|
|
|
;; if data is a stream we can't have a
|
|
|
|
;; FILE-PORTION without modifying it when
|
|
|
|
;; reading etc. The least we can do, though,
|
|
|
|
;; is forgo destroying it.
|
|
|
|
:ignore-close (typep data 'stream)
|
|
|
|
params))
|
|
|
|
(needs-decoder-stream (member encoding '(:quoted-printable
|
|
|
|
:base64))))
|
|
|
|
|
|
|
|
(if needs-decoder-stream
|
|
|
|
(make-instance
|
|
|
|
(ccase encoding
|
|
|
|
(:quoted-printable 'quoted-printable-decoder-stream)
|
2023-05-16 16:11:00 +02:00
|
|
|
(:base64 'qbase64:decode-stream))
|
refactor(3p/lisp/mime4cl): use flexi-streams and binary input
This refactor is driven by the following (ultimate) aims:
- Get rid of as much of the custom stream code in mime4cl which makes
less code to maintain in the future.
- Lay the groundwork for correct handling of 8bit transfer encoding:
The mime4cl we inherited assumes that any MIME message can be decoded
completely by the CL implementation (in SBCL's case using latin1)
into CHARACTERs. This is not necessarily the case. flexi-streams
allows changing how the stream is decoded on the fly and also has
support for reading the underlying bytes which is perfect for the
requirements decoding MIME has.
- Since flexi-streams uses trivial-gray-streams, it supports
READ-SEQUENCE. Taking advantage of this may improve decoding
performance significantly in the future.
This incurs the following changes:
- Naturally we now open given files as binary files in MIME-MESSAGE.
Given strings are encoded using STRING-TO-OCTETS and then passed on
to a new octet vector method. Instead of MY-STRING-INPUT-STREAM this
now uses flexi-streams' WITH-INPUT-FROM-SEQUENCE.
- OPEN-FILE-PORTION and OPEN-DECODED-FILE-PORTION need to be merged,
since the transfer encoding not only implies an extra decoder stream
that needs to be attached after file portion stream, but also imply a
certain encoding of the stream itself (mostly binary vs. ASCII).
As flexi-streams can change their encoding on the fly this could be
untangled again, but it is not strictly necessary.
As before, we use the DATA slot of the file portion to create a fresh
stream if possible. Instead of strings we now use an vector of octets
to match MIME-MESSAGE.
The actual portioned stream relies on POSITIONED-FLEXI-INPUT-STREAM, a
subclass of the stock FLEXI-INPUT-STREAM class, described below.
- POSITIONED-FLEXI-INPUT-STREAM replaces DELIMITED-INPUT-STREAM. It is
created using MAKE-POSITIONED-FLEXI-INPUT-STREAM which accepts the
same arguments as MAKE-FLEXI-STREAMS and, additionally, :IGNORE-CLOSE.
A POSITIONED-FLEXI-INPUT-STREAM works the same as an
FLEXI-INPUT-STREAM, but upon creation, the underlying stream is
rewinded or forwarded to the argument given by :POSITION using
FILE-POSITION.
If :IGNORE-CLOSE is T, a call to CLOSE is not forwarded to the
underlying stream.
Change-Id: I2d48c769bb110ca0b7cf52441bd63c1e1c2ccd04
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8559
Reviewed-by: sterni <sternenseemann@systemli.org>
Autosubmit: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
2023-05-03 18:19:41 +02:00
|
|
|
:underlying-stream portion-stream)
|
|
|
|
portion-stream))))
|