1 module aurorafw.core.io.stream;
2 
3 import core.exception;
4 import std.exception;
5 
6 version (unittest) import aurorafw.unit.assertion;
7 
8 enum Seek
9 {
10 	SET, /// beginning of the stream
11 	CUR, /// current position
12 	END /// end of the stream
13 }
14 
15 class StreamException : Exception
16 {
17 	mixin basicExceptionCtors;
18 }
19 
20 interface IStream
21 {
22 	/**
23 	 * A property that returns the length of the stream
24 	 *
25 	 * Returns: The length of the stream in bytes
26 	 */
27 	@property ulong length();
28 
29 	/**
30 	 * Current position
31 	 *
32 	 * Returns: The current positon in the stream
33 	 */
34 	@property ulong tell();
35 
36 	/**
37 	 * Check if empty
38 	 *
39 	 * Returns: True if the stream is empty
40 	 */
41 	@property bool empty();
42 
43 	/**
44 	 * Seekable
45 	 *
46 	 * Returns: True if the stream is seekable
47 	 */
48 	@property bool seekable();
49 
50 	/**
51 	 * Sets the current position in the stream
52 	 *
53 	 * Returns: The current positon in the stream
54 	 *
55 	 * Params:
56 	 *  pos = The number of bytes to offset from origin
57 	 *  origin = Position used as reference for the offset.
58 	 */
59 	ulong seek(in long pos, in Seek origin = Seek.SET);
60 
61 	/**
62 	 * Skip n positions in the stream
63 	 *
64 	 * Returns: The current position in the stream
65 	 *
66 	 * Params:
67 	 *  n = The number of bytes to skip
68 	 */
69 	ulong skip(ulong n);
70 
71 	/**
72 	 * Writable
73 	 *
74 	 * Returns: True if the stream is writeble
75 	 */
76 	@property bool writable();
77 
78 	/**
79 	 * Writes an unsigned byte to the stream
80 	 *
81 	 * Params:
82 	 *  b = The ubyte to write to the stream
83 	 */
84 	void write(in ubyte b);
85 
86 	/**
87 	 * Writes an array of unsigned bytes to the stream
88 	 *
89 	 * Params:
90 	 *  b = The array of ubyte to write to the stream
91 	 */
92 	void write(in ubyte[] b);
93 
94 	/**
95 	 * Readable
96 	 *
97 	 * Returns: True if the stream is readable
98 	 */
99 	@property bool readable();
100 
101 	/**
102 	 * Reads an unsigned byte from the stream
103 	 *
104 	 * Returns: The unsigned byte read from the stream
105 	 */
106 	ubyte read();
107 
108 	/**
109 	 * Reads an array of ubytes from the stream
110 	 *
111 	 * Returns: The array of unsigned bytes read from the stream
112 	 *
113 	 * Params:
114 	 *  n = The number of bytes to read from the stream
115 	 */
116 	ubyte[] read(in size_t n);
117 
118 	/**
119 	 * Reads the entire stream
120 	 *
121 	 * Returns: The array of unsigned bytes read from the stream
122 	 */
123 	ubyte[] data();
124 }
125 
126 version (unittest)
127 {
128 	// assuming the content of the stream is "abc"
129 	// and the function leave the stream at the beginning
130 	package void unittest_readable_stream(IStream s)
131 	{
132 		assertTrue(s.seekable);
133 		assertTrue(s.readable);
134 
135 		assertEquals(3, s.length);
136 		assertFalse(s.empty);
137 
138 		assertEquals(0, s.seek(0, Seek.SET));
139 		assertEquals("abc", s.data);
140 		assertEquals(0, s.seek(0, Seek.SET));
141 
142 		assertEquals('a', s.read());
143 		assertEquals("b", s.read(1));
144 		assertEquals(1, s.seek(-1, Seek.CUR));
145 		assertEquals("bc", s.data);
146 		assertEquals(2, s.seek(-1, Seek.END));
147 		assertEquals('c', s.read());
148 		expectThrows!StreamException(s.read());
149 
150 		assertTrue(s.empty);
151 		assertEmpty(s.data);
152 
153 		assertEquals(0, s.seek(0, Seek.SET));
154 	}
155 
156 	package void unittest_writable_stream(IStream s)
157 	{
158 		assertTrue(s.seekable);
159 		assertTrue(s.writable);
160 		ubyte[] arr = ['4', '5'];
161 		s.seek(0, Seek.END);
162 		s.write(arr);
163 		s.write('6');
164 		if (s.readable)
165 		{
166 			s.seek(-3, Seek.END);
167 			assertEquals(arr ~ '6', s.data);
168 		}
169 
170 		assertEquals(0, s.seek(0, Seek.SET));
171 	}
172 }