Created
March 13, 2022 06:58
-
-
Save DavidWells/95927d8f2e3912cdc3262fd5dcb87c77 to your computer and use it in GitHub Desktop.
Find closest object value via levenstein algo & JS proxy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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