Skip to content

Commit

Permalink
RrMapCache and RrObjectCache
Browse files Browse the repository at this point in the history
  • Loading branch information
jchook committed Jun 10, 2020
1 parent 2e3eeb2 commit 7ea7f60
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 2 deletions.
123 changes: 123 additions & 0 deletions jest/__snapshots__/bundles-snapshot.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -370,12 +370,135 @@ exports[`Dist bundle is unchanged 1`] = `
return LruMapCache;
}();
var RrObjectCache = /*#__PURE__*/function () {
function RrObjectCache(_temp) {
var _ref = _temp === void 0 ? {} : _temp,
cacheSize = _ref.cacheSize;
validateCacheSize(cacheSize);
this._cache = {};
this._cacheKeys = [];
this._cacheLength = 0;
this._cacheSize = cacheSize;
}
var _proto = RrObjectCache.prototype;
_proto.set = function set(key, selectorFn) {
if (this._cacheLength >= this._cacheSize) {
this._randomReplace(key, selectorFn);
} else {
this._cache[key] = selectorFn;
this._cacheKeys[this._cacheLength] = key;
this._cacheLength++;
}
};
_proto.get = function get(key) {
return this._cache[key];
};
_proto.remove = function remove(key) {
var index = this._cacheKeys.indexOf(key); // O(1)
if (index > -1) {
delete this._cache[key];
var lastIndex = this._cacheLength - 1;
var lastKey = this._cacheKeys[lastIndex];
this._cacheKeys[index] = lastKey;
this._cacheLength--;
}
};
_proto.clear = function clear() {
this._cache = {};
this._cacheKeys = [];
this._cacheLength = 0;
};
_proto._randomReplace = function _randomReplace(newKey, newValue) {
var index = Math.floor(Math.random() * this._cacheLength);
var key = this._cacheKeys[index];
delete this._cache[key];
this._cacheKeys[index] = newKey;
this._cache[newKey] = newValue;
};
_proto.isValidCacheKey = function isValidCacheKey(cacheKey) {
return isStringOrNumber(cacheKey);
};
return RrObjectCache;
}();
var RrMapCache = /*#__PURE__*/function () {
function RrMapCache(_temp) {
var _ref = _temp === void 0 ? {} : _temp,
cacheSize = _ref.cacheSize;
validateCacheSize(cacheSize);
this.clear();
this._cacheSize = cacheSize;
}
var _proto = RrMapCache.prototype;
_proto.set = function set(key, selectorFn) {
if (this._cacheLength >= this._cacheSize) {
this._randomReplace(key, selectorFn);
} else {
this._cache.set(key, selectorFn);
this._cacheKeys[this._cacheLength] = key;
this._cacheLength++;
}
};
_proto.get = function get(key) {
return this._cache.get(key);
};
_proto.remove = function remove(key) {
var index = this._cacheKeys.indexOf(key);
if (index > -1) {
delete this._cache[\\"delete\\"](key);
var lastIndex = this._cacheLength - 1;
var lastKey = this._cacheKeys[lastIndex];
this._cacheKeys[index] = lastKey;
this._cacheLength--;
}
};
_proto.clear = function clear() {
this._cache = new Map();
this._cacheKeys = [];
this._cacheLength = 0;
};
_proto._randomReplace = function _randomReplace(newKey, newValue) {
var index = Math.floor(Math.random() * this._cacheLength);
var key = this._cacheKeys[index];
this._cache[\\"delete\\"](key);
this._cacheKeys[index] = newKey;
this._cache.set(newKey, newValue);
};
return RrMapCache;
}();
exports.FifoMapCache = FifoMapCache;
exports.FifoObjectCache = FifoObjectCache;
exports.FlatMapCache = FlatMapCache;
exports.FlatObjectCache = FlatObjectCache;
exports.LruMapCache = LruMapCache;
exports.LruObjectCache = LruObjectCache;
exports.RrMapCache = RrMapCache;
exports.RrObjectCache = RrObjectCache;
exports.createCachedSelector = createCachedSelector;
exports.createStructuredCachedSelector = createStructuredCachedSelector;
exports.default = createCachedSelector;
Expand Down
7 changes: 5 additions & 2 deletions src/cache/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@

## Available cache objects

`re-reselect` ships with **6 ready-to-use cache object constructors**:
`re-reselect` ships with several **ready-to-use cache object constructors**:

| name | accepted cacheKey | type | storage |
| :---------------------------------------: | :---------------: | :-----------------------------------: | :----------------------------: |
| [`FlatObjectCache`](./FlatObjectCache.js) | `number` `string` | flat unlimited | JS object |
| [`FifoObjectCache`](./FifoObjectCache.js) | `number` `string` | [first in first out][docs-fifo-cache] | JS object |
| [`LruObjectCache`](./LruObjectCache.js) | `number` `string` | [least recently used][docs-lru-cache] | JS object |
| [`RrObjectCache`](./RrObjectCache.js) | `number` `string` | [random replacement][docs-rr-cache] | JS object |
| [`FlatMapCache`](./FlatMapCache.js) | any | flat unlimited | [Map object][docs-mozilla-map] |
| [`FifoMapCache`](./FifoMapCache.js) | any | [first in first out][docs-fifo-cache] | [Map object][docs-mozilla-map] |
| [`FifoMapCache`](./FifoMapCache.js) | any | [first in first out][docs-fifo-cache] | [Map object][docs-mozilla-map] |
| [`LruMapCache`](./LruMapCache.js) | any | [least recently used][docs-lru-cache] | [Map object][docs-mozilla-map] |
| [`RrMapCache`](./RrMapCache.js) | any | [random replacement][docs-rr-cache] | [Map object][docs-mozilla-map] |

<!-- prettier-ignore -->
```js
Expand Down Expand Up @@ -53,4 +55,5 @@ interface ICacheObject {
[wiki-strategy-pattern]: https://en.wikipedia.org/wiki/Strategy_pattern
[docs-fifo-cache]: https://en.wikipedia.org/wiki/Cache_replacement_policies#First_in_first_out_(FIFO)
[docs-lru-cache]: https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)
[docs-rr-cache]: https://en.wikipedia.org/wiki/Cache_replacement_policies#Random_replacement_(RR)
[docs-mozilla-map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
41 changes: 41 additions & 0 deletions src/cache/RrMapCache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import validateCacheSize from './util/validateCacheSize';
import isStringOrNumber from './util/isStringOrNumber';

export default class RrMapCache {
constructor({cacheSize} = {}) {
validateCacheSize(cacheSize);
this.clear();
this._cacheSize = cacheSize;
}
set(key, selectorFn) {
if (this._cacheLength >= this._cacheSize) {
this._randomReplace(key, selectorFn);
} else {
this._cache.set(key, selectorFn);
this._cacheKeys[this._cacheLength] = key;
this._cacheLength++;
}
}
get(key) {
return this._cache.get(key);
}
remove(key) {
const index = this._cacheKeys.indexOf(key); // O(1)
if (index > -1) {
delete this._cache.delete(key);
this._cacheLength--;
this._cacheKeys[index] = this._cacheKeys[this._cacheLength];
}
}
clear() {
this._cache = new Map();
this._cacheKeys = [];
this._cacheLength = 0;
}
_randomReplace(newKey, newValue) {
const index = Math.floor(Math.random() * this._cacheLength);
this._cache.delete(this._cacheKeys[index]);
this._cacheKeys[index] = newKey;
this._cache.set(newKey, newValue);
}
}
44 changes: 44 additions & 0 deletions src/cache/RrObjectCache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import validateCacheSize from './util/validateCacheSize';
import isStringOrNumber from './util/isStringOrNumber';

export default class RrObjectCache {
constructor({cacheSize} = {}) {
validateCacheSize(cacheSize);
this.clear();
this._cacheSize = cacheSize;
}
set(key, selectorFn) {
if (this._cacheLength >= this._cacheSize) {
this._randomReplace(key, selectorFn);
} else {
this._cache[key] = selectorFn;
this._cacheKeys[this._cacheLength] = key;
this._cacheLength++;
}
}
get(key) {
return this._cache[key];
}
remove(key) {
const index = this._cacheKeys.indexOf(key); // O(1)
if (index > -1) {
delete this._cache[key];
this._cacheLength--;
this._cacheKeys[index] = this._cacheKeys[this._cacheLength];
}
}
clear() {
this._cache = {};
this._cacheKeys = [];
this._cacheLength = 0;
}
_randomReplace(newKey, newValue) {
const index = Math.floor(Math.random() * this._cacheLength);
delete this._cache[this._cacheKeys[index]];
this._cacheKeys[index] = newKey;
this._cache[newKey] = newValue;
}
isValidCacheKey(cacheKey) {
return isStringOrNumber(cacheKey);
}
}
12 changes: 12 additions & 0 deletions src/cache/__tests__/RrMapCache.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {RrMapCache as CacheObject} from '../../../src/index';
import testBasicBehavior from '../__util__/testBasicBehavior';
import testRrBehavior from '../__util__/testRrBehavior';
import testCacheSizeOptionValidation from '../__util__/testCacheSizeOptionValidation';
import testMapCacheKeyBehavior from '../__util__/testMapCacheKeyBehavior';

describe('RrMapCache', () => {
testBasicBehavior(CacheObject, {cacheSize: 10});
testRrBehavior(CacheObject);
testCacheSizeOptionValidation(CacheObject);
testMapCacheKeyBehavior(CacheObject, {cacheSize: 10});
});
12 changes: 12 additions & 0 deletions src/cache/__tests__/RrObjectCache.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {RrObjectCache as CacheObject} from '../../../src/index';
import testBasicBehavior from '../__util__/testBasicBehavior';
import testRrBehavior from '../__util__/testRrBehavior';
import testCacheSizeOptionValidation from '../__util__/testCacheSizeOptionValidation';
import testObjectCacheKeyBehavior from '../__util__/testObjectCacheKeyBehavior';

describe('RrObjectCache', () => {
testBasicBehavior(CacheObject, {cacheSize: 10});
testRrBehavior(CacheObject);
testCacheSizeOptionValidation(CacheObject);
testObjectCacheKeyBehavior(CacheObject, {cacheSize: 10});
});
5 changes: 5 additions & 0 deletions src/cache/__util__/testBasicBehavior.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ function testBasicBehavior(CacheObject, options) {
expect(cache.get(entry)).toBe(undefined);
});
});

