weak-ref-collections
Advanced tools
Comparing version 1.0.0 to 1.1.0
101
index.js
@@ -0,1 +1,6 @@ | ||
// In Node.js this is packaged into a module | ||
// In the browser exports are bound directly to the window namespace | ||
const global = | ||
typeof exports !== 'undefined' && exports !== null ? exports : this | ||
class WeakRefMap extends Map { | ||
@@ -95,2 +100,96 @@ // Delete the corresponding key when object is collected | ||
} | ||
module.exports = WeakRefMap | ||
// Custom Set with weakly held values (WeakSet does something else) | ||
class WeakRefSet extends Set { | ||
// Used to check existing membership of the underlying target | ||
// Maps the target to its ref | ||
#membership = new WeakMap() | ||
// Delete the corresponding ref when object is collected | ||
#registry = new FinalizationRegistry(ref => { | ||
super.delete(ref) | ||
}) | ||
// When generating with an iterable, use the modified add | ||
// so that we generate weakrefs | ||
constructor (iterable) { | ||
super() | ||
if (iterable) { | ||
for (const value of iterable) { | ||
this.add(value) | ||
} | ||
} | ||
} | ||
// When add wrap the target in a weakref instead | ||
add (value) { | ||
// If it is already contained then skip | ||
if (this.#membership.has(value)) return this | ||
// Otherwise mark the membership | ||
// mark for clean up | ||
// and store the reference | ||
const ref = new WeakRef(value) | ||
this.#membership.set(value, ref) | ||
this.#registry.register(value, ref, ref) | ||
return super.add(ref) | ||
} | ||
has (value) { | ||
const ref = this.#membership.get(value) | ||
if (typeof ref === 'undefined') return false | ||
if (typeof ref.deref() === 'undefined') return false | ||
return true | ||
} | ||
delete (value) { | ||
const ref = this.#membership.get(value) | ||
// Early return if nothing defined | ||
if (typeof ref === 'undefined') return false | ||
// Otherwise an entry was found | ||
this.#membership.delete(value) | ||
this.#registry.unregister(ref) | ||
super.delete(ref) | ||
// Only return a successful delete if ref was still live | ||
if (typeof ref.deref() === 'undefined') return false | ||
return true | ||
} | ||
clear () { | ||
this.#membership = new WeakMap() | ||
this.#registry = new FinalizationRegistry(ref => { | ||
super.delete(ref) | ||
}) | ||
return super.clear() | ||
} | ||
// Follows the map API convention but passes value twice instead of | ||
// value and key | ||
forEach (callback, context) { | ||
for (const value of this) { | ||
callback.call(context, value, value, this) | ||
} | ||
} | ||
// Default iterator | ||
// Iterates but only yields live references | ||
* [Symbol.iterator] () { | ||
for (const ref of super[Symbol.iterator]()) { | ||
const value = ref.deref() | ||
if (typeof value !== 'undefined') yield value | ||
} | ||
} | ||
// The Set API follows a similar structure to Map despite lack of keys | ||
// Returns an array of [value, value] pairs | ||
* entries () { | ||
for (const value of this) { | ||
yield [value, value] | ||
} | ||
} | ||
* keys () { yield * this } | ||
* values () { yield * this } | ||
} | ||
global.WeakRefMap = WeakRefMap | ||
global.WeakRefSet = WeakRefSet |
{ | ||
"name": "weak-ref-collections", | ||
"version": "1.0.0", | ||
"description": "Iterable WeakMaps and WeakSets. Unlike WeakMap which stores keys weakly, this stores keys strongly but stores values using WeakRefs. Is fully iterable. Works just like a normal Map object but holds its values weakly and cleans itself up when its values are garbage collected.", | ||
"version": "1.1.0", | ||
"description": "Iterable WeakMaps and WeakSets. Provides WeakRefMap and WeakRefSet which store values using WeakRefs and clean themselves up when garbage collected.", | ||
"main": "index.js", | ||
@@ -18,13 +18,16 @@ "scripts": { | ||
"type": "git", | ||
"url": "git+https://github.com/fynyky/com.docker.devenvironments.code.git" | ||
"url": "git+https://github.com/fynyky/weak-ref-collections.git" | ||
}, | ||
"keywords": [ | ||
"weakref", | ||
"weakmap", | ||
"weakref", | ||
"weak" | ||
"weakset", | ||
"weak", | ||
"iterable", | ||
"enumerable" | ||
], | ||
"bugs": { | ||
"url": "https://github.com/fynyky/com.docker.devenvironments.code/issues" | ||
"url": "https://github.com/fynyky/weak-ref-collections/issues" | ||
}, | ||
"homepage": "https://github.com/fynyky/com.docker.devenvironments.code#readme" | ||
"homepage": "https://github.com/fynyky/weak-ref-collections#readme" | ||
} |
@@ -1,8 +0,8 @@ | ||
# WeakRefMap | ||
A Map of WeakRefs. Unlike WeakMap which stores keys weakly, this stores keys strongly but stores values using WeakRefs. Is fully iterable. Works just like a normal Map object but holds its values weakly and cleans itself up when its values are garbage collected. | ||
# Weak Ref Collections | ||
Iterable WeakMaps and WeakSets. Provides WeakRefMap and WeakRefSet which store values using WeakRefs and clean themselves up when garbage collected. | ||
Follows the [Map API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) | ||
Unlike WeakMap which stores keys weakly, WeakRefMap stores keys strongly but stores values using WeakRefs. Is fully iterable. Works just like a normal Map object but holds its values weakly and cleans itself up when its values are garbage collected. Follows the [Map API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) | ||
```javascript | ||
const WeakRefMap = require("weak-ref-map"); | ||
const { WeakRefMap } = require("weak-ref-collections"); | ||
@@ -16,3 +16,2 @@ weakRefMap = new WeakRefMap(); | ||
weakRefMap.forEach((value, key, map) => {}); | ||
// Supports iteration | ||
@@ -23,2 +22,20 @@ for (const [key, value] of weakRefMap) {} | ||
for (const value of weakRefMap.values()) {} | ||
``` | ||
Similarly for WeakRefSet is fully iterable unlike WeakSet. Works just like a normal Set object but holds its values weakly and cleans itself up when its values are garbage collected. Follows the [Set API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) | ||
```javascript | ||
const { WeakRefSet } = require("weak-ref-set"); | ||
weakRefSet = new WeakRefSet(); | ||
const element = {"bar": 1}; | ||
weakRefSet.add(element); | ||
weakRefSet.has(element); // true | ||
weakRefSet.delete(element); // true | ||
weakRefSet.clear(); // undefined | ||
weakRefSet.forEach((value, alsoValue, set) => {}); | ||
// Supports iteration | ||
for (const [value, alsoValue] of weakRefSet) {} | ||
for (const [value, alsoValue] of weakRefSet.entries()) {} | ||
for (const value of weakRefSet.keys()) {} | ||
for (const value of weakRefSet.values()) {} | ||
``` |
188
test.js
/* global describe, it */ | ||
const assert = require('assert') | ||
const WeakRefMap = require('./index.js') | ||
const { WeakRefMap, WeakRefSet } = require('./index.js') | ||
describe('WeakValueMap', () => { | ||
describe('WeakRefMap', () => { | ||
let weakRefMap, regularMap | ||
@@ -32,13 +32,13 @@ const normalizeMap = (a) => JSON.stringify(Array.from(a)) | ||
it('can have a value gotten', () => { | ||
const weakValueResult = weakRefMap.get('foo') | ||
const weakRefResult = weakRefMap.get('foo') | ||
const regularResult = regularMap.get('foo') | ||
assert(JSON.stringify(weakValueResult) === '{"qoo":1}') | ||
assert(JSON.stringify(weakValueResult) === JSON.stringify(regularResult)) | ||
assert(JSON.stringify(weakRefResult) === '{"qoo":1}') | ||
assert(JSON.stringify(weakRefResult) === JSON.stringify(regularResult)) | ||
}) | ||
it('can fails to get a nonexistent value', () => { | ||
const weakValueResult = weakRefMap.get('food') | ||
const weakRefResult = weakRefMap.get('food') | ||
const regularResult = regularMap.get('food') | ||
assert(typeof weakValueResult === 'undefined') | ||
assert(JSON.stringify(weakValueResult) === JSON.stringify(regularResult)) | ||
assert(typeof weakRefResult === 'undefined') | ||
assert(JSON.stringify(weakRefResult) === JSON.stringify(regularResult)) | ||
}) | ||
@@ -75,3 +75,3 @@ | ||
] | ||
let weakValueResult = '' | ||
let weakRefResult = '' | ||
let regularResult = '' | ||
@@ -81,3 +81,3 @@ weakRefMap = new WeakRefMap(seedData) | ||
for (const [key, value] of weakRefMap) { | ||
weakValueResult += JSON.stringify([key, value]) | ||
weakRefResult += JSON.stringify([key, value]) | ||
} | ||
@@ -87,11 +87,11 @@ for (const [key, value] of regularMap) { | ||
} | ||
assert(weakValueResult === '["foo",{"qoo":1}]["bar",{"qar":2}]') | ||
assert(weakValueResult === regularResult) | ||
assert(weakRefResult === '["foo",{"qoo":1}]["bar",{"qar":2}]') | ||
assert(weakRefResult === regularResult) | ||
}) | ||
it('can use forEach', () => { | ||
let weakValueResult = '' | ||
let weakRefResult = '' | ||
let regularResult = '' | ||
weakRefMap.forEach((value, key) => { | ||
weakValueResult += JSON.stringify([key, value]) | ||
weakRefResult += JSON.stringify([key, value]) | ||
}) | ||
@@ -101,11 +101,11 @@ regularMap.forEach((value, key) => { | ||
}) | ||
assert(weakValueResult === '["foo",{"qoo":1}]["bar",{"qar":2}]') | ||
assert(weakValueResult === regularResult) | ||
assert(weakRefResult === '["foo",{"qoo":1}]["bar",{"qar":2}]') | ||
assert(weakRefResult === regularResult) | ||
}) | ||
it('can iterate over entries', () => { | ||
let weakValueResult = '' | ||
let weakRefResult = '' | ||
let regularResult = '' | ||
for (const [key, value] of weakRefMap.entries()) { | ||
weakValueResult += JSON.stringify([key, value]) | ||
weakRefResult += JSON.stringify([key, value]) | ||
} | ||
@@ -115,11 +115,11 @@ for (const [key, value] of regularMap) { | ||
} | ||
assert(weakValueResult === '["foo",{"qoo":1}]["bar",{"qar":2}]') | ||
assert(weakValueResult === regularResult) | ||
assert(weakRefResult === '["foo",{"qoo":1}]["bar",{"qar":2}]') | ||
assert(weakRefResult === regularResult) | ||
}) | ||
it('can iterate over keys', () => { | ||
let weakValueResult = '' | ||
let weakRefResult = '' | ||
let regularResult = '' | ||
for (const key of weakRefMap.keys()) { | ||
weakValueResult += JSON.stringify(key) | ||
weakRefResult += JSON.stringify(key) | ||
} | ||
@@ -129,11 +129,11 @@ for (const key of regularMap.keys()) { | ||
} | ||
assert(weakValueResult === '"foo""bar"') | ||
assert(weakValueResult === regularResult) | ||
assert(weakRefResult === '"foo""bar"') | ||
assert(weakRefResult === regularResult) | ||
}) | ||
it('can iterate over values', () => { | ||
let weakValueResult = '' | ||
let weakRefResult = '' | ||
let regularResult = '' | ||
for (const value of weakRefMap.values()) { | ||
weakValueResult += JSON.stringify(value) | ||
weakRefResult += JSON.stringify(value) | ||
} | ||
@@ -143,5 +143,137 @@ for (const value of regularMap.values()) { | ||
} | ||
assert(weakValueResult === '{"qoo":1}{"qar":2}') | ||
assert(weakValueResult === regularResult) | ||
assert(weakRefResult === '{"qoo":1}{"qar":2}') | ||
assert(weakRefResult === regularResult) | ||
}) | ||
}) | ||
describe('WeakRefSet', () => { | ||
let weakRefSet, regularSet | ||
const normalizeSet = (a) => JSON.stringify(Array.from(a)) | ||
it('can be created', () => { | ||
weakRefSet = new WeakRefSet() | ||
regularSet = new Set() | ||
assert(normalizeSet(weakRefSet) === normalizeSet(regularSet)) | ||
}) | ||
it('can be created with iterable', () => { | ||
const seedData = [{ foo: 1 }, { bar: 2 }, { baz: 3 }] | ||
weakRefSet = new WeakRefSet(seedData) | ||
regularSet = new Set(seedData) | ||
assert(normalizeSet(weakRefSet) === normalizeSet(regularSet)) | ||
}) | ||
it('can have a value added', () => { | ||
weakRefSet.add({ qaz: 4 }) | ||
regularSet.add({ qaz: 4 }) | ||
assert(normalizeSet(weakRefSet) === '[{"foo":1},{"bar":2},{"baz":3},{"qaz":4}]') | ||
assert(normalizeSet(weakRefSet) === normalizeSet(regularSet)) | ||
}) | ||
const newElement = { qux: 5 } | ||
it('can have a value added only once', () => { | ||
weakRefSet.add(newElement) | ||
weakRefSet.add(newElement) | ||
regularSet.add(newElement) | ||
regularSet.add(newElement) | ||
assert(normalizeSet(weakRefSet) === '[{"foo":1},{"bar":2},{"baz":3},{"qaz":4},{"qux":5}]') | ||
assert(normalizeSet(weakRefSet) === normalizeSet(regularSet)) | ||
}) | ||
it('can check if it has element', () => { | ||
assert(weakRefSet.has(newElement) === true) | ||
}) | ||
it('can check if it does not have element', () => { | ||
assert(weakRefSet.has({}) === false) | ||
}) | ||
it('can delete an element', () => { | ||
let weakDidDelete = weakRefSet.delete(newElement) | ||
let regularDidDelete = regularSet.delete(newElement) | ||
assert(normalizeSet(weakRefSet) === '[{"foo":1},{"bar":2},{"baz":3},{"qaz":4}]') | ||
assert(normalizeSet(weakRefSet) === normalizeSet(regularSet)) | ||
assert(weakDidDelete === true) | ||
weakDidDelete = weakRefSet.delete(newElement) | ||
regularDidDelete = regularSet.delete(newElement) | ||
assert(weakDidDelete === false) | ||
assert(weakDidDelete === regularDidDelete) | ||
}) | ||
it('can clear itself', () => { | ||
weakRefSet.clear() | ||
regularSet.clear() | ||
assert(normalizeSet(weakRefSet) === '[]') | ||
assert(normalizeSet(weakRefSet) === normalizeSet(regularSet)) | ||
}) | ||
it('can forEach', () => { | ||
const seedData = [{ foo: 1 }, { bar: 2 }, { baz: 3 }] | ||
weakRefSet = new WeakRefSet(seedData) | ||
regularSet = new Set(seedData) | ||
let weakRefResult = '' | ||
let regularResult = '' | ||
weakRefSet.forEach((value, key, set) => { | ||
weakRefResult += (JSON.stringify(value) + JSON.stringify(key)) | ||
assert(set === weakRefSet) | ||
}) | ||
regularSet.forEach((value, key, set) => { | ||
regularResult += (JSON.stringify(value) + JSON.stringify(key)) | ||
assert(set === regularSet) | ||
}) | ||
assert(weakRefResult === '{"foo":1}{"foo":1}{"bar":2}{"bar":2}{"baz":3}{"baz":3}') | ||
assert(weakRefResult === regularResult) | ||
}) | ||
it('can iterate normally', () => { | ||
let weakRefResult = '' | ||
let regularResult = '' | ||
for (const value of weakRefSet) { | ||
weakRefResult += JSON.stringify(value) | ||
} | ||
for (const value of regularSet) { | ||
regularResult += JSON.stringify(value) | ||
} | ||
assert(weakRefResult === '{"foo":1}{"bar":2}{"baz":3}') | ||
assert(weakRefResult === regularResult) | ||
}) | ||
it('can iterate over entries', () => { | ||
let weakRefResult = '' | ||
let regularResult = '' | ||
for (const value of weakRefSet.entries()) { | ||
weakRefResult += JSON.stringify(value) | ||
} | ||
for (const value of regularSet.entries()) { | ||
regularResult += JSON.stringify(value) | ||
} | ||
assert(weakRefResult === '[{"foo":1},{"foo":1}][{"bar":2},{"bar":2}][{"baz":3},{"baz":3}]') | ||
assert(weakRefResult === regularResult) | ||
}) | ||
it('can iterate over keys', () => { | ||
let weakRefResult = '' | ||
let regularResult = '' | ||
for (const value of weakRefSet.keys()) { | ||
weakRefResult += JSON.stringify(value) | ||
} | ||
for (const value of regularSet.keys()) { | ||
regularResult += JSON.stringify(value) | ||
} | ||
assert(weakRefResult === '{"foo":1}{"bar":2}{"baz":3}') | ||
assert(weakRefResult === regularResult) | ||
}) | ||
it('can iterate over values', () => { | ||
let weakRefResult = '' | ||
let regularResult = '' | ||
for (const value of weakRefSet.values()) { | ||
weakRefResult += JSON.stringify(value) | ||
} | ||
for (const value of regularSet.values()) { | ||
regularResult += JSON.stringify(value) | ||
} | ||
assert(weakRefResult === '{"foo":1}{"bar":2}{"baz":3}') | ||
assert(weakRefResult === regularResult) | ||
}) | ||
}) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
16611
413
39