1 module aurorafw.core.io.filestream; 2 3 import std.stdio; 4 5 import aurorafw.core.io.stream; 6 7 version (unittest) import aurorafw.unit.assertion; 8 9 @safe 10 class FileStream : IStream 11 { 12 13 /** 14 * Constructs a FileStream with a given filename and 15 * mode. 16 * 17 * Examples: 18 * -------------------- 19 * FileStream foo = new FileStream("tuna.txt", "rb"); 20 * -------------------- 21 */ 22 public this(string filename, string mode = "rb") 23 { 24 fd = File(filename, mode); 25 26 import std.algorithm.searching : canFind; 27 28 _writable = WRITABLE.canFind(mode); 29 _readable = READABLE.canFind(mode); 30 } 31 32 @property ulong length() 33 { 34 return fd.size; 35 } 36 37 @property ulong tell() 38 { 39 return fd.tell; 40 } 41 42 @property bool empty() 43 { 44 return (fd.size - fd.tell) <= 0; 45 } 46 47 @property bool eof() 48 { 49 return empty(); 50 } 51 52 @property bool seekable() 53 { 54 return true; 55 } 56 57 @property bool readable() 58 { 59 return _readable; 60 } 61 62 @property bool writable() 63 { 64 return _writable; 65 } 66 67 ulong skip(ulong n) 68 { 69 return seek(n, Seek.CUR); 70 } 71 72 ulong seek(in long pos, in Seek origin = Seek.SET) 73 { 74 import std.stdio : SEEK_SET, SEEK_CUR, SEEK_END; 75 76 int orig; 77 with (Seek) final switch (origin) 78 { 79 case SET: 80 orig = SEEK_SET; 81 break; 82 case CUR: 83 orig = SEEK_CUR; 84 break; 85 case END: 86 orig = SEEK_END; 87 break; 88 } 89 90 fd.seek(pos, orig); 91 92 return tell; 93 } 94 95 void write(in ubyte b) 96 { 97 fd.rawWrite([b]); 98 } 99 100 void write(in ubyte[] b) 101 { 102 fd.rawWrite(b); 103 } 104 105 ubyte read() 106 { 107 if ((fd.tell + ubyte.sizeof) > fd.size) 108 throw new StreamException("Attempt reading outside of the stream"); 109 110 ubyte[1] b; 111 fd.rawRead(b); 112 113 return b[0]; 114 } 115 116 ubyte[] read(in size_t n) 117 { 118 import std.conv : to; 119 120 auto buf = new ubyte[(fd.tell + n > fd.size) ? (fd.size - fd.tell).to!size_t : n]; 121 fd.rawRead(buf); 122 123 return buf; 124 } 125 126 ubyte[] data() 127 { 128 import std.conv : to; 129 130 size_t s = (fd.size - fd.tell).to!size_t; 131 if (!s) 132 return []; 133 134 auto ret = new ubyte[s]; 135 fd.rawRead(ret); 136 return ret; 137 } 138 139 private enum READABLE = [ 140 "r", "w+", "r+", "x+", "c+", "rb", "w+b", "r+b", "x+b", "c+b", "rt", "w+t", "r+t", "x+t", "c+t", "a+" 141 ]; 142 143 private enum WRITABLE = [ 144 "w", "w+", "r+", "x+", "c+", "wb", "w+b", "r+b", "x+b", "c+b", "w+t", "r+t", "x+t", "c+t", "a", "a+" 145 ]; 146 147 private immutable bool _readable; 148 private immutable bool _writable; 149 150 private File fd; 151 } 152 153 version (unittest) 154 { 155 @safe 156 private string unittest_deleteme_file() 157 { 158 import std.file; 159 import std.path : buildPath; 160 import std.uuid : randomUUID; 161 162 auto dm = buildPath(tempDir(), randomUUID.toString); 163 std.file.write(dm, "abc"); 164 165 return dm; 166 } 167 } 168 169 /// 170 /*@safe*/ 171 @("File Stream: Readable stream") 172 unittest 173 { 174 import std.file; 175 176 auto dm = unittest_deleteme_file; 177 scope (exit) 178 std.file.remove(dm); 179 180 FileStream fs = new FileStream(dm); 181 unittest_readable_stream(fs); 182 assertFalse(fs.writable); 183 } 184 185 /// 186 /*@safe*/ 187 @("File Stream: Writable stream") 188 unittest 189 { 190 import std.file; 191 192 auto dm = unittest_deleteme_file; 193 scope (exit) 194 std.file.remove(dm); 195 196 FileStream fs = new FileStream(dm, "w"); 197 unittest_writable_stream(fs); 198 assertFalse(fs.readable); 199 } 200 201 /// 202 /*@safe*/ 203 @("File Stream: Read & Write stream") 204 unittest 205 { 206 import std.file; 207 208 auto dm = unittest_deleteme_file; 209 FileStream fs = new FileStream(dm, "r+"); 210 scope (exit) 211 std.file.remove(dm); 212 unittest_readable_stream(fs); 213 unittest_writable_stream(fs); 214 }