1 /** 2 * ae.sys.dataio 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <ae@cy.md> 12 */ 13 14 module ae.sys.dataio; 15 16 import std.conv : to; 17 import std.exception : enforce; 18 19 import ae.sys.data; 20 21 // ************************************************************************ 22 23 deprecated("std.stream is deprecated, use readFileData") 24 template readStreamData() 25 { 26 static import std.stream; 27 28 Data readStreamData(std.stream.Stream s) 29 { 30 auto size = s.size - s.position; 31 assert(size < size_t.max); 32 auto data = Data(cast(size_t)size); 33 s.readExact(data.mptr, data.length); 34 return data; 35 } 36 } 37 38 // ************************************************************************ 39 40 static import std.stdio; 41 42 /// Read the entire file `f` into a `Data` instance. 43 /// If `f` does not have a size, read it in 1MB chunks. 44 Data readFileData(std.stdio.File f) 45 { 46 Data result; 47 auto size = f.size; 48 if (size == ulong.max) 49 { 50 Data buf = Data(1024*1024); 51 buf.asDataOf!ubyte.enter((contents) { 52 while (!f.eof()) 53 result ~= f.rawRead(contents); 54 }); 55 } 56 else 57 { 58 auto pos = f.tell; 59 ulong remaining = size - pos; 60 if (!remaining) 61 return Data(); 62 enforce(remaining <= ulong(size_t.max), "File does not fit in memory"); 63 result = Data(remaining.to!size_t); 64 size_t bytesRead; 65 result.asDataOf!ubyte.enter((contents) { 66 bytesRead = f.rawRead(contents).length; 67 }); 68 result.length = bytesRead; 69 } 70 return result; 71 } 72 73 /// Read the entire file at the given `filename` into a `Data` instance. 74 Data readData(string filename) 75 { 76 auto f = std.stdio.File(filename, "rb"); 77 return readFileData(f); 78 } 79 80 // ************************************************************************ 81 82 /// Wrapper for Data class, allowing an object to be swapped to disk 83 /// and automatically retreived as required. 84 85 final class SwappedData 86 { 87 import std.file; 88 import std.string; 89 debug(SwappedData) import ae.sys.log; 90 91 private: 92 Data _data; 93 string fileName; 94 const(char)* cFileName; 95 96 static const MIN_SIZE = 4096; // minimum size to swap out 97 98 debug(SwappedData) static Logger log; 99 100 public: 101 this(string fileName) 102 { 103 debug(SwappedData) { if (log is null) log = fileAndConsoleLogger("SwappedData"); log(fileName ~ " - Creating"); } 104 this.fileName = fileName; 105 cFileName = fileName.toStringz(); 106 if (exists(fileName)) 107 remove(fileName); 108 } /// 109 110 /// Swap out the contents. 111 void unload() 112 { 113 if (!_data.empty && _data.length >= MIN_SIZE) 114 { 115 debug(SwappedData) log(fileName ~ " - Unloading"); 116 _data.enter((contents) { 117 write(fileName, contents); 118 }); 119 _data.clear(); 120 } 121 } 122 123 /// Returns `true` if the contents is in memory. 124 bool isLoaded() 125 { 126 return !exists(fileName); // wat 127 } 128 129 /// Retrieves the contents, swapping it in if necessary. 130 @property Data data() 131 { 132 if (!_data.length) 133 { 134 debug(SwappedData) log(fileName ~ " - Reloading"); 135 if (!exists(fileName)) 136 return Data(); 137 _data = readData(fileName); 138 remove(fileName); 139 } 140 return _data; 141 } 142 143 /// Sets the contents. 144 @property void data(Data data) 145 { 146 debug(SwappedData) log(fileName ~ " - Setting"); 147 if (exists(fileName)) 148 remove(fileName); 149 _data = data; 150 } 151 152 /// Returns the size of the contents in bytes. 153 size_t length() 154 { 155 if (!_data.empty) 156 return _data.length; 157 else 158 if (exists(fileName)) 159 return cast(size_t)getSize(fileName); 160 else 161 return 0; 162 } 163 164 ~this() @nogc 165 { 166 // Can't allocate in destructors. 167 168 //debug(SwappedData) log(fileName ~ " - Destroying"); 169 /*if (exists(fileName)) 170 { 171 debug(SwappedData) log(fileName ~ " - Deleting"); 172 remove(fileName); 173 }*/ 174 import core.stdc.stdio : remove; 175 remove(cFileName); 176 } 177 }