it('removes non-existant keys', () => {
const cache = new CacheObject({cacheSize: 5});
expect(() => cache.remove('foo')).not.toThrow();
});
});
}

Expand Down
30 changes: 30 additions & 0 deletions src/cache/__util__/testRrBehavior.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import fillCacheWith from './fillCacheWith';

function testRrBehavior(CacheObject) {
describe('RR cache behavior', () => {
it('limits cache size by removing a random item', () => {
const cache = new CacheObject({cacheSize: 5});
const entries = [1, 2, 3, 4, 5, 6];
const get = cache.get.bind(cache);
fillCacheWith(cache, entries);
expect(entries.every(get)).toBe(false);
});

it('shrinks when removing existing items manually', () => {
const cache = new CacheObject({cacheSize: 5});
const entries = [1, 2, 3, 4, 5];
const get = cache.get.bind(cache);
fillCacheWith(cache, entries);
expect(entries.every(get)).toBe(true);
cache.remove('non-existant');
cache.remove(5);
expect(cache.get(5)).toBeUndefined();
cache.set(5, 5);
expect(entries.every(get)).toBe(true);
cache.set(6, 6);
expect(entries.every(get)).toBe(false);
});
});
}

export default testRrBehavior;
17 changes: 17 additions & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4327,6 +4327,15 @@ export class LruObjectCache implements ICacheObject {
isValidCacheKey(key: ObjectCacheKey): boolean;
}

export class RrObjectCache implements ICacheObject {
constructor(options: {cacheSize: number});
set(key: ObjectCacheKey, selectorFn: any): void;
get(key: ObjectCacheKey): any;
remove(key: ObjectCacheKey): void;
clear(): void;
isValidCacheKey(key: ObjectCacheKey): boolean;
}

export class FlatMapCache implements ICacheObject {
set(key: any, selectorFn: any): void;
get(key: any): any;
Expand All @@ -4350,6 +4359,14 @@ export class LruMapCache implements ICacheObject {
clear(): void;
}

export class RrMapCache implements ICacheObject {
constructor(options: {cacheSize: number});
set(key: any, selectorFn: any): void;
get(key: any): any;
remove(key: any): void;
clear(): void;
}

/*
* Key selector creators
*/
Expand Down
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ export {default as LruObjectCache} from './cache/LruObjectCache';
export {default as FlatMapCache} from './cache/FlatMapCache';
export {default as FifoMapCache} from './cache/FifoMapCache';
export {default as LruMapCache} from './cache/LruMapCache';
export {default as RrObjectCache} from './cache/RrObjectCache';
export {default as RrMapCache} from './cache/RrMapCache';

0 comments on commit 7ea7f60

Please sign in to comment.