1 /*
2 Copyright (c) 2014 Martin Cejp
3
4 Boost Software License - Version 1.0 - August 17th, 2003
5
6 Permission is hereby granted, free of charge, to any person or organization
7 obtaining a copy of the software and accompanying documentation covered by
8 this license (the "Software") to use, reproduce, display, distribute,
9 execute, and transmit the Software, and to prepare derivative works of the
10 Software, and to permit third-parties to whom the Software is furnished to
11 do so, all subject to the following:
12
13 The copyright notices in the Software and this entire statement, including
14 the above license grant, this restriction and the following disclaimer,
15 must be included in all copies of the Software, in whole or in part, and
16 all derivative works of the Software, unless such copies or derivative
17 works are solely in the form of machine-executable object code generated by
18 a source language processor.
19
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 DEALINGS IN THE SOFTWARE.
27 */
28
29 // dimage is actually stripped out part of dlib - just to support reading PNG and JPEG
30 //dlib.core.stream
31 module dimage.stream;
32
33 import std.bitmanip;
34 import std.stdint;
35 import std.conv;
36
37 //import dlib.core.memory;
38
39 alias StreamPos = uint64_t;
40 alias StreamSize = uint64_t;
41 alias StreamOffset = int64_t;
42
43 class SeekException : Exception
44 {
45 this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null)
46 {
47 super(msg, file, line, next);
48 }
49 }
50
51 /// Seekable
52 interface Seekable
53 {
54 // Won't throw on invalid position, may throw on a more serious error.
55
56 StreamPos getPosition() @property;
57 bool setPosition(StreamPos pos);
58 StreamSize size();
59
60 // Throw-on-error wrappers
61
62 final StreamPos position(StreamPos pos)
63 {
64 if (!setPosition(pos))
65 throw new SeekException("Cannot set Seekable position to " ~ pos.to!string);
66
67 return pos;
68 }
69
70 final StreamPos position()
71 {
72 return getPosition();
73 }
74
75 // TODO: Non-throwing version
76 final StreamPos seek(StreamOffset amount)
77 {
78 immutable StreamPos seekTo = getPosition() + amount;
79
80 if (!setPosition(seekTo))
81 throw new SeekException("Cannot set Seekable position to " ~ seekTo.to!string);
82
83 return seekTo;
84 }
85 }
86
87 /// Stream
88 interface Stream : Seekable
89 {
90 void close();
91 bool seekable();
92 }
93
94 interface InputStream : Stream
95 {
96 // Won't throw on EOF, may throw on a more serious error.
97
98 bool readable();
99 size_t readBytes(void* buffer, size_t count);
100
101 /// Read array.length elements into an pre-allocated array.
102 /// Returns: true if all elements were read, false otherwise
103 final bool fillArray(T)(T[] array)
104 {
105 immutable size_t len = array.length * T.sizeof;
106 return readBytes(array.ptr, len) == len;
107 }
108
109 /// Read an integer in little-endian encoding
110 final bool readLE(T)(T* value)
111 {
112 ubyte[T.sizeof] buffer;
113
114 if (readBytes(buffer.ptr, buffer.length) != buffer.length)
115 return false;
116
117 *value = littleEndianToNative!T(buffer);
118 return true;
119 }
120
121 /// Read an integer in big-endian encoding
122 final bool readBE(T)(T* value)
123 {
124 ubyte[T.sizeof] buffer;
125
126 if (readBytes(buffer.ptr, buffer.length) != buffer.length)
127 return false;
128
129 *value = bigEndianToNative!T(buffer);
130 return true;
131 }
132 }
133
134 interface OutputStream : Stream
135 {
136 // Won't throw on full disk, may throw on a more serious error.
137
138 void flush();
139 bool writeable();
140 size_t writeBytes(const void* buffer, size_t count);
141
142 /// Write array.length elements from array.
143 /// Returns: true if all elements were written, false otherwise
144 final bool writeArray(T)(const T[] array)
145 {
146 immutable size_t len = array.length * T.sizeof;
147 return writeBytes(array.ptr, len) == len;
148 }
149
150 /// Write a string as zero-terminated
151 /// Returns: true on success, false otherwise
152 final bool writeStringz(string text)
153 {
154 ubyte[1] zero = [0];
155
156 return writeBytes(text.ptr, text.length)
157 && writeBytes(zero.ptr, zero.length);
158 }
159
160 /// Write an integer in little-endian encoding
161 final bool writeLE(T)(const T value)
162 {
163 ubyte[T.sizeof] buffer = nativeToLittleEndian!T(value);
164
165 return writeBytes(buffer.ptr, buffer.length) == buffer.length;
166 }
167
168 /// Write an integer in big-endian encoding
169 final bool writeBE(T)(const T value)
170 {
171 ubyte[T.sizeof] buffer = nativeToBigEndian!T(value);
172
173 return writeBytes(buffer.ptr, buffer.length) == buffer.length;
174 }
175 }
176
177 interface IOStream : InputStream, OutputStream
178 {
179 }
180
181 StreamSize copyFromTo(InputStream input, OutputStream output)
182 {
183 ubyte[0x1000] buffer;
184 StreamSize total = 0;
185
186 while (input.readable)
187 {
188 size_t have = input.readBytes(buffer.ptr, buffer.length);
189
190 if (have == 0)
191 break;
192
193 output.writeBytes(buffer.ptr, have);
194 total += have;
195 }
196
197 return total;
198 }
199
200 // TODO: Move this?
201 // TODO: Add OutputStream methods
202 class ArrayStream : InputStream {
203 import std.algorithm;
204
205 this() {
206 }
207
208 this(ubyte[] data, size_t size) {
209 assert(size_ <= data.length);
210
211 this.size_ = size;
212 this.data = data;
213 }
214
215 override void close() {
216 this.pos = 0;
217 this.size_ = 0;
218 this.data = null;
219 }
220
221 override bool readable() {
222 return pos < size_;
223 }
224
225 override size_t readBytes(void* buffer, size_t count) {
226 import std.c.string;
227
228 count = min(count, size_ - pos);
229
230 // whoops, memcpy out of nowhere, can we do better than that?
231 memcpy(buffer, data.ptr + pos, count);
232
233 pos += count;
234 return count;
235 }
236
237 override bool seekable() {
238 return true;
239 }
240
241 override StreamPos getPosition() {
242 return pos;
243 }
244
245 override bool setPosition(StreamPos pos) {
246 if (pos > size_)
247 return false;
248
249 this.pos = cast(size_t)pos;
250 return true;
251 }
252
253 override StreamSize size() {
254 return size;
255 }
256
257 //mixin ManualModeImpl;
258 //mixin FreeImpl;
259
260 private:
261 size_t pos = 0, size_ = 0;
262 ubyte[] data; // data.length is capacity
263 }