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 core.stdc.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 }