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 <vladimir@thecybershadow.net>
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 Data readFileData(ref std.stdio.File f)
42 {
43 	Data result;
44 	auto size = f.size;
45 	if (size == ulong.max)
46 	{
47 		Data buf = Data(1024*1024);
48 		while (!f.eof())
49 			result ~= f.rawRead(cast(ubyte[])buf.mcontents);
50 		buf.deleteContents();
51 	}
52 	else
53 	{
54 		auto pos = f.tell;
55 		ulong remaining = size - pos;
56 		if (!remaining)
57 			return Data();
58 		enforce(remaining <= ulong(size_t.max), "File does not fit in memory");
59 		result = Data(cast(size_t)remaining);
60 		result.length = f.rawRead(cast(ubyte[])result.mcontents).length;
61 	}
62 	return result;
63 }
64 
65 Data readData(string filename)
66 {
67 	auto f = std.stdio.File(filename, "rb");
68 	return readFileData(f);
69 }
70 
71 // ************************************************************************
72 
73 /// Wrapper for Data class, allowing an object to be swapped to disk
74 /// and automatically retreived as required.
75 
76 final class SwappedData
77 {
78 	import std.file;
79 	import std.string;
80 	debug(SwappedData) import ae.sys.log;
81 
82 private:
83 	Data _data;
84 	string fileName;
85 	const(char)* cFileName;
86 
87 	static const MIN_SIZE = 4096; // minimum size to swap out
88 
89 	debug(SwappedData) static Logger log;
90 
91 public:
92 	this(string fileName)
93 	{
94 		debug(SwappedData) { if (log is null) log = new FileAndConsoleLogger("SwappedData"); log(fileName ~ " - Creating"); }
95 		this.fileName = fileName;
96 		cFileName = fileName.toStringz();
97 		if (exists(fileName))
98 			remove(fileName);
99 	}
100 
101 	void unload()
102 	{
103 		if (!_data.empty && _data.length >= MIN_SIZE)
104 		{
105 			debug(SwappedData) log(fileName ~ " - Unloading");
106 			write(fileName, _data.contents);
107 			_data.clear();
108 		}
109 	}
110 
111 	bool isLoaded()
112 	{
113 		return !exists(fileName);
114 	}
115 
116 	// Getter
117 	Data data()
118 	{
119 		if (!_data.length)
120 		{
121 			debug(SwappedData) log(fileName ~ " - Reloading");
122 			if (!exists(fileName))
123 				return Data();
124 			_data = readData(fileName);
125 			remove(fileName);
126 		}
127 		return _data;
128 	}
129 
130 	// Setter
131 	void data(Data data)
132 	{
133 		debug(SwappedData) log(fileName ~ " - Setting");
134 		if (exists(fileName))
135 			remove(fileName);
136 		_data = data;
137 	}
138 
139 	size_t length()
140 	{
141 		if (!_data.empty)
142 			return _data.length;
143 		else
144 		if (exists(fileName))
145 			return cast(size_t)getSize(fileName);
146 		else
147 			return 0;
148 	}
149 
150 	~this()
151 	{
152 		// Can't allocate in destructors.
153 
154 		//debug(SwappedData) log(fileName ~ " - Destroying");
155 		/*if (exists(fileName))
156 		{
157 			debug(SwappedData) log(fileName ~ " - Deleting");
158 			remove(fileName);
159 		}*/
160 		import core.stdc.stdio : remove;
161 		remove(cFileName);
162 	}
163 }