Skip to content

Commit

Permalink
Merge pull request #15 from cjihrig/buffers
Browse files Browse the repository at this point in the history
Add support for Buffers
  • Loading branch information
geek committed Sep 9, 2014
2 parents 8460411 + 24666de commit 41d87ba
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 12 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ Current version: **1.0.x** [![Build Status](https://api.travis-ci.org/hapijs/cat
### Options

- `maxByteSize` - sets an upper limit on the number of bytes that can be stored in the
cached. Once this limit is reached no additional items will be added to the cache
cache. Once this limit is reached no additional items will be added to the cache
until some expire. The utilized memory calculation is a rough approximation and must
not be relied on. Defaults to `104857600` (100MB).
- `allowMixedContent` - by default, all data is cached as JSON strings, and converted
to an object using `JSON.parse()` on retrieval. By setting this option to `true`,
`Buffer` data can be stored alongside the stringified data. `Buffer`s are not
stringified, and are copied before storage to prevent the value from changing while
in the cache. Defaults to `false`.
54 changes: 43 additions & 11 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,35 @@ var internals = {};


internals.defaults = {
maxByteSize: 100 * 1024 * 1024 // 100MB
maxByteSize: 100 * 1024 * 1024, // 100MB
allowMixedContent: false
};

// Provides a named reference for memory debugging
internals.MemoryCacheSegment = function MemoryCacheSegment () {
};

internals.MemoryCacheEntry = function MemoryCacheEntry (key, value, ttl) {
internals.MemoryCacheEntry = function MemoryCacheEntry (key, value, ttl, allowMixedContent) {

// stringify() to prevent value from changing while in the cache
var stringifiedValue = JSON.stringify(value);
var valueByteSize = 0;

if (allowMixedContent && Buffer.isBuffer(value)) {
this.item = new Buffer(value.length);
// copy buffer to prevent value from changing while in the cache
value.copy(this.item);
valueByteSize = this.item.length;
}
else {
// stringify() to prevent value from changing while in the cache
this.item = JSON.stringify(value);
valueByteSize = Buffer.byteLength(this.item);
}

this.item = stringifiedValue;
this.stored = Date.now();
this.ttl = ttl;

// Approximate cache entry size without value: 144 bytes
this.byteSize = 144 + Buffer.byteLength(stringifiedValue) + Buffer.byteLength(key.segment) + Buffer.byteLength(key.id);
this.byteSize = 144 + valueByteSize + Buffer.byteLength(key.segment) + Buffer.byteLength(key.id);

this.timeoutId = null;
};
Expand All @@ -36,6 +47,7 @@ exports = module.exports = internals.Connection = function MemoryCache (options)

Hoek.assert(this.constructor === internals.Connection, 'Memory cache client must be instantiated using new');
Hoek.assert(!options || options.maxByteSize === undefined || options.maxByteSize >= 0, 'Invalid cache maxByteSize value');
Hoek.assert(!options || options.allowMixedContent === undefined || typeof options.allowMixedContent === 'boolean', 'Invalid allowMixedContent value');

this.settings = Hoek.applyToDefaults(internals.defaults, options || {});
this.cache = null;
Expand Down Expand Up @@ -102,11 +114,16 @@ internals.Connection.prototype.get = function (key, callback) {
}

var value = null;
try {
value = JSON.parse(envelope.item);

if (Buffer.isBuffer(envelope.item)) {
value = envelope.item;
}
catch (err) {
return callback(new Error('Bad value content'));
else {
value = internals.parseJSON(envelope.item);

if (value instanceof Error) {
return callback(new Error('Bad value content'));
}
}

var result = {
Expand Down Expand Up @@ -135,7 +152,7 @@ internals.Connection.prototype.set = function (key, value, ttl, callback) {

var envelope = null;
try {
envelope = new internals.MemoryCacheEntry(key, value, ttl);
envelope = new internals.MemoryCacheEntry(key, value, ttl, this.settings.allowMixedContent);
} catch (err) {
return callback(err);
}
Expand Down Expand Up @@ -192,3 +209,18 @@ internals.Connection.prototype.drop = function (key, callback) {

return callback();
};


internals.parseJSON = function (json) {

var obj = null;

try {
obj = JSON.parse(json);
}
catch (err) {
obj = err;
}

return obj;
};
68 changes: 68 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,74 @@ describe('Memory', function () {
});
});

it('buffers can be set and retrieved when allowMixedContent is true', function (done) {

var buffer = new Buffer('string value');
var client = new Catbox.Client(new Memory({ allowMixedContent: true }));

client.start(function (err) {

var key = { id: 'x', segment: 'test' };

client.set(key, buffer, 500, function (err) {

expect(err).to.not.exist;
client.get(key, function (err, result) {

expect(err).to.not.exist;
expect(result.item instanceof Buffer).to.equal(true);
expect(result.item).to.deep.equal(buffer);
done();
});
});
});
});

it('buffers are copied before storing when allowMixedContent is true', function (done) {

var buffer = new Buffer('string value');
var client = new Catbox.Client(new Memory({ allowMixedContent: true }));

client.start(function (err) {

var key = { id: 'x', segment: 'test' };

client.set(key, buffer, 500, function (err) {

expect(err).to.not.exist;
client.get(key, function (err, result) {

expect(err).to.not.exist;
expect(result.item).to.not.equal(buffer);
done();
});
});
});
});

it('buffers are stringified when allowMixedContent is not true', function (done) {

var buffer = new Buffer('string value');
var client = new Catbox.Client(new Memory());

client.start(function (err) {

var key = { id: 'x', segment: 'test' };

client.set(key, buffer, 500, function (err) {

expect(err).to.not.exist;
client.get(key, function (err, result) {

expect(err).to.not.exist;
expect(result.item instanceof Buffer).to.equal(false);
expect(result.item).to.deep.equal(JSON.parse(JSON.stringify(buffer)));
done();
});
});
});
});

it('gets an item after setting it (no memory limit)', function (done) {

var client = new Catbox.Client(new Memory({ maxByteSize: 0 }));
Expand Down

0 comments on commit 41d87ba

Please sign in to comment.