Skip to content

Instantly share code, notes, and snippets.

@DavidWells
Created March 13, 2022 06:58
Show Gist options
  • Save DavidWells/95927d8f2e3912cdc3262fd5dcb87c77 to your computer and use it in GitHub Desktop.
Save DavidWells/95927d8f2e3912cdc3262fd5dcb87c77 to your computer and use it in GitHub Desktop.
Find closest object value via levenstein algo & JS proxy
// https://github.com/SiddharthShyniben/typosquatter
const peq = new Uint32Array(0x10000);
const myers_32 = (a, b) => {
const n = a.length;
const m = b.length;
const lst = 1 << (n - 1);
let pv = -1;
let mv = 0;
let sc = n;
let i = m;
while (i--)
peq[b.charCodeAt(i)] = 0;
i = n;
while (i--)
peq[a.charCodeAt(i)] |= 1 << i;
for (i = 0; i < m; i++) {
let eq = peq[b.charCodeAt(i)];
const xv = eq | mv;
eq |= ((eq & pv) + pv) ^ pv;
mv |= ~(eq | pv);
pv &= eq;
if (mv & lst)
sc++;
if (pv & lst)
sc--;
mv = (mv << 1) | 1;
pv = (pv << 1) | ~(xv | mv);
mv &= xv;
}
return sc;
};
const myers_x = (b, a) => {
const n = a.length;
const m = b.length;
const mhc = [];
const phc = [];
const hsize = Math.ceil(n / 32);
const vsize = Math.ceil(m / 32);
let score = m;
for (let i = 0; i < hsize; i++) {
phc[i] = -1;
mhc[i] = 0;
}
let j = 0;
for (; j < vsize - 1; j++) {
let mv = 0;
let pv = -1;
const start = j * 32;
const vlen = Math.min(32, m - start);
let k = n;
while (k--)
peq[a.charCodeAt(k)] = 0;
for (k = start; k < start + vlen; k++) {
peq[b.charCodeAt(k)] |= 1 << k;
}
score = m;
for (let i = 0; i < n; i++) {
const eq = peq[a.charCodeAt(i)];
const pb = (phc[(i / 32) | 0] >>> i % 32) & 1;
const mb = (mhc[(i / 32) | 0] >>> i % 32) & 1;
const xv = eq | mv;
const xh = ((((eq | mb) & pv) + pv) ^ pv) | eq | mb;
let ph = mv | ~(xh | pv);
let mh = pv & xh;
if ((ph >>> 31) ^ pb)
phc[(i / 32) | 0] ^= 1 << i % 32;
if ((mh >>> 31) ^ mb)
mhc[(i / 32) | 0] ^= 1 << i % 32;
ph = (ph << 1) | pb;
mh = (mh << 1) | mb;
pv = mh | ~(xv | ph);
mv = ph & xv;
}
}
let mv = 0;
let pv = -1;
const start = j * 32;
const vlen = Math.min(32, m - start);
let k = n;
while (k--)
peq[a.charCodeAt(k)] = 0;
for (k = start; k < start + vlen; k++) {
peq[b.charCodeAt(k)] |= 1 << k;
}
score = m;
for (let i = 0; i < n; i++) {
const eq = peq[a.charCodeAt(i)];
const pb = (phc[(i / 32) | 0] >>> i % 32) & 1;
const mb = (mhc[(i / 32) | 0] >>> i % 32) & 1;
const xv = eq | mv;
const xh = ((((eq | mb) & pv) + pv) ^ pv) | eq | mb;
let ph = mv | ~(xh | pv);
let mh = pv & xh;
score += (ph >>> ((m % 32) - 1)) & 1;
score -= (mh >>> ((m % 32) - 1)) & 1;
if ((ph >>> 31) ^ pb)
phc[(i / 32) | 0] ^= 1 << i % 32;
if ((mh >>> 31) ^ mb)
mhc[(i / 32) | 0] ^= 1 << i % 32;
ph = (ph << 1) | pb;
mh = (mh << 1) | mb;
pv = mh | ~(xv | ph);
mv = ph & xv;
}
return score;
};
function distance(a, b) {
if (a.length < b.length) {
const tmp = b;
b = a;
a = tmp;
}
if (b.length === 0)
return a.length;
if (a.length <= 32) {
return myers_32(a, b);
}
return myers_x(a, b);
}
function closest(str, arr) {
let min_distance = Infinity;
let min_index = 0;
for (let i = 0; i < arr.length; i++) {
const dist = distance(str, arr[i]);
if (dist < min_distance) {
min_distance = dist;
min_index = i;
}
}
return arr[min_index];
}
const proxyGet = (obj, prop) => {
if (!(prop in obj))
prop = closest(prop, Object.getOwnPropertyNames(obj));
return typeof obj[prop] === 'object' ?
new Proxy(obj[prop], { get: proxyGet }) :
new Proxy({ value: obj[prop] }, { get: primProxyGet });
};
const primProxyGet = (obj, prop) => {
const prim = Reflect.get(obj, 'value');
let value = prim[prop];
if (!value) {
prop = closest(prop, Object.getOwnPropertyNames(Object.getPrototypeOf('')));
value = prim[prop];
}
if (typeof value === 'function')
return value.bind(prim);
return typeof value === 'object' ?
new Proxy(value, { get: proxyGet }) :
new Proxy({ value }, { get: primProxyGet });
};
const typosquatter = (obj) => new Proxy(obj, { get: typeof obj == 'object' ? proxyGet : primProxyGet });
let obj = typosquatter({foo: {bar: {baz: 'lol'}}});
console.log(obj.fo.ba.bz)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment