@cool-midway/cache-manager-fs-hash
Version:
file system store for node cache manager
118 lines (104 loc) • 4.22 kB
JavaScript
const promisify = require('util').promisify;
const fs = require('fs');
const zlib = require('zlib');
exports.write = async function (path, data, options) {
const externalBuffers = [];
let dataString = JSON.stringify(data, function replacerFunction(k, value) {
//Buffers searilize to {data: [...], type: "Buffer"}
if (value && value.type === 'Buffer' && value.data && value.data.length >= 1024 /* only save bigger Buffers external, small ones can be inlined */) {
const buffer = Buffer.from(value.data);
externalBuffers.push({
index: externalBuffers.length,
buffer: buffer,
});
return {
type: 'ExternalBuffer',
index: externalBuffers.length - 1,
size: buffer.length,
};
} else if (value === Infinity || value === -Infinity) {
return { type: 'Infinity', sign: Math.sign(value) };
} else {
return value;
}
});
let zipExtension = '';
if (options.zip) {
zipExtension = '.gz';
dataString = await promisify(zlib.deflate)(dataString);
}
//save main json file
await promisify(fs.writeFile)(path + '.json' + zipExtension, dataString, 'utf8');
//save external buffers
await Promise.all(externalBuffers.map(async function (externalBuffer) {
let buffer = externalBuffer.buffer;
if (options.zip) {
buffer = await promisify(zlib.deflate)(buffer);
}
await promisify(fs.writeFile)(path + '-' + externalBuffer.index + '.bin' + zipExtension, buffer, 'utf8');
}));
};
exports.read = async function (path, options) {
let zipExtension = '';
if (options.zip) {
zipExtension = '.gz';
}
//read main json file
let dataString;
if (options.zip) {
const compressedData = await promisify(fs.readFile)(path + '.json' + zipExtension);
dataString = (await promisify(zlib.unzip)(compressedData)).toString();
} else {
dataString = await promisify(fs.readFile)(path + '.json' + zipExtension, 'utf8');
}
const externalBuffers = [];
const data = JSON.parse(dataString, function bufferReceiver(k, value) {
if (value && value.type === 'Buffer' && value.data) {
return Buffer.from(value.data);
} else if (value && value.type === 'ExternalBuffer' && typeof value.index === 'number' && typeof value.size === 'number') {
//JSON.parse is sync so we need to return a buffer sync, we will fill the buffer later
const buffer = Buffer.alloc(value.size);
externalBuffers.push({
index: +value.index,
buffer: buffer,
});
return buffer;
} else if (value && value.type === 'Infinity' && typeof value.sign === 'number') {
return Infinity * value.sign;
} else {
return value;
}
});
//read external buffers
await Promise.all(externalBuffers.map(async function (externalBuffer) {
if (options.zip) {
const bufferCompressed = await promisify(fs.readFile)(path + '-' + +externalBuffer.index + '.bin' + zipExtension);
const buffer = await promisify(zlib.unzip)(bufferCompressed);
buffer.copy(externalBuffer.buffer);
} else {
const fd = await promisify(fs.open)(path + '-' + +externalBuffer.index + '.bin' + zipExtension, 'r');
await promisify(fs.read)(fd, externalBuffer.buffer, 0, externalBuffer.buffer.length, 0);
await promisify(fs.close)(fd);
}
}));
return data;
};
exports.delete = async function (path, options) {
let zipExtension = '';
if (options.zip) {
zipExtension = '.gz';
}
await promisify(fs.unlink)(path + '.json' + zipExtension);
//delete binary files
try {
for (let i = 0; i < Infinity; i++) {
await promisify(fs.unlink)(path + '-' + i + '.bin' + zipExtension);
}
} catch (err) {
if (err.code === 'ENOENT') {
// every binary is deleted, we are done
} else {
throw err;
}
}
};