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