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 }