Projekt

Obecné

Profil

Stáhnout (7.43 KB) Statistiky
| Větev: | Revize:
1
/*
2
	MIT License http://www.opensource.org/licenses/mit-license.php
3
	Author Tobias Koppers @sokra
4
*/
5
"use strict";
6

    
7
class Storage {
8
	constructor(duration) {
9
		this.duration = duration;
10
		this.running = new Map();
11
		this.data = new Map();
12
		this.levels = [];
13
		if(duration > 0) {
14
			this.levels.push(new Set(), new Set(), new Set(), new Set(), new Set(), new Set(), new Set(), new Set(), new Set());
15
			for(let i = 8000; i < duration; i += 500)
16
				this.levels.push(new Set());
17
		}
18
		this.count = 0;
19
		this.interval = null;
20
		this.needTickCheck = false;
21
		this.nextTick = null;
22
		this.passive = true;
23
		this.tick = this.tick.bind(this);
24
	}
25

    
26
	ensureTick() {
27
		if(!this.interval && this.duration > 0 && !this.nextTick)
28
			this.interval = setInterval(this.tick, Math.floor(this.duration / this.levels.length));
29
	}
30

    
31
	finished(name, err, result) {
32
		const callbacks = this.running.get(name);
33
		this.running.delete(name);
34
		if(this.duration > 0) {
35
			this.data.set(name, [err, result]);
36
			const levelData = this.levels[0];
37
			this.count -= levelData.size;
38
			levelData.add(name);
39
			this.count += levelData.size;
40
			this.ensureTick();
41
		}
42
		for(let i = 0; i < callbacks.length; i++) {
43
			callbacks[i](err, result);
44
		}
45
	}
46

    
47
	finishedSync(name, err, result) {
48
		if(this.duration > 0) {
49
			this.data.set(name, [err, result]);
50
			const levelData = this.levels[0];
51
			this.count -= levelData.size;
52
			levelData.add(name);
53
			this.count += levelData.size;
54
			this.ensureTick();
55
		}
56
	}
57

    
58
	provide(name, provider, callback) {
59
		if(typeof name !== "string") {
60
			callback(new TypeError("path must be a string"));
61
			return;
62
		}
63
		let running = this.running.get(name);
64
		if(running) {
65
			running.push(callback);
66
			return;
67
		}
68
		if(this.duration > 0) {
69
			this.checkTicks();
70
			const data = this.data.get(name);
71
			if(data) {
72
				return process.nextTick(() => {
73
					callback.apply(null, data);
74
				});
75
			}
76
		}
77
		this.running.set(name, running = [callback]);
78
		provider(name, (err, result) => {
79
			this.finished(name, err, result);
80
		});
81
	}
82

    
83
	provideSync(name, provider) {
84
		if(typeof name !== "string") {
85
			throw new TypeError("path must be a string");
86
		}
87
		if(this.duration > 0) {
88
			this.checkTicks();
89
			const data = this.data.get(name);
90
			if(data) {
91
				if(data[0])
92
					throw data[0];
93
				return data[1];
94
			}
95
		}
96
		let result;
97
		try {
98
			result = provider(name);
99
		} catch(e) {
100
			this.finishedSync(name, e);
101
			throw e;
102
		}
103
		this.finishedSync(name, null, result);
104
		return result;
105
	}
106

    
107
	tick() {
108
		const decay = this.levels.pop();
109
		for(let item of decay) {
110
			this.data.delete(item);
111
		}
112
		this.count -= decay.size;
113
		decay.clear();
114
		this.levels.unshift(decay);
115
		if(this.count === 0) {
116
			clearInterval(this.interval);
117
			this.interval = null;
118
			this.nextTick = null;
119
			return true;
120
		} else if(this.nextTick) {
121
			this.nextTick += Math.floor(this.duration / this.levels.length);
122
			const time = new Date().getTime();
123
			if(this.nextTick > time) {
124
				this.nextTick = null;
125
				this.interval = setInterval(this.tick, Math.floor(this.duration / this.levels.length));
126
				return true;
127
			}
128
		} else if(this.passive) {
129
			clearInterval(this.interval);
130
			this.interval = null;
131
			this.nextTick = new Date().getTime() + Math.floor(this.duration / this.levels.length);
132
		} else {
133
			this.passive = true;
134
		}
135
	}
136

    
137
	checkTicks() {
138
		this.passive = false;
139
		if(this.nextTick) {
140
			while(!this.tick());
141
		}
142
	}
143

    
144
	purge(what) {
145
		if(!what) {
146
			this.count = 0;
147
			clearInterval(this.interval);
148
			this.nextTick = null;
149
			this.data.clear();
150
			this.levels.forEach(level => {
151
				level.clear();
152
			});
153
		} else if(typeof what === "string") {
154
			for(let key of this.data.keys()) {
155
				if(key.startsWith(what))
156
					this.data.delete(key);
157
			}
158
		} else {
159
			for(let i = what.length - 1; i >= 0; i--) {
160
				this.purge(what[i]);
161
			}
162
		}
163
	}
164
}
165

    
166
module.exports = class CachedInputFileSystem {
167
	constructor(fileSystem, duration) {
168
		this.fileSystem = fileSystem;
169
		this._statStorage = new Storage(duration);
170
		this._readdirStorage = new Storage(duration);
171
		this._readFileStorage = new Storage(duration);
172
		this._readJsonStorage = new Storage(duration);
173
		this._readlinkStorage = new Storage(duration);
174

    
175
		this._stat = this.fileSystem.stat ? this.fileSystem.stat.bind(this.fileSystem) : null;
176
		if(!this._stat) this.stat = null;
177

    
178
		this._statSync = this.fileSystem.statSync ? this.fileSystem.statSync.bind(this.fileSystem) : null;
179
		if(!this._statSync) this.statSync = null;
180

    
181
		this._readdir = this.fileSystem.readdir ? this.fileSystem.readdir.bind(this.fileSystem) : null;
182
		if(!this._readdir) this.readdir = null;
183

    
184
		this._readdirSync = this.fileSystem.readdirSync ? this.fileSystem.readdirSync.bind(this.fileSystem) : null;
185
		if(!this._readdirSync) this.readdirSync = null;
186

    
187
		this._readFile = this.fileSystem.readFile ? this.fileSystem.readFile.bind(this.fileSystem) : null;
188
		if(!this._readFile) this.readFile = null;
189

    
190
		this._readFileSync = this.fileSystem.readFileSync ? this.fileSystem.readFileSync.bind(this.fileSystem) : null;
191
		if(!this._readFileSync) this.readFileSync = null;
192

    
193
		if(this.fileSystem.readJson) {
194
			this._readJson = this.fileSystem.readJson.bind(this.fileSystem);
195
		} else if(this.readFile) {
196
			this._readJson = (path, callback) => {
197
				this.readFile(path, (err, buffer) => {
198
					if(err) return callback(err);
199
					let data;
200
					try {
201
						data = JSON.parse(buffer.toString("utf-8"));
202
					} catch(e) {
203
						return callback(e);
204
					}
205
					callback(null, data);
206
				});
207
			};
208
		} else {
209
			this.readJson = null;
210
		}
211
		if(this.fileSystem.readJsonSync) {
212
			this._readJsonSync = this.fileSystem.readJsonSync.bind(this.fileSystem);
213
		} else if(this.readFileSync) {
214
			this._readJsonSync = (path) => {
215
				const buffer = this.readFileSync(path);
216
				const data = JSON.parse(buffer.toString("utf-8"));
217
				return data;
218
			};
219
		} else {
220
			this.readJsonSync = null;
221
		}
222

    
223
		this._readlink = this.fileSystem.readlink ? this.fileSystem.readlink.bind(this.fileSystem) : null;
224
		if(!this._readlink) this.readlink = null;
225

    
226
		this._readlinkSync = this.fileSystem.readlinkSync ? this.fileSystem.readlinkSync.bind(this.fileSystem) : null;
227
		if(!this._readlinkSync) this.readlinkSync = null;
228
	}
229

    
230
	stat(path, callback) {
231
		this._statStorage.provide(path, this._stat, callback);
232
	}
233

    
234
	readdir(path, callback) {
235
		this._readdirStorage.provide(path, this._readdir, callback);
236
	}
237

    
238
	readFile(path, callback) {
239
		this._readFileStorage.provide(path, this._readFile, callback);
240
	}
241

    
242
	readJson(path, callback) {
243
		this._readJsonStorage.provide(path, this._readJson, callback);
244
	}
245

    
246
	readlink(path, callback) {
247
		this._readlinkStorage.provide(path, this._readlink, callback);
248
	}
249

    
250
	statSync(path) {
251
		return this._statStorage.provideSync(path, this._statSync);
252
	}
253

    
254
	readdirSync(path) {
255
		return this._readdirStorage.provideSync(path, this._readdirSync);
256
	}
257

    
258
	readFileSync(path) {
259
		return this._readFileStorage.provideSync(path, this._readFileSync);
260
	}
261

    
262
	readJsonSync(path) {
263
		return this._readJsonStorage.provideSync(path, this._readJsonSync);
264
	}
265

    
266
	readlinkSync(path) {
267
		return this._readlinkStorage.provideSync(path, this._readlinkSync);
268
	}
269

    
270
	purge(what) {
271
		this._statStorage.purge(what);
272
		this._readdirStorage.purge(what);
273
		this._readFileStorage.purge(what);
274
		this._readlinkStorage.purge(what);
275
		this._readJsonStorage.purge(what);
276
	}
277
};
(4-4/39)