Skip to content

Commit e5614c7

Browse files
authored
feat(memoization): memoizers can be injected through opts.memoize (#90)
Fixes: #76 BREAKING CHANGE: If you were passing an object to opts.memoize, it will now be used as an injected memoization object. If you were only passing booleans and other non-objects through that option, no changes are needed.
1 parent 0e55dc9 commit e5614c7

File tree

5 files changed

+137
-23
lines changed

5 files changed

+137
-23
lines changed

Diff for: README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -418,9 +418,9 @@ If provided, cacache will memoize the given cache insertion in memory, bypassing
418418
any filesystem checks for that key or digest in future cache fetches. Nothing
419419
will be written to the in-memory cache unless this option is explicitly truthy.
420420

421-
There is no facility for limiting memory usage short of
422-
[`cacache.clearMemoized()`](#clear-memoized), so be mindful of the sort of data
423-
you ask to get memoized!
421+
If `opts.memoize` is an object or a `Map`-like (that is, an object with `get`
422+
and `set` methods), it will be written to instead of the global memoization
423+
cache.
424424

425425
Reading from existing memoized data can be forced by explicitly passing
426426
`memoize: false` to the reader functions, but their default will be to read from

Diff for: get.js

+10-9
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ function getData (byDigest, cache, key, opts) {
1919
opts = opts || {}
2020
const memoized = (
2121
byDigest
22-
? memo.get.byDigest(cache, key)
23-
: memo.get(cache, key)
22+
? memo.get.byDigest(cache, key, opts)
23+
: memo.get(cache, key, opts)
2424
)
2525
if (memoized && opts.memoize !== false) {
2626
return BB.resolve(byDigest ? memoized : {
@@ -46,9 +46,9 @@ function getData (byDigest, cache, key, opts) {
4646
integrity: entry.integrity
4747
}).then(res => {
4848
if (opts.memoize && byDigest) {
49-
memo.put.byDigest(cache, key, res)
49+
memo.put.byDigest(cache, key, res, opts)
5050
} else if (opts.memoize) {
51-
memo.put(cache, entry, res.data)
51+
memo.put(cache, entry, res.data, opts)
5252
}
5353
return res
5454
})
@@ -59,7 +59,7 @@ module.exports.stream = getStream
5959
function getStream (cache, key, opts) {
6060
opts = opts || {}
6161
let stream = through()
62-
const memoized = memo.get(cache, key)
62+
const memoized = memo.get(cache, key, opts)
6363
if (memoized && opts.memoize !== false) {
6464
stream.on('newListener', function (ev, cb) {
6565
ev === 'metadata' && cb(memoized.entry.metadata)
@@ -84,7 +84,7 @@ function getStream (cache, key, opts) {
8484
memoLength += c.length
8585
cb(null, c, en)
8686
}, cb => {
87-
memoData && memo.put(cache, entry, Buffer.concat(memoData, memoLength))
87+
memoData && memo.put(cache, entry, Buffer.concat(memoData, memoLength), opts)
8888
cb()
8989
})
9090
} else {
@@ -111,7 +111,7 @@ function getStream (cache, key, opts) {
111111
module.exports.stream.byDigest = getStreamDigest
112112
function getStreamDigest (cache, integrity, opts) {
113113
opts = opts || {}
114-
const memoized = memo.get.byDigest(cache, integrity)
114+
const memoized = memo.get.byDigest(cache, integrity, opts)
115115
if (memoized && opts.memoize !== false) {
116116
const stream = through()
117117
stream.write(memoized, () => stream.end())
@@ -129,7 +129,8 @@ function getStreamDigest (cache, integrity, opts) {
129129
memoData && memo.put.byDigest(
130130
cache,
131131
integrity,
132-
Buffer.concat(memoData, memoLength)
132+
Buffer.concat(memoData, memoLength),
133+
opts
133134
)
134135
cb()
135136
})
@@ -142,7 +143,7 @@ function getStreamDigest (cache, integrity, opts) {
142143
module.exports.info = info
143144
function info (cache, key, opts) {
144145
opts = opts || {}
145-
const memoized = memo.get(cache, key)
146+
const memoized = memo.get(cache, key, opts)
146147
if (memoized && opts.memoize !== false) {
147148
return BB.resolve(memoized.entry)
148149
} else {

Diff for: lib/memoization.js

+29-9
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,42 @@ function clearMemoized () {
2828
}
2929

3030
module.exports.put = put
31-
function put (cache, entry, data) {
32-
MEMOIZED.set(`key:${cache}:${entry.key}`, { entry, data })
33-
putDigest(cache, entry.integrity, data)
31+
function put (cache, entry, data, opts) {
32+
pickMem(opts).set(`key:${cache}:${entry.key}`, { entry, data })
33+
putDigest(cache, entry.integrity, data, opts)
3434
}
3535

3636
module.exports.put.byDigest = putDigest
37-
function putDigest (cache, integrity, data) {
38-
MEMOIZED.set(`digest:${cache}:${integrity}`, data)
37+
function putDigest (cache, integrity, data, opts) {
38+
pickMem(opts).set(`digest:${cache}:${integrity}`, data)
3939
}
4040

4141
module.exports.get = get
42-
function get (cache, key) {
43-
return MEMOIZED.get(`key:${cache}:${key}`)
42+
function get (cache, key, opts) {
43+
return pickMem(opts).get(`key:${cache}:${key}`)
4444
}
4545

4646
module.exports.get.byDigest = getDigest
47-
function getDigest (cache, integrity) {
48-
return MEMOIZED.get(`digest:${cache}:${integrity}`)
47+
function getDigest (cache, integrity, opts) {
48+
return pickMem(opts).get(`digest:${cache}:${integrity}`)
49+
}
50+
51+
class ObjProxy {
52+
constructor (obj) {
53+
this.obj = obj
54+
}
55+
get (key) { return this.obj[key] }
56+
set (key, val) { this.obj[key] = val }
57+
}
58+
59+
function pickMem (opts) {
60+
if (!opts || !opts.memoize) {
61+
return MEMOIZED
62+
} else if (opts.memoize.get && opts.memoize.set) {
63+
return opts.memoize
64+
} else if (typeof opts.memoize === 'object') {
65+
return new ObjProxy(opts.memoize)
66+
} else {
67+
return MEMOIZED
68+
}
4969
}

Diff for: put.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ function putData (cache, key, data, opts) {
1313
opts.size = res.size
1414
return index.insert(cache, key, res.integrity, opts).then(entry => {
1515
if (opts.memoize) {
16-
memo.put(cache, entry, data)
16+
memo.put(cache, entry, data, opts)
1717
}
1818
return res.integrity
1919
})
@@ -49,7 +49,7 @@ function putStream (cache, key, opts) {
4949
opts.size = size
5050
index.insert(cache, key, integrity, opts).then(entry => {
5151
if (opts.memoize) {
52-
memo.put(cache, entry, Buffer.concat(memoData, memoTotal))
52+
memo.put(cache, entry, Buffer.concat(memoData, memoTotal), opts)
5353
}
5454
stream.emit('integrity', integrity)
5555
cb()

Diff for: test/memoization.js

+93
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,96 @@ test('can clear out the memoization cache', t => {
6666
)
6767
t.done()
6868
})
69+
70+
test('accepts optional injected cache', t => {
71+
memo.clearMemoized()
72+
const MEMO = new Map()
73+
memo.put(CACHE, ENTRY, DATA, {memoize: MEMO})
74+
t.deepEqual(
75+
memo.get(CACHE, ENTRY.key),
76+
null,
77+
'entry not in global memo cache'
78+
)
79+
t.deepEqual(
80+
memo.get(CACHE, ENTRY.key, {memoize: MEMO}),
81+
{entry: ENTRY, data: DATA},
82+
'entry fetched from injected memoizer'
83+
)
84+
t.deepEqual(
85+
memo.get.byDigest(CACHE, ENTRY.integrity, {memoize: MEMO}),
86+
DATA,
87+
'content entry fetched from injected memoizer'
88+
)
89+
t.deepEqual(
90+
MEMO.get(`key:${CACHE}:${ENTRY.key}`),
91+
{entry: ENTRY, data: DATA},
92+
'entry is in the injected memoizer'
93+
)
94+
t.deepEqual(
95+
MEMO.get(`digest:${CACHE}:${ENTRY.integrity}`),
96+
DATA,
97+
'content entry is in the injected memoizer'
98+
)
99+
MEMO.clear()
100+
t.deepEqual(
101+
memo.get(CACHE, ENTRY.key, {memoize: MEMO}),
102+
null,
103+
'tried to read from cleared memoizer'
104+
)
105+
t.deepEqual(
106+
memo.get.byDigest(CACHE, ENTRY.integrity, {memoize: MEMO}),
107+
null,
108+
'tried to read by digest from cleared memoizer'
109+
)
110+
memo.put.byDigest(CACHE, ENTRY.integrity, DATA, {memoize: MEMO})
111+
t.deepEqual(
112+
MEMO.get(`digest:${CACHE}:${ENTRY.integrity}`),
113+
DATA,
114+
'content entry is in the injected memoizer'
115+
)
116+
const obj = {}
117+
memo.put(CACHE, ENTRY, DATA, {memoize: obj})
118+
t.deepEqual(
119+
memo.get(CACHE, ENTRY.key, {memoize: obj}),
120+
{entry: ENTRY, data: DATA},
121+
'entry fetched from injected object memoizer'
122+
)
123+
t.deepEqual(
124+
memo.get.byDigest(CACHE, ENTRY.integrity, {memoize: MEMO}),
125+
DATA,
126+
'content entry fetched from injected object memoizer'
127+
)
128+
memo.clearMemoized()
129+
memo.put(CACHE, ENTRY, DATA, {memoize: 'foo'})
130+
t.deepEqual(
131+
memo.get(CACHE, ENTRY.key, {memoize: 'foo'}),
132+
{entry: ENTRY, data: DATA},
133+
'entry fetched from global memoization obj on non-obj option'
134+
)
135+
t.deepEqual(
136+
memo.get(CACHE, ENTRY.key, {memoize: 'foo'}),
137+
{entry: ENTRY, data: DATA},
138+
'entry fetched from global memoization obj on non-obj option'
139+
)
140+
t.deepEqual(
141+
memo.get.byDigest(CACHE, ENTRY.integrity, {memoize: 'foo'}),
142+
DATA,
143+
'content entry fetched global memoizer obj on non-obj option'
144+
)
145+
t.deepEqual(
146+
memo.get.byDigest(CACHE, ENTRY.integrity, {memoize: 'foo'}),
147+
DATA,
148+
'content entry fetched global memoizer obj on non-obj option'
149+
)
150+
t.deepEqual(
151+
memo.get.byDigest(CACHE, ENTRY.integrity, {memoize: false}),
152+
DATA,
153+
'content entry fetched global memoizer obj on non-obj option'
154+
)
155+
t.deepEqual(
156+
memo.get.byDigest(CACHE, ENTRY.integrity, {memoize: false}),
157+
DATA,
158+
'content entry fetched global memoizer obj on non-obj option'
159+
)
160+
t.done()
161+
})

0 commit comments

Comments
 (0)