Initial Sample.

This commit is contained in:
2024-06-03 20:23:50 +05:30
parent ef2b65f673
commit 5269ec3c66
2575 changed files with 282312 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
# @wry/trie
A [trie](https://en.wikipedia.org/wiki/Trie) data structure that holds
object keys weakly, yet can also hold non-object keys, unlike `WeakMap`.

View File

@@ -0,0 +1,89 @@
'use strict';
// A [trie](https://en.wikipedia.org/wiki/Trie) data structure that holds
// object keys weakly, yet can also hold non-object keys, unlike the
// native `WeakMap`.
// If no makeData function is supplied, the looked-up data will be an empty,
// null-prototype Object.
var defaultMakeData = function () { return Object.create(null); };
// Useful for processing arguments objects as well as arrays.
var _a = Array.prototype, forEach = _a.forEach, slice = _a.slice;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var Trie = /** @class */ (function () {
function Trie(weakness, makeData) {
if (weakness === void 0) { weakness = true; }
if (makeData === void 0) { makeData = defaultMakeData; }
this.weakness = weakness;
this.makeData = makeData;
}
Trie.prototype.lookup = function () {
return this.lookupArray(arguments);
};
Trie.prototype.lookupArray = function (array) {
var node = this;
forEach.call(array, function (key) { return node = node.getChildTrie(key); });
return hasOwnProperty.call(node, "data")
? node.data
: node.data = this.makeData(slice.call(array));
};
Trie.prototype.peek = function () {
return this.peekArray(arguments);
};
Trie.prototype.peekArray = function (array) {
var node = this;
for (var i = 0, len = array.length; node && i < len; ++i) {
var map = node.mapFor(array[i], false);
node = map && map.get(array[i]);
}
return node && node.data;
};
Trie.prototype.remove = function () {
return this.removeArray(arguments);
};
Trie.prototype.removeArray = function (array) {
var data;
if (array.length) {
var head = array[0];
var map = this.mapFor(head, false);
var child = map && map.get(head);
if (child) {
data = child.removeArray(slice.call(array, 1));
if (!child.data && !child.weak && !(child.strong && child.strong.size)) {
map.delete(head);
}
}
}
else {
data = this.data;
delete this.data;
}
return data;
};
Trie.prototype.getChildTrie = function (key) {
var map = this.mapFor(key, true);
var child = map.get(key);
if (!child)
map.set(key, child = new Trie(this.weakness, this.makeData));
return child;
};
Trie.prototype.mapFor = function (key, create) {
return this.weakness && isObjRef(key)
? this.weak || (create ? this.weak = new WeakMap : void 0)
: this.strong || (create ? this.strong = new Map : void 0);
};
return Trie;
}());
function isObjRef(value) {
switch (typeof value) {
case "object":
if (value === null)
break;
// Fall through to return true...
case "function":
return true;
}
return false;
}
exports.Trie = Trie;
//# sourceMappingURL=bundle.cjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,89 @@
'use strict';
// A [trie](https://en.wikipedia.org/wiki/Trie) data structure that holds
// object keys weakly, yet can also hold non-object keys, unlike the
// native `WeakMap`.
// If no makeData function is supplied, the looked-up data will be an empty,
// null-prototype Object.
var defaultMakeData = function () { return Object.create(null); };
// Useful for processing arguments objects as well as arrays.
var _a = Array.prototype, forEach = _a.forEach, slice = _a.slice;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var Trie = /** @class */ (function () {
function Trie(weakness, makeData) {
if (weakness === void 0) { weakness = true; }
if (makeData === void 0) { makeData = defaultMakeData; }
this.weakness = weakness;
this.makeData = makeData;
}
Trie.prototype.lookup = function () {
return this.lookupArray(arguments);
};
Trie.prototype.lookupArray = function (array) {
var node = this;
forEach.call(array, function (key) { return node = node.getChildTrie(key); });
return hasOwnProperty.call(node, "data")
? node.data
: node.data = this.makeData(slice.call(array));
};
Trie.prototype.peek = function () {
return this.peekArray(arguments);
};
Trie.prototype.peekArray = function (array) {
var node = this;
for (var i = 0, len = array.length; node && i < len; ++i) {
var map = node.mapFor(array[i], false);
node = map && map.get(array[i]);
}
return node && node.data;
};
Trie.prototype.remove = function () {
return this.removeArray(arguments);
};
Trie.prototype.removeArray = function (array) {
var data;
if (array.length) {
var head = array[0];
var map = this.mapFor(head, false);
var child = map && map.get(head);
if (child) {
data = child.removeArray(slice.call(array, 1));
if (!child.data && !child.weak && !(child.strong && child.strong.size)) {
map.delete(head);
}
}
}
else {
data = this.data;
delete this.data;
}
return data;
};
Trie.prototype.getChildTrie = function (key) {
var map = this.mapFor(key, true);
var child = map.get(key);
if (!child)
map.set(key, child = new Trie(this.weakness, this.makeData));
return child;
};
Trie.prototype.mapFor = function (key, create) {
return this.weakness && isObjRef(key)
? this.weak || (create ? this.weak = new WeakMap : void 0)
: this.strong || (create ? this.strong = new Map : void 0);
};
return Trie;
}());
function isObjRef(value) {
switch (typeof value) {
case "object":
if (value === null)
break;
// Fall through to return true...
case "function":
return true;
}
return false;
}
exports.Trie = Trie;
//# sourceMappingURL=bundle.cjs.map

View File

@@ -0,0 +1,16 @@
export declare class Trie<Data> {
private weakness;
private makeData;
private weak?;
private strong?;
private data?;
constructor(weakness?: boolean, makeData?: (array: any[]) => Data);
lookup<T extends any[]>(...array: T): Data;
lookupArray<T extends IArguments | any[]>(array: T): Data;
peek<T extends any[]>(...array: T): Data | undefined;
peekArray<T extends IArguments | any[]>(array: T): Data | undefined;
remove(...array: any[]): Data | undefined;
removeArray<T extends IArguments | any[]>(array: T): Data | undefined;
private getChildTrie;
private mapFor;
}

View File

@@ -0,0 +1,82 @@
// A [trie](https://en.wikipedia.org/wiki/Trie) data structure that holds
// object keys weakly, yet can also hold non-object keys, unlike the
// native `WeakMap`.
// If no makeData function is supplied, the looked-up data will be an empty,
// null-prototype Object.
const defaultMakeData = () => Object.create(null);
// Useful for processing arguments objects as well as arrays.
const { forEach, slice } = Array.prototype;
const { hasOwnProperty } = Object.prototype;
export class Trie {
constructor(weakness = true, makeData = defaultMakeData) {
this.weakness = weakness;
this.makeData = makeData;
}
lookup() {
return this.lookupArray(arguments);
}
lookupArray(array) {
let node = this;
forEach.call(array, key => node = node.getChildTrie(key));
return hasOwnProperty.call(node, "data")
? node.data
: node.data = this.makeData(slice.call(array));
}
peek() {
return this.peekArray(arguments);
}
peekArray(array) {
let node = this;
for (let i = 0, len = array.length; node && i < len; ++i) {
const map = node.mapFor(array[i], false);
node = map && map.get(array[i]);
}
return node && node.data;
}
remove() {
return this.removeArray(arguments);
}
removeArray(array) {
let data;
if (array.length) {
const head = array[0];
const map = this.mapFor(head, false);
const child = map && map.get(head);
if (child) {
data = child.removeArray(slice.call(array, 1));
if (!child.data && !child.weak && !(child.strong && child.strong.size)) {
map.delete(head);
}
}
}
else {
data = this.data;
delete this.data;
}
return data;
}
getChildTrie(key) {
const map = this.mapFor(key, true);
let child = map.get(key);
if (!child)
map.set(key, child = new Trie(this.weakness, this.makeData));
return child;
}
mapFor(key, create) {
return this.weakness && isObjRef(key)
? this.weak || (create ? this.weak = new WeakMap : void 0)
: this.strong || (create ? this.strong = new Map : void 0);
}
}
function isObjRef(value) {
switch (typeof value) {
case "object":
if (value === null)
break;
// Fall through to return true...
case "function":
return true;
}
return false;
}
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,45 @@
{
"name": "@wry/trie",
"version": "0.5.0",
"author": "Ben Newman <ben@eloper.dev>",
"description": "https://en.wikipedia.org/wiki/Trie",
"license": "MIT",
"type": "module",
"main": "lib/bundle.cjs",
"module": "lib/index.js",
"types": "lib/index.d.ts",
"keywords": [
"trie",
"prefix",
"weak",
"dictionary",
"lexicon"
],
"homepage": "https://github.com/benjamn/wryware",
"repository": {
"type": "git",
"url": "git+https://github.com/benjamn/wryware.git"
},
"bugs": {
"url": "https://github.com/benjamn/wryware/issues"
},
"scripts": {
"build": "npm run clean:before && npm run tsc && npm run rollup && npm run clean:after",
"clean:before": "rimraf lib",
"tsc": "npm run tsc:es5 && npm run tsc:esm",
"tsc:es5": "tsc -p tsconfig.es5.json",
"tsc:esm": "tsc -p tsconfig.json",
"rollup": "rollup -c rollup.config.js",
"clean:after": "rimraf lib/es5",
"prepare": "npm run build",
"test:cjs": "../../shared/test.sh lib/tests/bundle.cjs",
"test:esm": "../../shared/test.sh lib/tests/bundle.js",
"test": "npm run test:esm && npm run test:cjs"
},
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": ">=8"
}
}

