summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoutade <thegoofist@protonmail.com>2019-05-02 14:18:34 -0500
committerBoutade <thegoofist@protonmail.com>2019-05-02 14:18:34 -0500
commit34fca959a16c28791012d10b204a140c8cb3b8cc (patch)
treeca8166e0ad4f9579b855f496c8f55e7ed738696f
parentcb1b079041f3e4f3021cd81d5b9a943514f15d26 (diff)
refactored interface and added character-input-replay-stream class
-rw-r--r--package.lisp8
-rw-r--r--replay-streams.lisp194
2 files changed, 98 insertions, 104 deletions
diff --git a/package.lisp b/package.lisp
index 38050de..b2265d2 100644
--- a/package.lisp
+++ b/package.lisp
@@ -4,12 +4,8 @@
(:use #:cl #:trivial-gray-streams)
(:export
#:checkpoint
- #:recover-source
- #:replay-character-stream
- #:replay-finished-p
- #:replay-on
- #:rewind
#:rewind-to
- #:rewound-p
+ #:free-checkpoint
+ #:character-input-replay-stream
#:static-text-replay-stream
))
diff --git a/replay-streams.lisp b/replay-streams.lisp
index 3b7239d..6108284 100644
--- a/replay-streams.lisp
+++ b/replay-streams.lisp
@@ -9,42 +9,26 @@
(:documentation "Creates a reference that can be used to rewind the stream at a later time."))
(defgeneric rewind-to (stream checkpoint)
- (:documentation "Rewinds the stream to the checkpoint"))
-
-
-;; DEPRECATED
-(defgeneric rewind (rp-stream)
- (:documentation "Rewinds a stream and returns it. Returns a second value, T if
- the stream was rewound. Returns NIL if the stream had already been rewound."))
-
-;; DEPRECATED
-(defgeneric rewound-p (stream)
- (:documentation "Returns T if the stream has been rewound."))
-
-;; DEPRECATED
-(defgeneric replay-finished-p (stream)
- (:documentation "Returns T when reads replay is concluded, meaning that subsequent reads affect the underlying stream again"))
-
-;; DEPRECATED
-(defgeneric recover-source (stream)
- (:documentation "Recover the source stream of a replay stream"))
+ (:documentation "Rewinds the stream to the checkpoint."))
+(defgeneric free-checkpoint (stream point)
+ (:documentation "Indicates that a particular checkpoint is no longer needed."))
;;;; THE CLASSES
-
-;; DEPRECATED
-(defclass replay-character-stream (fundamental-character-input-stream)
- ((source :initarg :source)
- (log :initform (make-array 8 :element-type 'character :adjustable t :fill-pointer 0))
- (replay-mode :initform nil)
- (replay-stream :initform nil)))
-
(defclass static-text-replay-stream (fundamental-character-input-stream)
((text :initarg :text)
(head :initform 0)))
+(defclass character-input-replay-stream (fundamental-character-input-stream)
+ ((source :initarg :source)
+ (source-head :initform 0)
+ (head :initform 0)
+ (checkpoints :initform nil)
+ (log :initarg nil)
+ (log-start :initarg nil)))
+
;;;; TRIVAL-GRAY-STREAMS SUPPORT
(defmethod stream-read-char ((stream static-text-replay-stream))
@@ -61,96 +45,110 @@
nil))
-
-(defun stream-log-push (log char)
- (destructuring-bind (size) (array-dimensions log)
- (when (<= size (length log))
- ;; thanks to (eq a (adjust-array a ...)) I don't need to worry
- (adjust-array log (* 2 size) :element-type 'character :fill-pointer (length log)))
- (vector-push char log)))
-
-;; DEPRECATED
-(defmethod stream-read-char ((stream replay-character-stream))
- (with-slots (source log replay-mode replay-stream) stream
+(defmethod stream-read-char ((stream character-input-replay-stream))
+ (with-slots (source source-head head checkpoints log log-start) stream
(cond
- ;; If we are not in replay mode, we read from the source and write to the log.
- ((not replay-mode) (if (peek-char nil source nil nil)
- (let ((c (read-char source)))
- (stream-log-push log c)
- c)
- :eof))
- ;; Otherwise we must be in replay mode.
- ;; We check for data in the replay-stream and return it
- ((and replay-stream (peek-char nil replay-stream nil nil)) (read-char replay-stream))
-
- ;; If the replay stream is null or empty, we make sure to close it if
- ;; necessary and then we just return characters from the source for the
- ;; rest of this instance's lifetime
- (t
- ;; destroy replay stream if necessary
- (when replay-stream
- (close replay-stream)
- (setf replay-stream nil))
- ;; and read froom source
+ ;; if there is no log and there are no checkpoints, then read normally
+ ((and (null log)
+ (null checkpoints))
(if (peek-char nil source nil nil)
(read-char source)
- :eof)))))
+ :eof))
-;; DEPRECATED
-(defmethod stream-unread-char ((stream replay-character-stream) char)
- (with-slots (source log replay-mode replay-stream) stream
- (cond ((not replay-mode)
- ;; we're not replaying, so we have to remove the character from the
- ;; log and put and unread it from the source stream
- (vector-pop log)
- (unread-char char source))
+ ;; if the head is less than source-head, then we're reading from the log
+ ((< head source-head)
+ (incf head) ;; order matters, we use the incremented value next
+ (aref log (- head log-start 1)))
- ((and replay-mode replay-stream)
- ;; we're currently replaying content from the non-nill replay-stream
- (unread-char char replay-stream))
+ ;; otherwise we're reading from the stream but we may be logging our reads
+ ;; so if we're not at the end of the input, we've got some stuff to do
+ ((peek-char nil source nil nil)
+ (let ((char (read-char source)))
+ (incf head)
+ (incf source-head)
- (t
- ;; we're in replay-mode (hence not logging), but we've already
- ;; consumed all the recorded content, so it's really just the source
- ;; stream that we need to unread.
- (unread-char char source)))))
+ (if checkpoints
+ ;; if there are active checkpoints, we log this read
+ (stream-log-push log char)
+ ;; otherwise we set the log to nil
+ (setf log nil))
+ ;; finally we return the read char
+ char))
-(defmethod checkpoint ((stream static-text-replay-stream))
- (with-slots (head) stream
- head))
- ;(slot-value stream 'head))
+ ;; otherwise we're at the end
+ (t :eof))))
+(defmethod stream-unread-char ((stream character-input-replay-stream) char)
+ (with-slots (source source-head head checkpoints log) stream
+ (cond
+ ;; not logging, we're just working with the source stream
+ ((and (null log) (null checkpoints))
+ (decf head)
+ (decf source-head)
+ (unread-char char source))
+
+ ;; we are reading-from the log
+ ((< head source-head)
+ (decf head))
+
+ ;; otherwise we're reading from the source, but might be logging
+ (t
+ ;; if there are checpoints, then we're logging, so we pop the log
+ (when checkpoints
+ (vector-pop log))
+
+ ;; otherwise this is just like the first condition
+ (decf head)
+ (decf source-head)
+ (unread-char char source)))))
-(defmethod rewind-to ((stream static-text-replay-stream) checkpoint)
- (with-slots (head) stream
- (setf head checkpoint)))
- ;;(setf (slot-value stream 'head) checkpoint))
-(defun replay-on (stream)
- (make-instance 'replay-character-stream :source stream))
+(defun stream-log-push (log char)
+ (destructuring-bind (size) (array-dimensions log)
+ (when (<= size (length log))
+ ;; thanks to (eq a (adjust-array a ...)) I don't need to worry
+ (adjust-array log (* 2 size) :element-type 'character :fill-pointer (length log)))
+ (vector-push char log)))
+;;; METHOD IMPLEMENTATIONS: STATIC-TEXT-REPLAY-STREAM
+(defmethod checkpoint ((stream static-text-replay-stream))
+ (with-slots (head) stream
+ head))
-(defmethod rewind ((rp-stream replay-character-stream))
- (with-slots (log replay-mode replay-stream) rp-stream
- (if replay-mode (values rp-stream nil)
- (progn
- (setf replay-mode t)
- (setf replay-stream (make-string-input-stream log))
- (values rp-stream t)))))
+(defmethod rewind-to ((stream static-text-replay-stream) checkpoint)
+ (with-slots (head) stream
+ (setf head checkpoint))
+ t)
+(defmethod free-checkpoint ((stream static-text-replay-stream) checkpoint)
+ t)
-(defmethod rewound-p ((stream replay-character-stream))
- (slot-value stream 'replay-mode))
+;;; METHOD IMPLEMENTATIONS: CHARACTER-INPUT-REPLAY-STREAM
+(defmethod checkpoint ((stream character-input-replay-stream))
+ (with-slots (head checkpoints log log-start) stream
-(defmethod replay-finished-p ((stream replay-character-stream))
- (with-slots (replay-mode replay-stream) stream
- (and replay-mode (not (peek-char nil replay-stream nil nil)))))
+ ;; if the log does not already exist, make a new one and set the log-start
+ (when (not log)
+ (setf log (make-array 64 :element-type 'character :adjustable t :fill-pointer 0))
+ (setf log-start head))
+ ;; add the checkpoint and return it
+ (push head checkpoints)
+ head))
-(defmethod recover-source ((stream replay-character-stream))
- (slot-value stream 'source))
+(defmethod rewind-to ((stream character-input-replay-stream) point)
+ (with-slots (head checkpoints) stream
+ (setf head point)
+ ;; reqinding to a point clobbers all "future" checkpoints
+ (setf checkpoints (remove-if (lambda (pt) (>= pt point)) checkpoints))
+ t))
+
+(defmethod free-checkpoint ((stream character-input-replay-stream) point)
+ (with-slots (checkpoints) stream
+ (setf checkpoints (remove point checkpoints)))
+ t)