1 /**
2     Streams
3 
4     Base interface for Input/Output streams in nulib.
5 
6     Copyright:
7         Copyright © 2023-2025, Kitsunebi Games
8         Copyright © 2023-2025, Inochi2D Project
9     
10     License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
11     Authors:   Luna Nielsen
12 */
13 module nulib.io.stream;
14 import numem;
15 
16 public import nulib.io.stream.memstream;
17 
18 /**
19     The origin of a seek operation
20 */
21 enum SeekOrigin {
22 
23     /**
24         Seek from beginning of stream
25     */
26     start = 0,
27 
28     /**
29         Seek relative to the current position in the stream
30     */
31     relative = 1,
32 
33     /**
34         Seek relative to the end of the stream
35     */
36     end = 2
37 }
38 
39 /**
40     Potential errors which can be raised by Stream R/W operations.
41 
42     Stream errors start from value $(D 0) which indicates an end-of-file
43     event.
44 */
45 enum StreamError : ptrdiff_t {
46     
47     /**
48         End of file reached.
49     */
50     eof = 0,
51     
52     /**
53         Operation is not supported by the stream.
54     */
55     notSupported = -1,
56     
57     /**
58         Operation attempted to access out-of-range indices.
59     */
60     outOfRange = -2,
61 
62     /**
63         Stream is in an invalid state; such as a file handle
64         being closed.
65     */
66     invalidState = -3,
67 }
68 
69 enum StreamError
70     STREAM_ERROR_EOF = StreamError.eof,                     /// End of file reached.
71     STREAM_ERROR_NOT_SUPPORTED = StreamError.notSupported,  /// Operation is not supported by the stream.
72     STREAM_ERROR_OUT_OF_RANGE = StreamError.outOfRange,     /// Operation attempted to access out-of-range indices.
73     STREAM_ERROR_INVALID_STATE = StreamError.invalidState;  /// Stream is in an invalid state.
74 
75 /**
76     A stream that can either be read from or written to.
77 
78     This is the base class of all stream related classes.
79 
80     Note:
81         Stream objects should always be nothrow; use error
82         codes as provided by $(D StreamError) to specify 
83         failure states.
84 */
85 abstract
86 class Stream : NuObject {
87 @nogc nothrow @safe:
88 public:
89 
90     /**
91         Whether the stream can be read from.
92 
93         Returns:
94             $(D true) if you can read data from the stream,
95             $(D false) otherwise.
96     */
97     abstract @property bool canRead();
98 
99     /**
100         Whether the stream can be written to.
101 
102         Returns:
103             $(D true) if you can write data to the stream,
104             $(D false) otherwise.
105     */
106     abstract @property bool canWrite();
107     
108     /**
109         Whether the stream can be seeked.
110 
111         Returns:
112             $(D true) if you can seek through the stream,
113             $(D false) otherwise.
114     */
115     abstract @property bool canSeek();
116 
117     /**
118         Whether the stream can timeout during operations.
119 
120         Returns:
121             $(D true) if the stream may time out during operations,
122             $(D false) otherwise.
123     */
124     abstract @property bool canTimeout();
125 
126     /**
127         Whether the stream can be flushed to disk.
128 
129         Returns:
130             $(D true) if the stream may be flushed,
131             $(D false) otherwise.
132     */
133     abstract @property bool canFlush();
134 
135     /**
136         Length of the stream.
137 
138         Returns:
139             Length of the stream, or $(D -1) if the length is unknown.
140     */
141     abstract @property ptrdiff_t length();
142 
143     /**
144         Position in stream
145     
146         Returns
147             Position in the stream, or $(D -1) if the position is unknown.
148     */
149     abstract @property ptrdiff_t tell();
150 
151     /**
152         Timeout in milliseconds before a read operation will fail.
153 
154         Returns
155             A timeout in milliseconds, or $(D 0) if there's no timeout.
156     */
157     abstract @property int readTimeout();
158 
159     /**
160         Timeout in milliseconds before a write operation will fail.
161 
162         Returns
163             A timeout in milliseconds, or $(D 0) if there's no timeout.
164     */
165     abstract @property int writeTimeout();
166 
167     /**
168         Clears all buffers of the stream and causes data to be written to the underlying device.
169     
170         Returns:
171             $(D true) if the flush operation succeeded,
172             $(D false) otherwise.
173     */
174     abstract bool flush();
175 
176     /**
177         Sets the reading position within the stream
178 
179         Returns
180             The new position in the stream, or a $(D StreamError) if the 
181             seek operation failed.
182         
183         See_Also:
184             $(D StreamError)
185     */
186     abstract long seek(ptrdiff_t offset, SeekOrigin origin = SeekOrigin.start);
187 
188     /**
189         Closes the stream.
190     */
191     abstract void close();
192 
193     /**
194         Reads bytes from the specified stream in to the specified buffer
195         
196         Notes:
197             The position and length to read is specified by the slice of `buffer`.  
198             Use slicing operation to specify a range to read to.
199 
200         Returns:
201             The amount of bytes read from the stream, 
202             or a $(D StreamError).
203         
204         See_Also:
205             $(D StreamError)
206     */
207     abstract ptrdiff_t read(ubyte[] buffer);
208 
209     /**
210         Writes bytes from the specified buffer in to the stream
211 
212         Notes
213             The position and length to write is specified by the slice of `buffer`.  
214             Use slicing operation to specify a range to write from.
215 
216         Returns:
217             The amount of bytes read from the stream, 
218             or a $(D StreamError).
219         
220         See_Also:
221             $(D StreamError)
222     */
223     abstract ptrdiff_t write(ubyte[] buffer);
224 }
225 
226 /**
227     An exception that can be thrown to indicate that a required operation
228     is not supported by the given stream.
229 
230     Note:
231         These exceptions are there for convenience for wrappers built
232         on top of the base stream interface; such as stream readers/writers.
233         The core functionality of streams will never throw.
234 */
235 class StreamUnsupportedException : NuException {
236 @nogc:
237 public:
238 
239     /**
240         The stream the exception applies to.
241     */
242     Stream stream;
243 
244     // Destructor.
245     ~this() {
246 
247         // Ensure this doesn't end up getting freed.
248         stream = null;
249     }
250 
251     /**
252         Constructor.
253     */
254     this(Stream stream, string file = __FILE__, size_t line = __LINE__) {
255         this.stream = stream;
256 
257         super("The requested operation is not supported by the stream!", null, file, line);
258     }
259 }
260 
261 /**
262     An exception that can be thrown to indicate that a read operation
263     failed.
264 
265     Note:
266         These exceptions are there for convenience for wrappers built
267         on top of the base stream interface; such as stream readers/writers.
268         The core functionality of streams will never throw.
269 */
270 class StreamReadException : NuException {
271 @nogc:
272 public:
273 
274     /**
275         The stream the exception applies to.
276     */
277     Stream stream;
278 
279     // Destructor.
280     ~this() {
281 
282         // Ensure this doesn't end up getting freed.
283         stream = null;
284     }
285 
286     /**
287         Constructor.
288     */
289     this(Stream stream, string reason, string file = __FILE__, size_t line = __LINE__) {
290         this.stream = stream;
291 
292         super(reason, null, file, line);
293     }
294 }
295 
296 /**
297     An exception that can be thrown to indicate that a write operation
298     failed.
299 
300     Note:
301         These exceptions are there for convenience for wrappers built
302         on top of the base stream interface; such as stream readers/writers.
303         The core functionality of streams will never throw.
304 */
305 class StreamWriteException : NuException {
306 @nogc:
307 public:
308 
309     /**
310         The stream the exception applies to.
311     */
312     Stream stream;
313 
314     // Destructor.
315     ~this() {
316 
317         // Ensure this doesn't end up getting freed.
318         stream = null;
319     }
320 
321     /**
322         Constructor.
323     */
324     this(Stream stream, string reason, string file = __FILE__, size_t line = __LINE__) {
325         this.stream = stream;
326         
327         super(reason, null, file, line);
328     }
329 }