View File

@@ -0,0 +1 @@
export { default } from "../../shared/rollup.config.js";

View File

@@ -0,0 +1,104 @@
// A [trie](https://en.wikipedia.org/wiki/Trie) data structure that holds
// object keys weakly, yet can also hold non-object keys, unlike the
// native `WeakMap`.
// If no makeData function is supplied, the looked-up data will be an empty,
// null-prototype Object.
const defaultMakeData = () => Object.create(null);
// Useful for processing arguments objects as well as arrays.
const { forEach, slice } = Array.prototype;
const { hasOwnProperty } = Object.prototype;
export class Trie<Data> {
// Since a `WeakMap` cannot hold primitive values as keys, we need a
// backup `Map` instance to hold primitive keys. Both `this._weakMap`
// and `this._strongMap` are lazily initialized.
private weak?: WeakMap<any, Trie<Data>>;
private strong?: Map<any, Trie<Data>>;
private data?: Data;
constructor(
private weakness = true,
private makeData: (array: any[]) => Data = defaultMakeData,
) {}
public lookup<T extends any[]>(...array: T): Data;
public lookup(): Data {
return this.lookupArray(arguments);
}
public lookupArray<T extends IArguments | any[]>(array: T): Data {
let node: Trie<Data> = this;
forEach.call(array, key => node = node.getChildTrie(key));
return hasOwnProperty.call(node, "data")
? node.data as Data
: node.data = this.makeData(slice.call(array));
}
public peek<T extends any[]>(...array: T): Data | undefined;
public peek(): Data | undefined {
return this.peekArray(arguments);
}
public peekArray<T extends IArguments | any[]>(array: T): Data | undefined {
let node: Trie<Data> | undefined = this;
for (let i = 0, len = array.length; node && i < len; ++i) {
const map = node.mapFor(array[i], false);
node = map && map.get(array[i]);
}
return node && node.data;
}
public remove(...array: any[]): Data | undefined;
public remove(): Data | undefined {
return this.removeArray(arguments);
}
public removeArray<T extends IArguments | any[]>(array: T): Data | undefined {
let data: Data | undefined;
if (array.length) {
const head = array[0];
const map = this.mapFor(head, false);
const child = map && map.get(head);
if (child) {
data = child.removeArray(slice.call(array, 1));
if (!child.data && !child.weak && !(child.strong && child.strong.size)) {
map.delete(head);
}
}
} else {
data = this.data;
delete this.data;
}
return data;
}
private getChildTrie(key: any) {
const map = this.mapFor(key, true)!;
let child = map.get(key);
if (!child) map.set(key, child = new Trie<Data>(this.weakness, this.makeData));
return child;
}
private mapFor(key: any, create: boolean): Trie<Data>["weak" | "strong"] | undefined {
return this.weakness && isObjRef(key)
? this.weak || (create ? this.weak = new WeakMap : void 0)
: this.strong || (create ? this.strong = new Map : void 0);
}
}
function isObjRef(value: any) {
switch (typeof value) {
case "object":
if (value === null) break;
// Fall through to return true...
case "function":
return true;
}
return false;
}

