1 /** 
2  * Object-oriented interfaces and classes for dealing with streams. The symbols
3  * defined in this module are only available when not in "BetterC" mode, as
4  * they require the use of the Garbage Collector.
5  */
6 module streams.interfaces;
7 
8 import streams.primitives;
9 
10 version (D_BetterC) {} else {
11 
12 /** 
13  * Interface defining an input stream object that reads elements from some
14  * resource.
15  */
16 interface InputStream(E) {
17     /** 
18      * Reads elements from a resource and writes them to `buffer`.
19      * Params:
20      *   buffer = The buffer to read elements into.
21      * Returns: The stream result.
22      */
23     StreamResult readFromStream(E[] buffer);
24 }
25 
26 /** 
27  * Interface defining an output stream object that writes elements to some
28  * resource.
29  */
30 interface OutputStream(E) {
31     /** 
32      * Writes elements from `buffer` to a resource.
33      * Params:
34      *   buffer = The buffer containing elements to write.
35      * Returns: The stream result.
36      */
37     StreamResult writeToStream(E[] buffer);
38 }
39 
40 /** 
41  * Interface defining a stream that is closable.
42  */
43 interface ClosableStream {
44     /** 
45      * Closes the stream.
46      * Returns: An optional stream error, if closing the stream fails.
47      */
48     OptionalStreamError closeStream();
49 }
50 
51 /** 
52  * Interface defining a stream that is flushable.
53  */
54 interface FlushableStream {
55     /** 
56      * Flushes the stream.
57      * Returns: An optional stream error, if flushing the stream fails.
58      */
59     OptionalStreamError flushStream();
60 }
61 
62 /** 
63  * Input stream implementation that wraps around a primitive input stream.
64  */
65 class InputStreamObject(S, E = StreamType!S) : InputStream!E if (isInputStream!(S, E)) {
66     private S stream;
67 
68     /**
69      * Constructs the input stream wrapper with the given base stream.
70      * Params:
71      *   stream = The stream to wrap in an object-oriented stream.
72      */
73     this(S stream) {
74         this.stream = stream;
75     }
76 
77     /** 
78      * Reads up to `buffer.length` elements from the wrapped input stream, and
79      * writes them to `buffer`.
80      * Params:
81      *   buffer = The buffer to read elements into.
82      * Returns: Either the number of elements read, or a stream error.
83      */
84     StreamResult readFromStream(E[] buffer) {
85         return this.stream.readFromStream(buffer);
86     }
87 
88     static if (isClosableStream!S) {
89         OptionalStreamError closeStream() {
90             return this.stream.closeStream();
91         }
92     }
93 }
94 
95 /** 
96  * Gets a new object-oriented input stream implementation that wraps the given
97  * input stream.
98  * Params:
99  *   stream = The stream to wrap.
100  * Returns: An input stream wrapper object.
101  */
102 InputStreamObject!(S, E) inputStreamObjectFor(S, E = StreamType!S)(S stream) if (isInputStream!(S, E)) {
103     return new InputStreamObject!(S, E)(stream);
104 }
105 
106 /** 
107  * Output stream implementation that wraps around a primitive output stream.
108  */
109 class OutputStreamObject(S, E = StreamType!S) : OutputStream!E if (isOutputStream!(S, E)) {
110     private S stream;
111 
112     /**
113      * Constructs the output stream wrapper with a reference to a primitive
114      * output stream.
115      * Params:
116      *   stream = The stream to wrap in an object-oriented stream.
117      */
118     this(S stream) {
119         this.stream = stream;
120     }
121 
122     /**
123      * Writes up to `buffer.length` elements to the wrapped output stream.
124      * Params:
125      *   buffer = The buffer to write elements from.
126      * Returns: Either the number of elements written, or a stream error.
127      */
128     StreamResult writeToStream(E[] buffer) {
129         return this.stream.writeToStream(buffer);
130     }
131 
132     static if (isClosableStream!S) {
133         OptionalStreamError closeStream() {
134             return this.stream.closeStream();
135         }
136     }
137 
138     static if (isFlushableStream!S) {
139         OptionalStreamError flushStream() {
140             return this.stream.flushStream();
141         }
142     }
143 }
144 
145 /** 
146  * Gets a new object-oriented output stream implementation that wraps the given
147  * output stream.
148  * Params:
149  *   stream = The stream to wrap.
150  * Returns: An output stream wrapper object.
151  */
152 OutputStreamObject!(S, E) outputStreamObjectFor(S, E = StreamType!S)(S stream) if (isOutputStream!(S, E)) {
153     return new OutputStreamObject!(S, E)(stream);
154 }
155 
156 unittest {
157     import streams;
158     // Test input stream wrapper.
159     auto sIn1 = arrayInputStreamFor!ubyte([1, 2, 3, 4]);
160     auto wrapIn1 = new InputStreamObject!(typeof(&sIn1))(&sIn1);
161     ubyte[] buffer1 = new ubyte[4];
162     assert(wrapIn1.readFromStream(buffer1) == StreamResult(4));
163     assert(buffer1 == [1, 2, 3, 4]);
164     // Test using the function to make it easier.
165     sIn1.reset();
166     wrapIn1 = inputStreamObjectFor(&sIn1);
167     assert(wrapIn1.readFromStream(buffer1[0 .. 2]) == StreamResult(2));
168     assert(buffer1[0 .. 2] == [1, 2]);
169 
170     // Test output stream wrapper.
171     auto sOut1 = byteArrayOutputStream();
172     auto wrapOut1 = new OutputStreamObject!(typeof(&sOut1))(&sOut1);
173     assert(wrapOut1.writeToStream([1]) == StreamResult(1));
174     assert(sOut1.toArrayRaw() == [1]);
175     wrapOut1 = outputStreamObjectFor(&sOut1);
176     assert(wrapOut1.writeToStream([2, 3, 4]) == StreamResult(3));
177     assert(sOut1.toArrayRaw() == [1, 2, 3, 4]);
178 }
179 
180 }