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 }