View File

@@ -0,0 +1,207 @@
import * as assert from "assert";
import { Trie } from "../index.js";
describe("Trie", function () {
it("can be imported", function () {
assert.strictEqual(typeof Trie, "function");
});
it("can hold objects weakly", function () {
const trie = new Trie<object>(true);
assert.strictEqual((trie as any).weakness, true);
const obj1 = {};
assert.strictEqual(
trie.lookup(obj1, 2, 3),
trie.lookup(obj1, 2, 3),
);
const obj2 = {};
assert.notStrictEqual(
trie.lookup(1, obj2),
trie.lookup(1, obj2, 3),
);
assert.strictEqual((trie as any).weak.has(obj1), true);
assert.strictEqual((trie as any).strong.has(obj1), false);
assert.strictEqual((trie as any).strong.get(1).weak.has(obj2), true);
assert.strictEqual((trie as any).strong.get(1).weak.get(obj2).strong.has(3), true);
});
it("can disable WeakMap", function () {
const trie = new Trie<object>(false);
assert.strictEqual((trie as any).weakness, false);
const obj1 = {};
assert.strictEqual(
trie.lookup(obj1, 2, 3),
trie.lookup(obj1, 2, 3),
);
const obj2 = {};
assert.notStrictEqual(
trie.lookup(1, obj2),
trie.lookup(1, obj2, 3),
);
assert.strictEqual(typeof (trie as any).weak, "undefined");
assert.strictEqual((trie as any).strong.has(obj1), true);
assert.strictEqual((trie as any).strong.has(1), true);
assert.strictEqual((trie as any).strong.get(1).strong.has(obj2), true);
assert.strictEqual((trie as any).strong.get(1).strong.get(obj2).strong.has(3), true);
});
it("can produce data types other than Object", function () {
const symbolTrie = new Trie(true, args => Symbol.for(args.join(".")));
const s123 = symbolTrie.lookup(1, 2, 3);
assert.strictEqual(s123.toString(), "Symbol(1.2.3)");
assert.strictEqual(s123, symbolTrie.lookup(1, 2, 3));
assert.strictEqual(s123, symbolTrie.lookupArray([1, 2, 3]));
const sNull = symbolTrie.lookup();
assert.strictEqual(sNull.toString(), "Symbol()");
const regExpTrie = new Trie(true, args => new RegExp("^(" + args.join("|") + ")$"));
const rXYZ = regExpTrie.lookup("x", "y", "z");
assert.strictEqual(rXYZ.test("w"), false);
assert.strictEqual(rXYZ.test("x"), true);
assert.strictEqual(rXYZ.test("y"), true);
assert.strictEqual(rXYZ.test("z"), true);
assert.strictEqual(String(rXYZ), "/^(x|y|z)$/");
class Data {
constructor(public readonly args: any[]) {}
}
const dataTrie = new Trie(true, args => new Data(args));
function checkData(...args: any[]) {
const data = dataTrie.lookupArray(args);
assert.strictEqual(data instanceof Data, true);
assert.notStrictEqual(data.args, args);
assert.deepStrictEqual(data.args, args);
assert.strictEqual(data, dataTrie.lookup(...args));
assert.strictEqual(data, dataTrie.lookupArray(arguments));
return data;
}
const datas = [
checkData(),
checkData(1),
checkData(1, 2),
checkData(2),
checkData(2, 3),
checkData(true, "a"),
checkData(/asdf/i, "b", function oyez() {}),
];
// Verify that all Data objects are distinct.
assert.strictEqual(new Set(datas).size, datas.length);
});
it("can peek at values", function () {
const trie = new Trie(true, (args) => args);
const obj = {};
assert.strictEqual(trie.peek(1, 2, 'x'), undefined);
assert.strictEqual(trie.peek(1, 2, obj), undefined);
assert.strictEqual(trie.peekArray([1, 2, 'x']), undefined);
assert.strictEqual(trie.peekArray([1, 2, obj]), undefined);
// peek/peekArray should not create anything on its own
assert.strictEqual(trie['weak'], undefined);
assert.strictEqual(trie['strong'], undefined);
assert.strictEqual(trie['data'], undefined);
const data1 = trie.lookup(1, 2, 'x');
const data2 = trie.lookup(1, 2, obj);
assert.strictEqual(trie.peek(1, 2, 'x'), data1);
assert.strictEqual(trie.peek(1, 2, obj), data2);
assert.strictEqual(trie.peekArray([1, 2, 'x']), data1);
assert.strictEqual(trie.peekArray([1, 2, obj]), data2);
});
describe("can remove values", function () {
it("will remove values", () => {
const trie = new Trie(true, (args) => args);
trie.lookup(1, 2, "x");
trie.remove(1, 2, "x");
assert.strictEqual(trie.peek(1, 2, "x"), undefined);
});
it("removing will return the value", () => {
const trie = new Trie(true, (args) => args);
const data = trie.lookup(1, 2, "x");
assert.strictEqual(trie.remove(1, 2, "x"), data);
});
it("will remove empty parent nodes", () => {
const trie = new Trie(true, (args) => args);
const data = trie.lookup(1, 2, "x");
assert.strictEqual(trie.peek(1, 2, "x"), data);
assert.equal(pathExistsInTrie(trie, 1, 2, "x"), true);
assert.strictEqual(trie.remove(1, 2, "x"), data);
assert.equal(pathExistsInTrie(trie, 1), false);
});
it("will not remove parent nodes with other children", () => {
const trie = new Trie(true, (args) => args);
trie.lookup(1, 2, "x");
const data = trie.lookup(1, 2);
trie.remove(1, 2, "x");
assert.strictEqual(trie.peek(1, 2, "x"), undefined);
assert.strictEqual(trie.peek(1, 2), data);
});
it("will remove data, not the full node, if a node still has children", () => {
const trie = new Trie(true, (args) => args);
trie.lookup(1, 2);
const data = trie.lookup(1, 2, "x");
trie.remove(1, 2);
assert.strictEqual(trie.peek(1, 2), undefined);
assert.strictEqual(trie.peek(1, 2, "x"), data);
});
it("will remove direct children", () => {
const trie = new Trie(true, (args) => args);
trie.lookup(1);
trie.remove(1);
assert.strictEqual(trie.peek(1), undefined);
});
it("will remove nodes from WeakMaps", () => {
const trie = new Trie(true, (args) => args);
const obj = {};
const data = trie.lookup(1, obj, "x");
assert.equal(pathExistsInTrie(trie, 1), true);
assert.strictEqual(trie.remove(1, obj, "x"), data);
assert.strictEqual(trie.peek(1, obj, "x"), undefined);
assert.equal(pathExistsInTrie(trie, 1, obj), false);
});
it("will not remove nodes if they contain an (even empty) WeakMap", () => {
const trie = new Trie(true, (args) => args);
const obj = {};
const data = trie.lookup(1, 2, "x");
trie.lookup(1, obj);
trie.remove(1, obj);
assert.strictEqual(trie.peek(1, 2, "x"), data);
assert.equal(pathExistsInTrie(trie, 1), true);
assert.equal(pathExistsInTrie(trie, 1, 2), true);
assert.strictEqual(trie.remove(1, 2, "x"), data);
assert.equal(pathExistsInTrie(trie, 1), true);
assert.equal(pathExistsInTrie(trie, 1, 2), false);
});
});
function pathExistsInTrie(trie: Trie<unknown>, ...path: any[]) {
return (
path.reduce((node: Trie<unknown> | undefined, key: any) => {
const map: Trie<unknown>["weak" | "strong"] =
// not the full implementation but enough for a test
trie["weakness"] && typeof key === "object"
? node?.["weak"]
: node?.["strong"];
return map?.get(key);
}, trie) !== undefined
);
}
});