Files
SnowLuma/.pnpm-store/v11/files/ec/bb71d23a7d0f22a36aedc346209e2305514ce42eaeff0af32301876b590f1aec6cf48086543ed5ae15e2546981aaa56db05055ce938e4f22b094f2b3bb4a3e
T
TanTan 2a69b23b47
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
docs: 仓库链接指向本镜像仓库
2026-11-13 09:27:07 +08:00

127 lines
3.6 KiB
Plaintext

import { MatcherBaseClass } from '../../types.mjs';
import Scoring from '../../scoring/index.mjs';
/*
*-------------------------------------------------------------------------------
* repeats (aaa, abcabcabc) ------------------------------
*-------------------------------------------------------------------------------
*/
class MatchRepeat extends MatcherBaseClass {
constructor(options) {
super(options);
this.scoring = new Scoring(options);
}
// eslint-disable-next-line max-statements
match({
password,
omniMatch
}) {
const matches = [];
let lastIndex = 0;
while (lastIndex < password.length) {
const greedyMatch = this.getGreedyMatch(password, lastIndex);
const lazyMatch = this.getLazyMatch(password, lastIndex);
if (greedyMatch == null) {
break;
}
const {
match,
baseToken
} = this.setMatchToken(greedyMatch, lazyMatch);
if (match) {
const j = match.index + match[0].length - 1;
const baseGuesses = this.getBaseGuesses(baseToken, omniMatch);
matches.push(this.normalizeMatch(baseToken, j, match, baseGuesses));
lastIndex = j + 1;
}
}
const hasPromises = matches.some(match => {
return match instanceof Promise;
});
if (hasPromises) {
// eslint-disable-next-line @typescript-eslint/await-thenable
return Promise.all(matches);
}
return matches;
}
// eslint-disable-next-line max-params
normalizeMatch(baseToken, j, match, baseGuesses) {
const baseMatch = {
pattern: 'repeat',
i: match.index,
j,
token: match[0],
baseToken,
baseGuesses: 0,
repeatCount: match[0].length / baseToken.length
};
if (baseGuesses instanceof Promise) {
return baseGuesses.then(resolvedBaseGuesses => {
return {
...baseMatch,
baseGuesses: resolvedBaseGuesses
};
});
}
return {
...baseMatch,
baseGuesses
};
}
getGreedyMatch(password, lastIndex) {
const greedy = /(.+)\1+/g;
greedy.lastIndex = lastIndex;
return greedy.exec(password);
}
getLazyMatch(password, lastIndex) {
const lazy = /(.+?)\1+/g;
lazy.lastIndex = lastIndex;
return lazy.exec(password);
}
setMatchToken(greedyMatch, lazyMatch) {
const lazyAnchored = /^(.+?)\1+$/;
let match;
let baseToken = '';
if (lazyMatch && greedyMatch[0].length > lazyMatch[0].length) {
// greedy beats lazy for 'aabaab'
// greedy: [aabaab, aab]
// lazy: [aa, a]
match = greedyMatch;
// greedy's repeated string might itself be repeated, eg.
// aabaab in aabaabaabaab.
// run an anchored lazy match on greedy's repeated string
// to find the shortest repeated string
const temp = lazyAnchored.exec(match[0]);
if (temp) {
baseToken = temp[1];
}
} else {
// lazy beats greedy for 'aaaaa'
// greedy: [aaaa, aa]
// lazy: [aaaaa, a]
match = lazyMatch;
if (match) {
baseToken = match[1];
}
}
return {
match,
baseToken
};
}
getBaseGuesses(baseToken, omniMatch) {
const matches = omniMatch.match(baseToken);
if (matches instanceof Promise) {
return matches.then(resolvedMatches => {
const baseAnalysis = this.scoring.mostGuessableMatchSequence(baseToken, resolvedMatches);
return baseAnalysis.guesses;
});
}
const baseAnalysis = this.scoring.mostGuessableMatchSequence(baseToken, matches);
return baseAnalysis.guesses;
}
}
export { MatchRepeat as default };
//# sourceMappingURL=matching.mjs.map