Browse code
initial
fiddlerwoaroof authored on 13/06/2017 01:29:00
Showing 6 changed files
Showing 6 changed files
- .gitignore
- README.txt
- package.lisp
- stream-to-vector.lisp
- vector-update-stream.asd
- vector-update-stream.lisp
0 | 6 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,129 @@ |
1 |
+(in-package :vector-update-stream) |
|
2 |
+ |
|
3 |
+(defclass vector-update-stream (fundamental-binary-output-stream) |
|
4 |
+ ((vector :accessor vector-stream-vector :initarg :vector)) |
|
5 |
+ (:documentation "A binary output stream that writes its data to an associated vector.")) |
|
6 |
+ |
|
7 |
+(deftype octet () |
|
8 |
+ '(unsigned-byte 8)) |
|
9 |
+ |
|
10 |
+(defun make-update-stream (array) |
|
11 |
+ (unless (and (array-has-fill-pointer-p array) |
|
12 |
+ (adjustable-array-p array)) |
|
13 |
+ (error "GET-STREAM-FOR-ARRAY requires an adjustable array with a fill pointer")) |
|
14 |
+ (make-instance 'vector-update-stream |
|
15 |
+ :vector array)) |
|
16 |
+ |
|
17 |
+(defun check-if-open (stream) |
|
18 |
+ "Checks if STREAM is open and signals an error otherwise." |
|
19 |
+ (unless (open-stream-p stream) |
|
20 |
+ (error 'flexi-streams:in-memory-stream-closed-error |
|
21 |
+ :stream stream))) |
|
22 |
+ |
|
23 |
+(defmethod stream-write-byte ((stream vector-update-stream) byte) |
|
24 |
+ "Writes a byte \(octet) by extending the underlying vector." |
|
25 |
+ (check-if-open stream) |
|
26 |
+ (with-accessors ((vector vector-stream-vector)) stream |
|
27 |
+ (let ((orig-fill-pointer (fill-pointer vector))) |
|
28 |
+ (handler-bind ((error (lambda (c) |
|
29 |
+ (declare (ignore c)) |
|
30 |
+ (setf (fill-pointer vector) orig-fill-pointer)))) |
|
31 |
+ (vector-push-extend byte vector))))) |
|
32 |
+ |
|
33 |
+(defmethod stream-write-sequence ((stream vector-update-stream) sequence start end &key) |
|
34 |
+ "Just calls VECTOR-PUSH-EXTEND repeatedly." |
|
35 |
+ (declare (fixnum start end)) |
|
36 |
+ (with-accessors ((vector vector-stream-vector)) stream |
|
37 |
+ (let ((orig-fill-pointer (fill-pointer vector))) |
|
38 |
+ (handler-bind ((error (lambda (c) |
|
39 |
+ (declare (ignore c)) |
|
40 |
+ (setf (fill-pointer vector) orig-fill-pointer)))) |
|
41 |
+ (loop for index of-type fixnum from start below end |
|
42 |
+ do (vector-push-extend (elt sequence index) vector)))) |
|
43 |
+ sequence)) |
|
44 |
+ |
|
45 |
+(defmethod stream-file-position ((stream vector-update-stream)) |
|
46 |
+ "Simply returns the fill pointer of the underlying vector." |
|
47 |
+ (with-accessors ((vector vector-stream-vector)) stream |
|
48 |
+ (fill-pointer vector))) |
|
49 |
+ |
|
50 |
+(defmethod (setf stream-file-position) (position-spec (stream vector-update-stream)) |
|
51 |
+ "Sets the fill pointer underlying vector if POSITION-SPEC is |
|
52 |
+acceptable. Adjusts the vector if necessary." |
|
53 |
+ (with-accessors ((vector vector-stream-vector)) |
|
54 |
+ stream |
|
55 |
+ (let* ((total-size (array-total-size vector)) |
|
56 |
+ (new-fill-pointer |
|
57 |
+ (case position-spec |
|
58 |
+ (:start 0) |
|
59 |
+ (:end |
|
60 |
+ (warn "File position designator :END doesn't really make sense for an output stream.") |
|
61 |
+ total-size) |
|
62 |
+ (otherwise |
|
63 |
+ (unless (integerp position-spec) |
|
64 |
+ (error 'flexi-streams:in-memory-stream-position-spec-error |
|
65 |
+ :format-control "Unknown file position designator: ~S." |
|
66 |
+ :format-arguments (list position-spec) |
|
67 |
+ :stream stream |
|
68 |
+ :position-spec position-spec)) |
|
69 |
+ (unless (<= 0 position-spec array-total-size-limit) |
|
70 |
+ (error 'flexi-streams:in-memory-stream-position-spec-error |
|
71 |
+ :format-control "File position designator ~S is out of bounds." |
|
72 |
+ :format-arguments (list position-spec) |
|
73 |
+ :stream stream |
|
74 |
+ :position-spec position-spec)) |
|
75 |
+ position-spec)))) |
|
76 |
+ (declare (fixnum total-size new-fill-pointer)) |
|
77 |
+ (when (> new-fill-pointer total-size) |
|
78 |
+ (adjust-array vector new-fill-pointer)) |
|
79 |
+ (setf (fill-pointer vector) new-fill-pointer) |
|
80 |
+ position-spec))) |
|
81 |
+ |
|
82 |
+(defmethod get-output-stream-sequence ((stream vector-update-stream) &key) |
|
83 |
+ "Returns a vector containing, in order, all the octets that have |
|
84 |
+been output to the IN-MEMORY stream STREAM. This operation clears any |
|
85 |
+octets on STREAM, so the vector contains only those octets which have |
|
86 |
+been output since the last call to GET-OUTPUT-STREAM-SEQUENCE or since |
|
87 |
+the creation of the stream, whichever occurred most recently. If |
|
88 |
+AS-LIST is true the return value is coerced to a list." |
|
89 |
+ (vector-stream-vector stream)) |
|
90 |
+ |
|
91 |
+(defmacro deftest (name (expected actual-init) (&rest bindings) &body actions) |
|
92 |
+ (alexandria:with-gensyms (assertion-template) |
|
93 |
+ `(let* ((expected ,expected) |
|
94 |
+ (actual ,actual-init) |
|
95 |
+ ,@bindings |
|
96 |
+ (,assertion-template (formatter "~&~a Test: ~:[fail~%~4texpected ~s~%~4tactual ~s~;succeed~]~%"))) |
|
97 |
+ (declare (ignorable ,assertion-template)) |
|
98 |
+ (flet ((check (assertion) |
|
99 |
+ (funcall ,assertion-template t ',name (funcall assertion expected actual) expected actual))) |
|
100 |
+ ,@actions) |
|
101 |
+ (values)))) |
|
102 |
+ |
|
103 |
+(defun test () |
|
104 |
+ (deftest write-sequence |
|
105 |
+ (#(1 2 3) (make-array 0 :adjustable t :fill-pointer 0)) |
|
106 |
+ ((vs (make-update-stream actual))) |
|
107 |
+ (write-sequence expected vs) |
|
108 |
+ (check 'serapeum:vector=)) |
|
109 |
+ |
|
110 |
+ (deftest write-sequence-undo-on-error |
|
111 |
+ (#(1 2 3) (make-array 0 :adjustable t :fill-pointer 0 :element-type 'octet)) |
|
112 |
+ ((vs (make-update-stream actual))) |
|
113 |
+ (write-sequence expected vs) |
|
114 |
+ (handler-case (write-sequence #(1 2 3 #\a) vs) |
|
115 |
+ (type-error (c) c (values))) |
|
116 |
+ (check 'serapeum:vector=)) |
|
117 |
+ |
|
118 |
+ (deftest write-byte |
|
119 |
+ (#(1) (make-array 0 :adjustable t :fill-pointer 0)) |
|
120 |
+ ((vs (make-update-stream actual))) |
|
121 |
+ (write-byte 1 vs) |
|
122 |
+ (check 'serapeum:vector=)) |
|
123 |
+ |
|
124 |
+ (deftest write-byte-undo-on-error |
|
125 |
+ (#() (make-array 0 :adjustable t :fill-pointer 0 :element-type 'octet)) |
|
126 |
+ ((vs (make-update-stream actual))) |
|
127 |
+ (handler-case (stream-write-byte vs #\a) |
|
128 |
+ (type-error (c) c (values))) |
|
129 |
+ (check 'serapeum:vector=))) |
0 | 130 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,14 @@ |
1 |
+;;;; vector-update-stream.asd |
|
2 |
+ |
|
3 |
+(asdf:defsystem #:vector-update-stream |
|
4 |
+ :description "A stream that updates a user-provided backing vector, based on flexi-streams" |
|
5 |
+ :author "Ed Langley <fiddlerwoaroof@gmail.com" |
|
6 |
+ :license "BSD-2-CLAUSE" |
|
7 |
+ :depends-on (#:trivial-gray-streams |
|
8 |
+ #:flexi-streams |
|
9 |
+ #:alexandria |
|
10 |
+ #:serapeum) |
|
11 |
+ :serial t |
|
12 |
+ :components ((:file "package") |
|
13 |
+ (:file "stream-to-vector"))) |
|
14 |
+ |