From 4e458a1b7032e7d7ffbeb01e51510f9bc46d9714 Mon Sep 17 00:00:00 2001 From: Thaddee Tyl Date: Fri, 10 Jan 2014 18:01:31 +0100 Subject: [PATCH] Fixed-size cache store for bitmap images. Part of issue #23. --- svg-to-img.js | 63 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/svg-to-img.js b/svg-to-img.js index 9204d0a..83f493e 100644 --- a/svg-to-img.js +++ b/svg-to-img.js @@ -6,19 +6,18 @@ var childProcess = require('child_process'); var phantomScript = path.join(__dirname, 'phantomjs-svg2png.js'); var imgCache = Object.create(null); - -// If available, use the font here. -var fontPath = './Verbana.ttf'; -try { - // This happens at startup. Needn't be async. - var fontBase64 = fs.readFileSync(fontPath, 'base64'); -} catch(e) {} +var imgCacheTime = Object.create(null); +var imgCacheSize = 0; +// The following is an arbitrary limit (~1.5MB, 1.5kB/image). +var imgCacheMaxSize = 1000; module.exports = function (svg, format, out, cb) { var cacheIndex = format + svg; if (imgCache[cacheIndex] !== undefined) { // We own a cache for this svg conversion. - imgCache[cacheIndex].pipe(out); + (new DataStream(imgCache[cacheIndex])).pipe(out); + imgCacheTime[cacheIndex] = +(new Date()); + return; } var tmpFile = path.join(os.tmpdir(), "svg2img-" + (Math.random()*2147483648|0) + "." + format); @@ -37,21 +36,47 @@ module.exports = function (svg, format, out, cb) { // Remove the temporary file after use. inStream.on('end', function() { out.end(); - imgCache[cacheIndex] = streamFromData(cached); + addToCache(cacheIndex, cached); fs.unlink(tmpFile, cb); }); }); }; -// Fake stream from the cache. -var stream = require('stream'); -function streamFromData(data) { - var newStream = new stream.Readable(); - newStream._read = function() { - for (var i = 0; i < data.length; i++) { - newStream.push(data[i]); +function addToCache(cacheIndex, cached) { + imgCache[cacheIndex] = cached; + var mostAncient = +(new Date()); + imgCacheTime[cacheIndex] = mostAncient; + if (imgCacheSize >= imgCacheMaxSize) { + // Find the most ancient image. + var ancientCacheIndex = cacheIndex; + for (var currentCacheIndex in imgCacheTime) { + if (mostAncient > imgCacheTime[currentCacheIndex]) { + mostAncient = imgCacheTime[currentCacheIndex]; + ancientCacheIndex = currentCacheIndex; + } } - newStream.push(null) - }; - return newStream; + // Delete that image. + delete imgCache[ancientCacheIndex]; + delete imgCacheTime[ancientCacheIndex]; + } else { + imgCacheSize++; + } } + +// Fake stream from the cache. +var Readable = require('stream').Readable; +var util = require('util'); +function DataStream(data) { + Readable.call(this); + this.data = data; + this.i = 0; +} +util.inherits(DataStream, Readable); +DataStream.prototype._read = function() { + while (this.i < this.data.length) { + var stop = this.push(this.data[this.i]); + this.i++; + if (stop) { return; } + } + this.push(null); +};