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