Skip to content

Commit

Permalink
Add support for storing/retrieving binary data in metro-cache
Browse files Browse the repository at this point in the history
Reviewed By: mjesun

Differential Revision: D10868923

fbshipit-source-id: 9e849b483ade5d97a4194155615f05cd70559593
  • Loading branch information
rafeca authored and facebook-github-bot committed Oct 26, 2018
1 parent db3ec38 commit 843748f
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 6 deletions.
22 changes: 20 additions & 2 deletions packages/metro-cache/src/stores/FileStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ const mkdirp = require('mkdirp');
const path = require('path');
const rimraf = require('rimraf');

const NULL_BYTE = 0x00;
const NULL_BYTE_BUFFER = new Buffer([NULL_BYTE]);

export type Options = {|
root: string,
|};
Expand All @@ -29,7 +32,13 @@ class FileStore<T> {

get(key: Buffer): ?T {
try {
return JSON.parse(fs.readFileSync(this._getFilePath(key), 'utf8'));
const data = fs.readFileSync(this._getFilePath(key));

if (data[0] === NULL_BYTE) {
return (data.slice(1): any);
} else {
return JSON.parse(data.toString('utf8'));
}
} catch (err) {
if (err.code === 'ENOENT') {
return null;
Expand All @@ -40,7 +49,16 @@ class FileStore<T> {
}

set(key: Buffer, value: T): void {
fs.writeFileSync(this._getFilePath(key), JSON.stringify(value));
if (value instanceof Buffer) {
const fd = fs.openSync(this._getFilePath(key), 'w');

fs.writeSync(fd, NULL_BYTE_BUFFER);
fs.writeSync(fd, value);

fs.closeSync(fd);
} else {
fs.writeFileSync(this._getFilePath(key), JSON.stringify(value));
}
}

clear() {
Expand Down
23 changes: 19 additions & 4 deletions packages/metro-cache/src/stores/HttpStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ const ZLIB_OPTIONS = {
level: 9,
};

const NULL_BYTE = 0x00;
const NULL_BYTE_BUFFER = new Buffer([NULL_BYTE]);

class HttpStore<T> {
static HttpError = HttpError;
static NetworkError = NetworkError;
Expand Down Expand Up @@ -82,7 +85,7 @@ class HttpStore<T> {

const req = this._module.request(options, res => {
const code = res.statusCode;
let data = '';
const data = [];

if (code === 404) {
res.resume();
Expand All @@ -99,7 +102,7 @@ class HttpStore<T> {
const gunzipped = res.pipe(zlib.createGunzip());

gunzipped.on('data', chunk => {
data += chunk.toString();
data.push(chunk);
});

gunzipped.on('error', err => {
Expand All @@ -108,7 +111,13 @@ class HttpStore<T> {

gunzipped.on('end', () => {
try {
resolve(JSON.parse(data));
const buffer = Buffer.concat(data);

if (buffer.length > 0 && buffer[0] === NULL_BYTE) {
resolve((buffer.slice(1): any));
} else {
resolve(JSON.parse(buffer.toString('utf8')));
}
} catch (err) {
reject(err);
}
Expand Down Expand Up @@ -152,7 +161,13 @@ class HttpStore<T> {
});

gzip.pipe(req);
gzip.end(JSON.stringify(value) || 'null');

if (value instanceof Buffer) {
gzip.write(NULL_BYTE_BUFFER);
gzip.end(value);
} else {
gzip.end(JSON.stringify(value) || 'null');
}
});
}

Expand Down
9 changes: 9 additions & 0 deletions packages/metro-cache/src/stores/__tests__/FileStore-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,13 @@ describe('FileStore', () => {

expect(fileStore.get(cache)).toEqual(null);
});

it('reads and writes binary data', () => {
const fileStore = new FileStore({root: '/root'});
const cache = Buffer.from([0xfa, 0xce, 0xb0, 0x0c]);
const data = Buffer.from([0xca, 0xc4, 0xe5]);

fileStore.set(cache, data);
expect(fileStore.get(cache)).toEqual(data);
});
});
56 changes: 56 additions & 0 deletions packages/metro-cache/src/stores/__tests__/HttpStore-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,4 +187,60 @@ describe('HttpStore', () => {
done();
});
});

it('gets the same value that was set', async () => {
const store = new HttpStore({endpoint: 'http://www.example.com/endpoint'});
const chunks = [];
let storedValue;

httpPassThrough.on('data', chunk => {
chunks.push(chunk);
});

httpPassThrough.on('end', () => {
storedValue = zlib.gunzipSync(Buffer.concat(chunks));

const callbackSet = require('http').request.mock.calls[0][1];

callbackSet(responseHttpOk(''));
});

await store.set(Buffer.from('key-set'), {foo: 42});

const promiseGet = store.get(Buffer.from('key-set'));
const callbackGet = require('http').request.mock.calls[1][1];

callbackGet(responseHttpOk(storedValue));

expect(await promiseGet).toEqual({foo: 42});
});

it('gets the same value that was set when storing buffers', async () => {
const store = new HttpStore({endpoint: 'http://www.example.com/endpoint'});
const chunks = [];
let storedValue;

httpPassThrough.on('data', chunk => {
chunks.push(chunk);
});

httpPassThrough.on('end', () => {
storedValue = zlib.gunzipSync(Buffer.concat(chunks));

const callbackSet = require('http').request.mock.calls[0][1];

callbackSet(responseHttpOk(''));
});

const bufferValue = new Buffer([0xfb, 0xca, 0xc4]);

await store.set(Buffer.from('key-set'), bufferValue);

const promiseGet = store.get(Buffer.from('key-set'));
const callbackGet = require('http').request.mock.calls[1][1];

callbackGet(responseHttpOk(storedValue));

expect(await promiseGet).toEqual(bufferValue);
});
});

0 comments on commit 843748f

Please sign in to comment.