1 /// Basic file stream implementation inheriting `IStream`. 2 module tern.stream.file_stream; 3 4 public import tern.stream.impl; 5 import tern.typecons; 6 import tern.serialization; 7 import tern.traits; 8 import tern.memory; 9 import tern.digest.mira; 10 import std.stdio; 11 12 public enum Mode 13 { 14 Read, 15 Write, 16 ReadWrite, 17 Append, 18 ReadAppend, 19 } 20 21 public class FileStream : IStream 22 { 23 protected: 24 final: 25 File file; 26 27 public: 28 Endianness endianness; 29 30 this(string path, Mode mode = Mode.ReadWrite, Endianness endianness = Endianness.Native) 31 { 32 if (mode == Mode.Read) 33 this.file = File(path, "r"); 34 else if (mode == Mode.Write) 35 this.file = File(path, "w"); 36 else if (mode == Mode.ReadWrite) 37 this.file = File(path, "r+"); 38 else if (mode == Mode.Append) 39 this.file = File(path, "a"); 40 else if (mode == Mode.ReadAppend) 41 this.file = File(path, "a+"); 42 this.endianness = endianness; 43 } 44 45 this(File file, Endianness endianness = Endianness.Native) 46 { 47 this.file = file; 48 this.endianness = endianness; 49 } 50 51 @property size_t position() 52 { 53 return file.tell(); 54 } 55 56 @property size_t position(size_t val) 57 { 58 file.seek(val, SEEK_SET); 59 return file.tell(); 60 } 61 62 size_t size() 63 { 64 return file.size(); 65 } 66 67 bool mayRead(T)() 68 { 69 return position + T.sizeof < size; 70 } 71 72 bool mayRead(size_t size = 1) 73 { 74 return position + size < this.size; 75 } 76 77 void step(T)() 78 { 79 file.seek(T.sizeof, SEEK_CUR); 80 } 81 82 void seek(Seek SEEK = Seek.Start)(size_t offset) 83 { 84 if (SEEK == Seek.Current) 85 file.seek(offset, SEEK_CUR); 86 else if (SEEK == Seek.Start) 87 file.seek(offset, SEEK_SET); 88 else 89 file.seek(offset, SEEK_END); 90 } 91 92 ubyte[] readAllBytes() 93 { 94 ubyte[] buff = new ubyte[size]; 95 file.rawRead(buff); 96 return buff; 97 } 98 99 immutable(CHAR)[] readAllText(CHAR = char)() 100 { 101 return cast(immutable(CHAR)[])readAllBytes; 102 } 103 104 T read(T)() 105 { 106 ubyte[T.sizeof] buff; 107 file.rawRead(buff); 108 return (cast(ubyte[])buff).deserialize!T; 109 } 110 111 T[] read(T)(size_t count) 112 { 113 ubyte[] buff = new ubyte[T.sizeof * count + size_t.sizeof]; 114 file.rawRead(buff); 115 return (cast(ubyte[])buff).deserialize!(T[]); 116 } 117 118 T[] readRaw(T)(size_t count) 119 { 120 ubyte[] buff = new ubyte[T.sizeof * count]; 121 file.rawRead(buff); 122 ubyte[] ret = new ubyte[(T.sizeof * count) + size_t.sizeof]; 123 ret[size_t.sizeof..$] = buff; 124 ret[0..size_t.sizeof] = (cast(ubyte*)&count)[0..size_t.sizeof]; 125 return (cast(ubyte[])ret).deserialize!(T[]); 126 } 127 128 T read(T)() 129 if (isDynamicArray!T) 130 { 131 return read!T(read7EncodedInt()); 132 } 133 134 T peek(T)() 135 { 136 size_t position = file.tell(); 137 scope(exit) file.seek(position); 138 ubyte[T.sizeof] buff; 139 file.rawRead(buff); 140 return (cast(ubyte[])buff).deserialize!T; 141 } 142 143 T[] peek(T)(size_t count) 144 { 145 size_t position = file.tell(); 146 scope(exit) file.seek(position); 147 ubyte[] buff = new ubyte[T.sizeof * count + size_t.sizeof]; 148 file.rawRead(buff); 149 return (cast(ubyte[])buff).deserialize!(T[]); 150 } 151 152 T[] peekRaw(T)(size_t count) 153 { 154 size_t position = file.tell(); 155 scope(exit) file.seek(position); 156 ubyte[] buff = new ubyte[T.sizeof * count]; 157 file.rawRead(buff); 158 ubyte[] ret = new ubyte[(T.sizeof * count) + size_t.sizeof]; 159 ret[size_t.sizeof..$] = buff; 160 ret[0..size_t.sizeof] = (cast(ubyte*)&count)[0..size_t.sizeof]; 161 return (cast(ubyte[])ret).deserialize!(T[]); 162 } 163 164 T read(T)() 165 if (isDynamicArray!T) 166 { 167 size_t position = file.tell(); 168 scope(exit) file.seek(position); 169 return read!T(read7EncodedInt()); 170 } 171 172 void write(bool RAW = false, T)(T val) 173 if (!isArray!T) 174 { 175 file.rawWrite(val.serialize!RAW); 176 } 177 178 void write(T, bool PREFIXED = true)(T val) 179 if (isArray!T) 180 { 181 static if (PREFIXED) 182 write7EncodedInt(cast(uint)val.length); 183 184 file.rawWrite(val.serialize()[8..$]); 185 } 186 187 void put(bool RAW = false, T)(T val) 188 if (!isArray!T) 189 { 190 size_t position = file.tell(); 191 scope(exit) file.seek(position); 192 file.rawWrite(val.serialize!RAW); 193 } 194 195 void put(T, bool PREFIXED = true)(T val) 196 if (isArray!T) 197 { 198 size_t position = file.tell(); 199 scope(exit) file.seek(position); 200 static if (PREFIXED) 201 write7EncodedInt(cast(uint)val.length); 202 203 file.rawWrite(val.serialize()[8..$]); 204 } 205 206 immutable(CHAR)[] readString(CHAR, bool PREFIXED = false)() 207 { 208 static if (PREFIXED) 209 return cast(immutable(CHAR)[])read!(immutable(CHAR)[]); 210 else 211 { 212 immutable(CHAR)[] ret; 213 while (peek!CHAR != '\0' && position < size) 214 ret ~= read!CHAR; 215 return ret; 216 } 217 } 218 219 immutable(CHAR)[] peekString(CHAR, bool PREFIXED = false)() 220 { 221 size_t position = file.tell(); 222 scope(exit) file.seek(position); 223 static if (PREFIXED) 224 return cast(immutable(CHAR)[])read!(immutable(CHAR)[]); 225 else 226 { 227 immutable(CHAR)[] ret; 228 while (peek!CHAR != '\0' && position < size) 229 ret ~= read!CHAR; 230 return ret; 231 } 232 } 233 234 void writeString(CHAR, bool PREFIXED = false)(immutable(CHAR)[] val) 235 { 236 write!(immutable(CHAR)[], PREFIXED)(val); 237 } 238 239 void putString(CHAR, bool PREFIXED = false)(immutable(CHAR)[] val) 240 { 241 put!(immutable(CHAR)[], PREFIXED)(val); 242 } 243 244 uint read7EncodedInt() 245 { 246 uint result = 0; 247 uint shift = 0; 248 249 foreach (i; 0..5) 250 { 251 ubyte b = read!ubyte; 252 result |= cast(uint)(b & 0x7F) << shift; 253 if ((b & 0x80) == 0) 254 return result; 255 shift += 7; 256 } 257 258 return result; 259 } 260 261 void write7EncodedInt(uint val) 262 { 263 foreach (i; 0..5) 264 { 265 byte b = cast(byte)(val & 0x7F); 266 val >>= 7; 267 if (val != 0) 268 b |= 0x80; 269 write(b); 270 if (val == 0) 271 return; 272 } 273 } 274 275 void encrypt(size_t size, string key) 276 { 277 if (!mayRead(size)) 278 encrypt(this.size, key); 279 280 ubyte[] buff = peek!ubyte(size); 281 Mira256.encrypt(buff, key); 282 write!(ubyte[], false)(buff); 283 } 284 285 void decrypt(size_t size, string key) 286 { 287 if (!mayRead(size)) 288 decrypt(this.size, key); 289 290 ubyte[] buff = peek!ubyte(size); 291 Mira256.decrypt(buff, key); 292 write!(ubyte[], false)(buff); 293 } 294 }