diff --git a/examples/package-lock.json b/examples/package-lock.json index 04b2b79..ac0cea3 100644 --- a/examples/package-lock.json +++ b/examples/package-lock.json @@ -13,24 +13,24 @@ } }, "..": { - "version": "4.0.1", + "version": "4.1.0", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.18.2", "@eslint/js": "^9.29.0", "@tsconfig/node20": "^20.1.6", - "@types/node": "^18.19.112", + "@types/node": "^20.19.1", "@vitest/coverage-istanbul": "^3.2.4", "cross-env": "^7.0.3", "eslint": "^9.29.0", "eslint-plugin-simple-import-sort": "^12.1.1", "globals": "^16.2.0", "lorem-ipsum": "^2.0.8", - "prettier": "^3.5.3", + "prettier": "^3.6.1", "shx": "^0.4.0", - "tsdown": "^0.12.8", + "tsdown": "^0.12.9", "typescript": "^5.8.3", - "typescript-eslint": "^8.34.1", + "typescript-eslint": "^8.35.0", "vitest": "^3.2.4" }, "engines": { diff --git a/src/Procedures/procExecRegExp.test.ts b/src/Procedures/procExecRegExp.test.ts index 219b23b..2925a9b 100644 --- a/src/Procedures/procExecRegExp.test.ts +++ b/src/Procedures/procExecRegExp.test.ts @@ -27,7 +27,7 @@ describe('procExecRegExp', () => { test('RequestExecRegExp bad regex', () => { const req: RequestExecRegExp = createRequest(requestTypeExecRegExp, { text: 'two words', regexp: /\[/g }); - Object.assign(req.data, { regexp: '/[/g' }); + Object.assign(req.data, { regexp: { source: '/[/g' } }); const result = procExecRegExp(req); const response = isErrorResponse(result) ? result : undefined; expect(isExecRegExpResponse(result)).toBe(false); diff --git a/src/Procedures/procExecRegExp.ts b/src/Procedures/procExecRegExp.ts index 83c176b..58cd8c6 100644 --- a/src/Procedures/procExecRegExp.ts +++ b/src/Procedures/procExecRegExp.ts @@ -1,5 +1,6 @@ import type { ExecRegExpResult } from '../helpers/evaluateRegExp.js'; import { execRegExp, toRegExp } from '../helpers/evaluateRegExp.js'; +import type { RegExpLike } from '../helpers/regexp.js'; import { format } from '../util/format.js'; import { createErrorResponse, @@ -18,7 +19,7 @@ export const requestTypeExecRegExp: ExecRegExpRequestType = 'ExecRegExp'; export interface RequestExecRegExpData { text: string; - regexp: RegExp; + regexp: RegExpLike; } export type RequestExecRegExp = Request; diff --git a/src/Procedures/procMatchAllRegExp.test.ts b/src/Procedures/procMatchAllRegExp.test.ts index e1e8ce0..93020dd 100644 --- a/src/Procedures/procMatchAllRegExp.test.ts +++ b/src/Procedures/procMatchAllRegExp.test.ts @@ -27,7 +27,7 @@ describe('procMatchAllRegExp', () => { test('RequestExecRegExp bad regex', () => { const req: RequestMatchAllRegExp = createRequestMatchAllRegExp({ text: 'two words', regexp: /\[/g }); - Object.assign(req.data, { regexp: '/[/g' }); + Object.assign(req.data, { regexp: { source: '[', flags: 'g' } }); const result = procMatchAllRegExp(req); const response = isErrorResponse(result) ? result : undefined; expect(isMatchAllRegExpResponse(result)).toBe(false); diff --git a/src/Procedures/procMatchAllRegExp.ts b/src/Procedures/procMatchAllRegExp.ts index d8ea1bb..20c666d 100644 --- a/src/Procedures/procMatchAllRegExp.ts +++ b/src/Procedures/procMatchAllRegExp.ts @@ -1,5 +1,6 @@ import type { MatchAllRegExpResult } from '../helpers/evaluateRegExp.js'; import { matchAllRegExp, toRegExp } from '../helpers/evaluateRegExp.js'; +import type { RegExpLike } from '../helpers/regexp.js'; import { format } from '../util/format.js'; import type { ErrorResponse, Request, Response } from './procedure.js'; import { createErrorResponse, createRequest, createResponse, isRequestType, isResponseType } from './procedure.js'; @@ -10,7 +11,7 @@ export type MatchAllRegExpResponseType = MatchAllRegExpRequestType; export interface RequestMatchAllRegExpData { text: string; - regexp: RegExp; + regexp: RegExpLike; } export type RequestMatchAllRegExp = Request; diff --git a/src/Procedures/procMatchAllRegExpArray.test.ts b/src/Procedures/procMatchAllRegExpArray.test.ts index bd98e17..eb95109 100644 --- a/src/Procedures/procMatchAllRegExpArray.test.ts +++ b/src/Procedures/procMatchAllRegExpArray.test.ts @@ -27,7 +27,7 @@ describe('procMatchRegExpArray', () => { test('RequestExecRegExp bad regex', () => { const req: Request = createRequestMatchRegExpArray({ text: 'two words', regexps: [/\[/g] }); - Object.assign(req.data as string[], { regexps: ['/[/g'] }); // Intentionally malformed regex + Object.assign(req.data as string[], { regexps: [{ source: '[', flags: 'g' }] }); // Intentionally malformed regex const result = procMatchAllRegExpArray(req); const response = isErrorResponse(result) ? result : undefined; expect(isMatchRegExpArrayResponse(result)).toBe(false); diff --git a/src/Procedures/procMatchAllRegExpArray.ts b/src/Procedures/procMatchAllRegExpArray.ts index 43d5125..b0bff70 100644 --- a/src/Procedures/procMatchAllRegExpArray.ts +++ b/src/Procedures/procMatchAllRegExpArray.ts @@ -1,5 +1,6 @@ import type { MatchAllRegExpArrayResult } from '../helpers/evaluateRegExp.js'; import { matchAllRegExpArray, toRegExp } from '../helpers/evaluateRegExp.js'; +import type { RegExpLike } from '../helpers/regexp.js'; import { format } from '../util/format.js'; import type { ErrorResponse, Request, Response } from './procedure.js'; import { createErrorResponse, createRequest, createResponse, isRequestType, isResponseType } from './procedure.js'; @@ -10,7 +11,7 @@ export type MatchRegExpArrayResponseType = MatchRegExpArrayRequestType; export interface RequestMatchRegExpArrayData { text: string; - regexps: RegExp[]; + regexps: RegExpLike[]; } export type RequestMatchAllRegExpArray = Request; diff --git a/src/Procedures/procMatchAllRegExpAsRange.test.ts b/src/Procedures/procMatchAllRegExpAsRange.test.ts index 1b5d33f..39ea0bf 100644 --- a/src/Procedures/procMatchAllRegExpAsRange.test.ts +++ b/src/Procedures/procMatchAllRegExpAsRange.test.ts @@ -33,7 +33,7 @@ describe('procMatchAllRegExpAsRange', () => { test('RequestExecRegExp bad regex', () => { const req: RequestMatchAllRegExpAsRange = createRequestMatchAllRegExpAsRange({ text: 'two words', regexp: /\[/g }); - Object.assign(req.data, { regexp: '/[/g' }); + Object.assign(req.data, { regexp: { source: '[', flags: 'g' } }); const result = procMatchAllRegExpAsRange(req); const response = isErrorResponse(result) ? result : undefined; expect(isMatchAllRegExpAsRangeResponse(result)).toBe(false); diff --git a/src/Procedures/procMatchAllRegExpAsRange.ts b/src/Procedures/procMatchAllRegExpAsRange.ts index c63a5f9..30e67f1 100644 --- a/src/Procedures/procMatchAllRegExpAsRange.ts +++ b/src/Procedures/procMatchAllRegExpAsRange.ts @@ -1,5 +1,6 @@ import type { MatchAllToRangesRegExpResult } from '../helpers/evaluateRegExp.js'; import { matchAllToRangesRegExp, toRegExp } from '../helpers/evaluateRegExp.js'; +import type { RegExpLike } from '../helpers/regexp.js'; import { format } from '../util/format.js'; import type { ErrorResponse, Request, Response } from './procedure.js'; import { createErrorResponse, createRequest, createResponse, isRequestType, isResponseType } from './procedure.js'; @@ -10,7 +11,7 @@ export type MatchAllRegExpAsRangeResponseType = MatchAllRegExpAsRangeRequestType export interface RequestMatchAllRegExpAsRangeData { text: string; - regexp: RegExp; + regexp: RegExpLike; } export type RequestMatchAllRegExpAsRange = Request; diff --git a/src/Procedures/procMatchRegExp.test.ts b/src/Procedures/procMatchRegExp.test.ts index 294dc4a..749ef77 100644 --- a/src/Procedures/procMatchRegExp.test.ts +++ b/src/Procedures/procMatchRegExp.test.ts @@ -25,7 +25,7 @@ describe('procMatchRegExp', () => { }); test('RequestExecRegExp bad regex', () => { - const req: Request = createRequestMatchRegExp({ text: 'two words', regexp: '/[/g' }); + const req: Request = createRequestMatchRegExp({ text: 'two words', regexp: { source: '[', flags: 'g' } }); const result = procMatchRegExp(req); const response = isErrorResponse(result) ? result : undefined; expect(isMatchRegExpResponse(result)).toBe(false); diff --git a/src/Procedures/procMatchRegExp.ts b/src/Procedures/procMatchRegExp.ts index 31d602e..2072465 100644 --- a/src/Procedures/procMatchRegExp.ts +++ b/src/Procedures/procMatchRegExp.ts @@ -1,5 +1,6 @@ import type { MatchRegExpResult } from '../helpers/evaluateRegExp.js'; import { matchRegExp, toRegExp } from '../helpers/evaluateRegExp.js'; +import type { RegExpLike } from '../helpers/regexp.js'; import { format } from '../util/format.js'; import type { ErrorResponse, Request, Response } from './procedure.js'; import { createErrorResponse, createRequest, createResponse, isRequestType, isResponseType } from './procedure.js'; @@ -10,7 +11,7 @@ export type MatchRegExpResponseType = MatchRegExpRequestType; export interface RequestMatchRegExpData { text: string; - regexp: RegExp | string; + regexp: RegExpLike; } export type RequestMatchRegExp = Request; diff --git a/src/RegExpWorker.test.ts b/src/RegExpWorker.test.ts index 372150f..3129d71 100644 --- a/src/RegExpWorker.test.ts +++ b/src/RegExpWorker.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { describe, expect, test } from 'vitest'; @@ -34,14 +35,18 @@ expect.extend({ // cspell:ignore hellothere describe('RegExpWorker', () => { - test( - 'matchAll', - run(async (w) => { - const r = await w.matchAll('hello\nthere', /\w/g); - expect(r.matches.map((m) => m[0])).toEqual('hellothere'.split('')); - expect(r.elapsedTimeMs).toBeGreaterThan(0); - }), - ); + test.each` + text | regexp | expected + ${'hello\nthere'} | ${/\w/g} | ${'hellothere'.split('')} + ${'Good Morning'} | ${/\b\w+/g} | ${['Good', 'Morning']} + ${'Good Morning.'} | ${/Good/} | ${['Good']} + ${'Good Morning.'} | ${/\b\w+/g} | ${['Good', 'Morning']} + `('matchAll $test $regexp', async ({ text, regexp, expected }) => { + await using worker = cr(); + const r = await worker.matchAll(text, regexp); + expect(r.matches.map((m) => m[0])).toEqual(expected); + expect(r.elapsedTimeMs).toBeGreaterThan(0); + }); test( 'set timeout', diff --git a/src/RegExpWorker.ts b/src/RegExpWorker.ts index 48ed1f3..3ee9e9a 100644 --- a/src/RegExpWorker.ts +++ b/src/RegExpWorker.ts @@ -4,18 +4,21 @@ import type { MatchAllToRangesRegExpResult, MatchRegExpResult, } from './helpers/evaluateRegExp.js'; -import { type MatchAllRegExpResult } from './helpers/evaluateRegExp.js'; +import type { MatchAllRegExpResult } from './helpers/evaluateRegExp.js'; +import { normalizeRegExp, type RegExpLike } from './helpers/regexp.js'; import type { RequestMatchRegExp } from './Procedures/index.js'; +import type { + RequestExecRegExp, + RequestMatchAllRegExp, + RequestMatchAllRegExpArray, + RequestMatchAllRegExpAsRange, + Response, +} from './Procedures/index.js'; import { createRequestExecRegExp, createRequestMatchAllRegExp, createRequestMatchRegExp, createRequestMatchRegExpArray, - type RequestExecRegExp, - type RequestMatchAllRegExp, - type RequestMatchAllRegExpArray, - type RequestMatchAllRegExpAsRange, - type Response, } from './Procedures/index.js'; import { createRequestMatchAllRegExpAsRange } from './Procedures/procMatchAllRegExpAsRange.js'; import { Scheduler } from './scheduler/index.js'; @@ -24,6 +27,18 @@ import type { CreateWorker } from './worker/di.js'; export type { ExecRegExpResult, MatchAllRegExpArrayResult, MatchAllRegExpResult, MatchRegExpResult } from './helpers/evaluateRegExp.js'; +if (typeof Symbol.dispose === 'undefined') { + // Polyfill for Symbol.dispose if not available + // This is necessary for environments that do not support the dispose protocol natively + (Symbol as unknown as { dispose: symbol }).dispose = Symbol.for('dispose'); +} + +if (typeof Symbol.asyncDispose === 'undefined') { + // Polyfill for Symbol.dispose if not available + // This is necessary for environments that do not support the dispose protocol natively + (Symbol as unknown as { asyncDispose: symbol }).asyncDispose = Symbol.for('asyncDispose'); +} + export class RegExpWorkerBase { private scheduler: Scheduler; public dispose: () => Promise = () => this._dispose(); @@ -44,7 +59,8 @@ export class RegExpWorkerBase { * @param text - The text to search within. * @param timeLimitMs - Optional time limit in milliseconds for the operation. */ - public exec(regExp: RegExp, text: string, timeLimitMs?: number): Promise { + public exec(regExp: RegExpLike, text: string, timeLimitMs?: number): Promise { + regExp = normalizeRegExp(regExp); const req = createRequestExecRegExp({ regexp: regExp, text }); return this.makeRequest(req, timeLimitMs); } @@ -55,7 +71,8 @@ export class RegExpWorkerBase { * @param regExp - The regular expression to match against the text. * @param timeLimitMs - Optional time limit in milliseconds for the operation. */ - public matchAll(text: string, regExp: RegExp, timeLimitMs?: number): Promise { + public matchAll(text: string, regExp: RegExpLike, timeLimitMs?: number): Promise { + regExp = normalizeRegExp(regExp); const req = createRequestMatchAllRegExp({ regexp: regExp, text }); return this.makeRequest(req, timeLimitMs); } @@ -66,7 +83,8 @@ export class RegExpWorkerBase { * @param regExp - The regular expression to match against the text. * @param timeLimitMs - Optional time limit in milliseconds for the operation. */ - public match(text: string, regExp: RegExp, timeLimitMs?: number): Promise { + public match(text: string, regExp: RegExpLike, timeLimitMs?: number): Promise { + regExp = normalizeRegExp(regExp); const req = createRequestMatchRegExp({ regexp: regExp, text }); return this.makeRequest(req, timeLimitMs); } @@ -77,7 +95,8 @@ export class RegExpWorkerBase { * @param regExps - An array of regular expressions to match against the text. * @param timeLimitMs - Optional time limit in milliseconds for the operation. */ - public matchAllArray(text: string, regExp: RegExp[], timeLimitMs?: number): Promise { + public matchAllArray(text: string, regExp: RegExpLike[], timeLimitMs?: number): Promise { + regExp = regExp.map(normalizeRegExp); const req = createRequestMatchRegExpArray({ regexps: regExp, text }); return this.makeRequest(req, timeLimitMs); } @@ -85,11 +104,12 @@ export class RegExpWorkerBase { /** * Runs text.matchAll against an array of RegExps in a worker. * @param text - The text to search within. - * @param regExps - An array of regular expressions to match against the text. + * @param regExp - A regular expressions to match against the text. * @param timeLimitMs - Optional time limit in milliseconds for the operation. */ - public async matchAllAsRangePairs(text: string, regexp: RegExp, timeLimitMs?: number): Promise { - const req = createRequestMatchAllRegExpAsRange({ regexp, text }); + public async matchAllAsRangePairs(text: string, regExp: RegExpLike, timeLimitMs?: number): Promise { + regExp = normalizeRegExp(regExp); + const req = createRequestMatchAllRegExpAsRange({ regexp: regExp, text }); const result = await this.makeRequest(req, timeLimitMs); return { elapsedTimeMs: result.elapsedTimeMs, @@ -114,6 +134,14 @@ export class RegExpWorkerBase { return this.scheduler.scheduleRequest(req, timeLimitMs).then(extractResult, timeoutRejection) as Promise; } + [Symbol.dispose](): void { + this._dispose().catch(() => {}); + } + + [Symbol.asyncDispose](): Promise { + return this._dispose().catch(() => {}); + } + /** * Shuts down the background Worker and rejects any pending scheduled items. */ @@ -176,3 +204,90 @@ function mapToRanges(flatRange: Uint32Array): RangePair[] { export function createRegExpWorker(createWorker: CreateWorker, timeoutMs?: number, stopIdleWorkerAfterMs?: number): RegExpWorkerBase { return new RegExpWorkerBase(createWorker, timeoutMs, stopIdleWorkerAfterMs); } + +export async function createAndApplyToWorker( + createRegExpWorker: CreateRegExpWorker, + fn: (worker: RegExpWorkerBase) => Promise, + timeoutMs?: number, + stopIdleWorkerAfterMs?: number, +): Promise { + await using worker = createRegExpWorker(timeoutMs, stopIdleWorkerAfterMs); + return await fn(worker); +} + +export type CreateRegExpWorker = (timeoutMs?: number, stopIdleWorkerAfterMs?: number) => T; + +/** + * Run text.matchAll against a RegExp in a worker. + * @param text - The text to search within. + * @param regExp - The regular expression to match against the text. + * @param timeLimitMs - Optional time limit in milliseconds for the operation. + */ +export function crWorkerMatchAll( + createRegExpWorker: CreateRegExpWorker, + text: string, + regExp: RegExp, + timeLimitMs?: number, +): Promise { + return createAndApplyToWorker(createRegExpWorker, (worker) => worker.matchAll(text, regExp, timeLimitMs)); +} + +/** + * Run text.matchAll against a RegExp in a worker and return the matches as [start, end] range pairs. + * @param text - The text to search within. + * @param regExp - The regular expression to match against the text. + * @param timeLimitMs - Optional time limit in milliseconds for the operation. + */ +export function crWorkerMatchAllAsRangePairs( + createRegExpWorker: CreateRegExpWorker, + text: string, + regExp: RegExp, + timeLimitMs?: number, +): Promise { + return createAndApplyToWorker(createRegExpWorker, (worker) => worker.matchAllAsRangePairs(text, regExp, timeLimitMs)); +} + +/** + * Runs text.matchAll against an array of RegExps in a worker. + * @param text - The text to search within. + * @param regExps - An array of regular expressions to match against the text. + * @param timeLimitMs - Optional time limit in milliseconds for the operation. + */ +export function crWorkerMatchAllArray( + createRegExpWorker: CreateRegExpWorker, + text: string, + regExps: RegExp[], + timeLimitMs?: number, +): Promise { + return createAndApplyToWorker(createRegExpWorker, (worker) => worker.matchAllArray(text, regExps, timeLimitMs)); +} + +/** + * Run RegExp.exec in a worker. + * @param regExp - The regular expression to execute. + * @param text - The text to search within. + * @param timeLimitMs - Optional time limit in milliseconds for the operation. + */ +export function crWorkerExec( + createRegExpWorker: CreateRegExpWorker, + regExp: RegExp, + text: string, + timeLimitMs?: number, +): Promise { + return createAndApplyToWorker(createRegExpWorker, (worker) => worker.exec(regExp, text, timeLimitMs)); +} + +/** + * Run text.match with a RegExp in a worker. + * @param text - The text to search within. + * @param regExp - The regular expression to match against the text. + * @param timeLimitMs - Optional time limit in milliseconds for the operation. + */ +export function crWorkerMatch( + createRegExpWorker: CreateRegExpWorker, + text: string, + regExp: RegExp, + timeLimitMs?: number, +): Promise { + return createAndApplyToWorker(createRegExpWorker, (worker) => worker.match(text, regExp, timeLimitMs)); +} diff --git a/src/RegExpWorkerBrowser.ts b/src/RegExpWorkerBrowser.ts index 82e37d0..725bb73 100644 --- a/src/RegExpWorkerBrowser.ts +++ b/src/RegExpWorkerBrowser.ts @@ -5,10 +5,18 @@ import type { MatchAllRegExpResult, MatchRegExpResult, } from './RegExpWorker.js'; -import { RegExpWorkerBase } from './RegExpWorker.js'; +import { + crWorkerExec, + crWorkerMatch, + crWorkerMatchAll, + crWorkerMatchAllArray, + crWorkerMatchAllAsRangePairs, + RegExpWorkerBase, +} from './RegExpWorker.js'; import { createWorkerBrowser } from './worker/workerBrowser.js'; -export { toRegExp } from './helpers/evaluateRegExp.js'; +export type { RegExpLike } from './helpers/regexp.js'; +export { toRegExp } from './helpers/regexp.js'; export type { ExecRegExpResult, MatchAllAsRangePairsResult, @@ -39,9 +47,8 @@ export function createRegExpWorker(timeoutMs?: number, stopIdleWorkerAfterMs?: n * @param regExp - The regular expression to match against the text. * @param timeLimitMs - Optional time limit in milliseconds for the operation. */ -export async function workerMatchAll(text: string, regExp: RegExp, timeLimitMs?: number): Promise { - const worker = createRegExpWorker(); - return await worker.matchAll(text, regExp, timeLimitMs); +export function workerMatchAll(text: string, regExp: RegExp, timeLimitMs?: number): Promise { + return crWorkerMatchAll(createRegExpWorker, text, regExp, timeLimitMs); } /** @@ -50,9 +57,8 @@ export async function workerMatchAll(text: string, regExp: RegExp, timeLimitMs?: * @param regExp - The regular expression to match against the text. * @param timeLimitMs - Optional time limit in milliseconds for the operation. */ -export async function workerMatchAllAsRangePairs(text: string, regExp: RegExp, timeLimitMs?: number): Promise { - const worker = createRegExpWorker(); - return await worker.matchAllAsRangePairs(text, regExp, timeLimitMs); +export function workerMatchAllAsRangePairs(text: string, regExp: RegExp, timeLimitMs?: number): Promise { + return crWorkerMatchAllAsRangePairs(createRegExpWorker, text, regExp, timeLimitMs); } /** @@ -61,9 +67,8 @@ export async function workerMatchAllAsRangePairs(text: string, regExp: RegExp, t * @param regExps - An array of regular expressions to match against the text. * @param timeLimitMs - Optional time limit in milliseconds for the operation. */ -export async function workerMatchAllArray(text: string, regExps: RegExp[], timeLimitMs?: number): Promise { - const worker = createRegExpWorker(); - return await worker.matchAllArray(text, regExps, timeLimitMs); +export function workerMatchAllArray(text: string, regExps: RegExp[], timeLimitMs?: number): Promise { + return crWorkerMatchAllArray(createRegExpWorker, text, regExps, timeLimitMs); } /** @@ -72,9 +77,8 @@ export async function workerMatchAllArray(text: string, regExps: RegExp[], timeL * @param text - The text to search within. * @param timeLimitMs - Optional time limit in milliseconds for the operation. */ -export async function workerExec(regExp: RegExp, text: string, timeLimitMs?: number): Promise { - const worker = createRegExpWorker(); - return await worker.exec(regExp, text, timeLimitMs); +export function workerExec(regExp: RegExp, text: string, timeLimitMs?: number): Promise { + return crWorkerExec(createRegExpWorker, regExp, text, timeLimitMs); } /** @@ -83,7 +87,6 @@ export async function workerExec(regExp: RegExp, text: string, timeLimitMs?: num * @param regExp - The regular expression to match against the text. * @param timeLimitMs - Optional time limit in milliseconds for the operation. */ -export async function workerMatch(text: string, regExp: RegExp, timeLimitMs?: number): Promise { - const worker = createRegExpWorker(); - return await worker.match(text, regExp, timeLimitMs); +export function workerMatch(text: string, regExp: RegExp, timeLimitMs?: number): Promise { + return crWorkerMatch(createRegExpWorker, text, regExp, timeLimitMs); } diff --git a/src/RegExpWorkerNode.ts b/src/RegExpWorkerNode.ts index ba55027..0517584 100644 --- a/src/RegExpWorkerNode.ts +++ b/src/RegExpWorkerNode.ts @@ -5,10 +5,18 @@ import type { MatchAllRegExpResult, MatchRegExpResult, } from './RegExpWorker.js'; -import { RegExpWorkerBase as RegExpWorkerBase } from './RegExpWorker.js'; +import { + crWorkerExec, + crWorkerMatch, + crWorkerMatchAll, + crWorkerMatchAllArray, + crWorkerMatchAllAsRangePairs, + RegExpWorkerBase as RegExpWorkerBase, +} from './RegExpWorker.js'; import { createWorkerNode } from './worker/workerNode.js'; -export { toRegExp } from './helpers/evaluateRegExp.js'; +export type { RegExpLike } from './helpers/regexp.js'; +export { toRegExp } from './helpers/regexp.js'; export type { ExecRegExpResult, MatchAllAsRangePairsResult, @@ -39,9 +47,8 @@ export function createRegExpWorker(timeoutMs?: number, stopIdleWorkerAfterMs?: n * @param regExp - The regular expression to match against the text. * @param timeLimitMs - Optional time limit in milliseconds for the operation. */ -export async function workerMatchAll(text: string, regExp: RegExp, timeLimitMs?: number): Promise { - const worker = createRegExpWorker(); - return await worker.matchAll(text, regExp, timeLimitMs); +export function workerMatchAll(text: string, regExp: RegExp, timeLimitMs?: number): Promise { + return crWorkerMatchAll(createRegExpWorker, text, regExp, timeLimitMs); } /** @@ -50,9 +57,8 @@ export async function workerMatchAll(text: string, regExp: RegExp, timeLimitMs?: * @param regExp - The regular expression to match against the text. * @param timeLimitMs - Optional time limit in milliseconds for the operation. */ -export async function workerMatchAllAsRangePairs(text: string, regExp: RegExp, timeLimitMs?: number): Promise { - const worker = createRegExpWorker(); - return await worker.matchAllAsRangePairs(text, regExp, timeLimitMs); +export function workerMatchAllAsRangePairs(text: string, regExp: RegExp, timeLimitMs?: number): Promise { + return crWorkerMatchAllAsRangePairs(createRegExpWorker, text, regExp, timeLimitMs); } /** @@ -61,9 +67,8 @@ export async function workerMatchAllAsRangePairs(text: string, regExp: RegExp, t * @param regExps - An array of regular expressions to match against the text. * @param timeLimitMs - Optional time limit in milliseconds for the operation. */ -export async function workerMatchAllArray(text: string, regExps: RegExp[], timeLimitMs?: number): Promise { - const worker = createRegExpWorker(); - return await worker.matchAllArray(text, regExps, timeLimitMs); +export function workerMatchAllArray(text: string, regExps: RegExp[], timeLimitMs?: number): Promise { + return crWorkerMatchAllArray(createRegExpWorker, text, regExps, timeLimitMs); } /** @@ -72,9 +77,8 @@ export async function workerMatchAllArray(text: string, regExps: RegExp[], timeL * @param text - The text to search within. * @param timeLimitMs - Optional time limit in milliseconds for the operation. */ -export async function workerExec(regExp: RegExp, text: string, timeLimitMs?: number): Promise { - const worker = createRegExpWorker(); - return await worker.exec(regExp, text, timeLimitMs); +export function workerExec(regExp: RegExp, text: string, timeLimitMs?: number): Promise { + return crWorkerExec(createRegExpWorker, regExp, text, timeLimitMs); } /** @@ -83,7 +87,6 @@ export async function workerExec(regExp: RegExp, text: string, timeLimitMs?: num * @param regExp - The regular expression to match against the text. * @param timeLimitMs - Optional time limit in milliseconds for the operation. */ -export async function workerMatch(text: string, regExp: RegExp, timeLimitMs?: number): Promise { - const worker = createRegExpWorker(); - return await worker.match(text, regExp, timeLimitMs); +export function workerMatch(text: string, regExp: RegExp, timeLimitMs?: number): Promise { + return crWorkerMatch(createRegExpWorker, text, regExp, timeLimitMs); } diff --git a/src/browser.ts b/src/browser.ts index b0e34fd..31bd5b9 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -4,6 +4,7 @@ export type { MatchAllRegExpArrayResult, MatchAllRegExpResult, MatchRegExpResult, + RegExpLike, } from './RegExpWorkerBrowser.js'; export { createRegExpWorker, diff --git a/src/helpers/evaluateRegExp.test.ts b/src/helpers/evaluateRegExp.test.ts index 1aaa60c..23296a8 100644 --- a/src/helpers/evaluateRegExp.test.ts +++ b/src/helpers/evaluateRegExp.test.ts @@ -5,8 +5,6 @@ import type { FlatRanges, MatchAllRegExpResult } from './evaluateRegExp.js'; import { execRegExp, flatRangesToRanges, - isRegExp, - isRegExpLike, matchAllRegExp, matchAllRegExpArray, matchAllToRangesRegExp, @@ -48,10 +46,6 @@ const x2 = 'hello'; regExp | expected | expectedLastIndex ${/./g} | ${/./g} | ${0} ${/./gu} | ${/./gu} | ${0} - ${'/hello/'} | ${/hello/} | ${0} - ${'hello'} | ${/hello/} | ${0} - ${'hello.'} | ${/hello./} | ${0} - ${'hello*'} | ${/hello*/} | ${0} ${{ source: 'hello*', flags: 'gu' }} | ${/hello*/gu} | ${0} ${{ source: 'hello*', flags: 'gu', lastIndex: 10 }} | ${/hello*/gu} | ${10} ${{ source: 'hello*', flags: '' }} | ${/hello*/} | ${0} @@ -62,25 +56,14 @@ const x2 = 'hello'; }); test.each` - regExp | expected | expectedIsRegEx - ${/./g} | ${true} | ${true} - ${/./gu} | ${true} | ${true} - ${'hello'} | ${false} | ${false} - ${'hello.'} | ${false} | ${false} - ${'hello*'} | ${false} | ${false} - ${{ source: 'hello*', flags: 'gu' }} | ${true} | ${false} - ${{ source: 'hello*', flags: 'gu', lastIndex: 10 }} | ${true} | ${false} - `('isRegExLike $regExp', ({ regExp, expected, expectedIsRegEx }) => { - expect(isRegExpLike(regExp)).toBe(expected); - expect(isRegExp(regExp)).toBe(expectedIsRegEx); - }); - - test.each` - regExp | expected - ${{ source: undefined, flags: 'gu', lastIndex: 10 }} | ${TypeError('Invalid RegExp or string.')} - ${{ source: '', flags: 4, lastIndex: 10 }} | ${TypeError('Invalid RegExp or string.')} - ${null} | ${TypeError('Invalid RegExp or string.')} - ${'(.'} | ${SyntaxError('Invalid regular expression: /(./: Unterminated group')} + regExp | expected + ${{ source: undefined, flags: 'gu', lastIndex: 10 }} | ${TypeError('Invalid RegExp.')} + ${{ source: '', flags: 4, lastIndex: 10 }} | ${SyntaxError("Invalid flags supplied to RegExp constructor '4'")} + ${null} | ${expect.any(TypeError)} + ${'(.'} | ${TypeError('Invalid RegExp.')} + ${{ source: 'hello*', flags: 'gux', lastIndex: 10 }} | ${SyntaxError("Invalid flags supplied to RegExp constructor 'gux'")} + ${{ source: '\nh.* # Match anything starting with `h`\n', flags: 'gx' }} | ${SyntaxError("Invalid flags supplied to RegExp constructor 'gx'")} + ${{ source: '\nh.* # Use last index 10\n', flags: 'gx', lastIndex: 10 }} | ${SyntaxError("Invalid flags supplied to RegExp constructor 'gx'")} `('toRegExp Error $regExp', ({ regExp, expected }) => { expect(() => toRegExp(regExp)).toThrowError(expected); }); diff --git a/src/helpers/evaluateRegExp.ts b/src/helpers/evaluateRegExp.ts index b8450a2..510e0ac 100644 --- a/src/helpers/evaluateRegExp.ts +++ b/src/helpers/evaluateRegExp.ts @@ -1,4 +1,5 @@ import { measureExecution } from '../timer.js'; +import type { RegExpLike } from './regexp.js'; export interface MatchAllRegExpResult { elapsedTimeMs: number; @@ -68,50 +69,27 @@ export function matchAllRegExpArray(text: string, regExpArray: RegExp[]): MatchA return { elapsedTimeMs, results }; } -export type RegExpOrString = RegExp | string; - -export interface RegExpLike { - source: string; - flags: string; - lastIndex?: number; -} - /** - * - * @param r + * Converts a RegExp or RegExpLike object to a RegExp object. + * If the input is a RegExp, it is returned as is. + * If the input is a RegExpLike object, it should have `source` and `flags` properties. + * The `x` flag is NOT supported and will throw an error if present. + * @param regexp * @param defaultFlags * @returns */ -export function toRegExp(r: RegExp | RegExpLike | string, defaultFlags?: string): RegExp { - if (r instanceof RegExp) return r; - - if (typeof r === 'string') { - const match = r.match(/^\/(.*)\/([gimsuy]*)$/); - if (match) { - return new RegExp(match[1], match[2] || defaultFlags); - } - return new RegExp(r, defaultFlags); - } +export function toRegExp(regexp: RegExp | RegExpLike): RegExp { + if (regexp instanceof RegExp) return regexp; - if (isRegExpLike(r)) { - const reg = new RegExp(r.source, r.flags || defaultFlags); - if (r.lastIndex !== undefined) { - reg.lastIndex = r.lastIndex; - } - return reg; + const { source, flags } = regexp; + if (typeof source !== 'string') { + throw new TypeError('Invalid RegExp.'); } - throw new TypeError('Invalid RegExp or string.'); -} - -export function isRegExp(r: unknown): r is RegExp { - return r instanceof RegExp; -} - -export function isRegExpLike(r: unknown): r is RegExpLike { - if (r instanceof RegExp) return true; - if (r && typeof r === 'object' && 'source' in r && 'flags' in r && typeof r.source === 'string' && typeof r.flags === 'string') - return true; - return false; + const reg = new RegExp(source, flags); + if (regexp.lastIndex !== undefined) { + reg.lastIndex = regexp.lastIndex; + } + return reg; } function doExecRegExp(regExp: RegExp, text: string): RegExpExecArray | null { diff --git a/src/helpers/regexp.test.ts b/src/helpers/regexp.test.ts new file mode 100644 index 0000000..d942750 --- /dev/null +++ b/src/helpers/regexp.test.ts @@ -0,0 +1,50 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +import { describe, expect, test } from 'vitest'; + +import { isRegExp, isRegExpLike, toRegExp } from './regexp.js'; + +describe('EvaluateRegExp', () => { + test.each` + regExp | expected | expectedLastIndex + ${/./g} | ${/./g} | ${0} + ${/./gu} | ${/./gu} | ${0} + ${'/hello/'} | ${/hello/} | ${0} + ${'hello'} | ${/hello/} | ${0} + ${'hello.'} | ${/hello./} | ${0} + ${'hello*'} | ${/hello*/} | ${0} + ${{ source: 'hello*', flags: 'gu' }} | ${/hello*/gu} | ${0} + ${{ source: 'hello*', flags: 'gu', lastIndex: 10 }} | ${/hello*/gu} | ${10} + ${{ source: 'hello*', flags: 'gux', lastIndex: 10 }} | ${/hello*/gu} | ${10} + ${{ source: 'hello*', flags: '' }} | ${/hello*/} | ${0} + ${{ source: '\nh.* # Match anything starting with `h`\n', flags: 'gx' }} | ${/h.*/g} | ${0} + ${{ source: '\nh.* # Use last index 10\n', flags: 'gx', lastIndex: 10 }} | ${/h.*/g} | ${10} + `('toRegExp $regExp', ({ regExp, expected, expectedLastIndex }) => { + const reg = toRegExp(regExp); + expect(reg).toEqual(expected); + expect(reg.lastIndex).toBe(expectedLastIndex); + }); + + test.each` + regExp | expected | expectedIsRegEx + ${/./g} | ${true} | ${true} + ${/./gu} | ${true} | ${true} + ${'hello'} | ${false} | ${false} + ${'hello.'} | ${false} | ${false} + ${'hello*'} | ${false} | ${false} + ${{ source: 'hello*', flags: 'gu' }} | ${true} | ${false} + ${{ source: 'hello*', flags: 'gu', lastIndex: 10 }} | ${true} | ${false} + `('isRegExLike $regExp', ({ regExp, expected, expectedIsRegEx }) => { + expect(isRegExpLike(regExp)).toBe(expected); + expect(isRegExp(regExp)).toBe(expectedIsRegEx); + }); + + test.each` + regExp | expected + ${{ source: undefined, flags: 'gu', lastIndex: 10 }} | ${TypeError('Invalid RegExp or string.')} + ${{ source: '', flags: 4, lastIndex: 10 }} | ${TypeError('Invalid RegExp or string.')} + ${null} | ${TypeError('Invalid RegExp or string.')} + ${'(.'} | ${SyntaxError('Invalid regular expression: /(./: Unterminated group')} + `('toRegExp Error $regExp', ({ regExp, expected }) => { + expect(() => toRegExp(regExp)).toThrowError(expected); + }); +}); diff --git a/src/helpers/regexp.ts b/src/helpers/regexp.ts new file mode 100644 index 0000000..a7efb42 --- /dev/null +++ b/src/helpers/regexp.ts @@ -0,0 +1,82 @@ +export type RegExpOrString = RegExp | string; + +export interface RegExpLike { + source: string; + flags: string; + lastIndex?: number | undefined; +} + +export function isRegExp(r: unknown): r is RegExp { + return r instanceof RegExp; +} + +export function isRegExpLike(r: unknown): r is RegExpLike { + if (r instanceof RegExp) return true; + if (r && typeof r === 'object' && 'source' in r && 'flags' in r && typeof r.source === 'string' && typeof r.flags === 'string') + return true; + return false; +} + +/** + * Converts a RegExp, RegExpLike object, or string to a RegExp object. + * If the input is a string, it can be in the format `/pattern/flags`. + * If the input is a RegExpLike object, it should have `source` and `flags` properties. + * If the input is a RegExp, it is returned as is. + * The `x` flag is removed and the source is normalized to remove comments and whitespace. + * @param r + * @param defaultFlags + * @returns + */ +export function toRegExp(r: RegExp | RegExpLike | string, defaultFlags?: string): RegExp { + if (r instanceof RegExp) return r; + + r = typeof r === 'string' ? stringToRegExpLike(r, defaultFlags) : r; + + if (isRegExpLike(r)) { + const { source, flags } = normalizeRegExp(r); + const reg = new RegExp(source, flags); + if (r.lastIndex !== undefined) { + reg.lastIndex = r.lastIndex; + } + return reg; + } + throw new TypeError('Invalid RegExp or string.'); +} + +/** + * Removes the `x` flag from a regular expression if it exists. + * The `x` flag is used for extended mode, which allows whitespace and comments in the + * regular expression pattern. This function is useful to ensure that the regular expression + * can be safely used in contexts where the `x` flag is not supported or desired. + * @param regex + * @returns + */ +export function normalizeRegExp(regex: RegExpLike): RegExpLike { + if (regex instanceof RegExp) return regex; + let { source, flags } = regex; + if (!flags.includes('x')) { + return regex; + } + + flags = flags.replace('x', ''); + source = source.replace(/#.*$/gm, ''); // Remove comments + source = source.replace(/\s+/g, ''); // Remove whitespace + + const r: RegExpLike = { source, flags }; + if (regex.lastIndex !== undefined) { + r.lastIndex = regex.lastIndex; + } + + return r; +} + +// cspell:ignore dgimsuvyx +const regExpIsRegExpLike = /^\/(.*)\/([dgimsuvyx]*)$/; + +export function stringToRegExpLike(str: string, defaultFlags?: string): RegExpLike { + const match = str.match(regExpIsRegExpLike); + if (match) { + return { source: match[1], flags: match[2] || defaultFlags || '' }; + } + return { source: str, flags: '' }; +} diff --git a/src/index.mts b/src/index.mts index 2f646d3..1b921d6 100644 --- a/src/index.mts +++ b/src/index.mts @@ -4,6 +4,7 @@ export type { MatchAllRegExpArrayResult, MatchAllRegExpResult, MatchRegExpResult, + RegExpLike, } from './RegExpWorkerNode.js'; export { createRegExpWorker, diff --git a/src/worker/workerCodeBrowserDataURL.ts b/src/worker/workerCodeBrowserDataURL.ts index 3088d2d..d1381b6 100644 --- a/src/worker/workerCodeBrowserDataURL.ts +++ b/src/worker/workerCodeBrowserDataURL.ts @@ -27,192 +27,189 @@ YW0gcmVnRXhwQXJyYXkKKiBAcmV0dXJucwoqLwpmdW5jdGlvbiBtYXRjaEFsbFJlZ0V4cEFycmF5KHRl eHQsIHJlZ0V4cEFycmF5KSB7Cgljb25zdCB7IGVsYXBzZWRUaW1lTXMsIHI6IHJlc3VsdHMgfSA9IG1l\ YXN1cmVFeGVjdXRpb24oKCkgPT4gewoJCXJldHVybiByZWdFeHBBcnJheS5tYXAoKHIpID0+IG1hdGNo\ QWxsUmVnRXhwKHRleHQsIHIpKTsKCX0pOwoJcmV0dXJuIHsKCQllbGFwc2VkVGltZU1zLAoJCXJlc3Vs\ -dHMKCX07Cn0KLyoqCioKKiBAcGFyYW0gcgoqIEBwYXJhbSBkZWZhdWx0RmxhZ3MKKiBAcmV0dXJucwoq\ -LwpmdW5jdGlvbiB0b1JlZ0V4cChyLCBkZWZhdWx0RmxhZ3MpIHsKCWlmIChyIGluc3RhbmNlb2YgUmVn\ -RXhwKSByZXR1cm4gcjsKCWlmICh0eXBlb2YgciA9PT0gInN0cmluZyIpIHsKCQljb25zdCBtYXRjaCA9\ -IHIubWF0Y2goL15cLyguKilcLyhbZ2ltc3V5XSopJC8pOwoJCWlmIChtYXRjaCkgcmV0dXJuIG5ldyBS\ -ZWdFeHAobWF0Y2hbMV0sIG1hdGNoWzJdIHx8IGRlZmF1bHRGbGFncyk7CgkJcmV0dXJuIG5ldyBSZWdF\ -eHAociwgZGVmYXVsdEZsYWdzKTsKCX0KCWlmIChpc1JlZ0V4cExpa2UocikpIHsKCQljb25zdCByZWcg\ -PSBuZXcgUmVnRXhwKHIuc291cmNlLCByLmZsYWdzIHx8IGRlZmF1bHRGbGFncyk7CgkJaWYgKHIubGFz\ -dEluZGV4ICE9PSB2b2lkIDApIHJlZy5sYXN0SW5kZXggPSByLmxhc3RJbmRleDsKCQlyZXR1cm4gcmVn\ -OwoJfQoJdGhyb3cgbmV3IFR5cGVFcnJvcigiSW52YWxpZCBSZWdFeHAgb3Igc3RyaW5nLiIpOwp9CmZ1\ -bmN0aW9uIGlzUmVnRXhwTGlrZShyKSB7CglpZiAociBpbnN0YW5jZW9mIFJlZ0V4cCkgcmV0dXJuIHRy\ -dWU7CglpZiAociAmJiB0eXBlb2YgciA9PT0gIm9iamVjdCIgJiYgInNvdXJjZSIgaW4gciAmJiAiZmxh\ -Z3MiIGluIHIgJiYgdHlwZW9mIHIuc291cmNlID09PSAic3RyaW5nIiAmJiB0eXBlb2Ygci5mbGFncyA9\ -PT0gInN0cmluZyIpIHJldHVybiB0cnVlOwoJcmV0dXJuIGZhbHNlOwp9CmZ1bmN0aW9uIGRvRXhlY1Jl\ -Z0V4cChyZWdFeHAsIHRleHQpIHsKCXJldHVybiByZWdFeHAuZXhlYyh0ZXh0KTsKfQpmdW5jdGlvbiBk\ -b01hdGNoQWxsUmVnRXhwKHJlZ0V4cCwgdGV4dCkgewoJcmV0dXJuIHJlZ0V4cFtTeW1ib2wubWF0Y2hB\ -bGxdKHRleHQpOwp9CmZ1bmN0aW9uIGRvTWF0Y2hSZWdFeHAocmVnRXhwLCB0ZXh0KSB7CglyZXR1cm4g\ -dGV4dC5tYXRjaChyZWdFeHApOwp9CmZ1bmN0aW9uIHRvUmFuZ2VzKHIpIHsKCWNvbnN0IHJhbmdlcyA9\ -IG5ldyBVaW50MzJBcnJheShyLm1hdGNoZXMubGVuZ3RoICogMik7CglsZXQgaSA9IDA7Cglmb3IgKGNv\ -bnN0IG0gb2Ygci5tYXRjaGVzKSB7CgkJY29uc3QgaW5kZXggPSBtLmluZGV4IHx8IDA7CgkJcmFuZ2Vz\ -W2krK10gPSBpbmRleDsKCQlyYW5nZXNbaSsrXSA9IGluZGV4ICsgbVswXS5sZW5ndGg7Cgl9CglyZXR1\ -cm4gewoJCWVsYXBzZWRUaW1lTXM6IHIuZWxhcHNlZFRpbWVNcywKCQlyYW5nZXMKCX07Cn0KZnVuY3Rp\ -b24gbWF0Y2hBbGxUb1Jhbmdlc1JlZ0V4cCh0ZXh0LCByZWdFeHApIHsKCXJldHVybiB0b1Jhbmdlcyht\ -YXRjaEFsbFJlZ0V4cCh0ZXh0LCByZWdFeHApKTsKfQoKLy8jZW5kcmVnaW9uCi8vI3JlZ2lvbiBzcmMv\ -dXRpbC9mb3JtYXQudHMKZnVuY3Rpb24gZm9ybWF0KHZhbHVlKSB7Cgljb25zdCB2aXNpdGVkID0gLyog\ -QF9fUFVSRV9fICovIG5ldyBTZXQoKTsKCWZ1bmN0aW9uIF9mb3JtYXQodmFsdWUkMSkgewoJCWlmICh2\ -aXNpdGVkLmhhcyh2YWx1ZSQxKSkgcmV0dXJuICJbQ2lyY3VsYXJdIjsKCQl2aXNpdGVkLmFkZCh2YWx1\ -ZSQxKTsKCQlzd2l0Y2ggKHR5cGVvZiB2YWx1ZSQxKSB7CgkJCWNhc2UgImJpZ2ludCI6IHJldHVybiB2\ -YWx1ZSQxLnRvU3RyaW5nKCkgKyAibiI7CgkJCWNhc2UgInN5bWJvbCI6IHJldHVybiB2YWx1ZSQxLnRv\ -U3RyaW5nKCk7CgkJCWNhc2UgImZ1bmN0aW9uIjogcmV0dXJuIHZhbHVlJDEubmFtZSA/IGBbRnVuY3Rp\ -b246ICR7dmFsdWUkMS5uYW1lfV1gIDogIltGdW5jdGlvbl0iOwoJCQljYXNlICJ1bmRlZmluZWQiOiBy\ -ZXR1cm4gInVuZGVmaW5lZCI7CgkJCWNhc2UgIm9iamVjdCI6IHJldHVybiBvYmpUb1N0cmluZyh2YWx1\ -ZSQxKTsKCQkJY2FzZSAic3RyaW5nIjogcmV0dXJuIEpTT04uc3RyaW5naWZ5KHZhbHVlJDEpOwoJCX0K\ -CQlyZXR1cm4gU3RyaW5nKHZhbHVlJDEpOwoJfQoJZnVuY3Rpb24gb2JqVG9TdHJpbmcodmFsdWUkMSkg\ -ewoJCXRyeSB7CgkJCWlmICh2YWx1ZSQxID09PSBudWxsKSByZXR1cm4gIm51bGwiOwoJCQlpZiAodmFs\ -dWUkMSBpbnN0YW5jZW9mIEVycm9yKSByZXR1cm4gYEVycm9yOiAke3ZhbHVlJDEubWVzc2FnZX1cblN0\ -YWNrOiAke3ZhbHVlJDEuc3RhY2t9YDsKCQkJaWYgKEFycmF5LmlzQXJyYXkodmFsdWUkMSkpIHJldHVy\ -biBgWyR7dmFsdWUkMS5tYXAoX2Zvcm1hdCkuam9pbigiLCAiKX1dYDsKCQkJaWYgKCJ0b1N0cmluZyIg\ -aW4gdmFsdWUkMSAmJiB0eXBlb2YgdmFsdWUkMVsidG9TdHJpbmciXSA9PT0gImZ1bmN0aW9uIiAmJiBP\ -YmplY3QucHJvdG90eXBlLnRvU3RyaW5nICE9PSB2YWx1ZSQxWyJ0b1N0cmluZyJdKSByZXR1cm4gdmFs\ -dWUkMVsidG9TdHJpbmciXSgpOwoJCQljb25zdCBzID0gT2JqZWN0LmVudHJpZXModmFsdWUkMSkubWFw\ -KChba2V5LCB2YWxdKSA9PiBgJHtrZXl9OiAke19mb3JtYXQodmFsKX1gKS5qb2luKCIsICIpOwoJCQly\ -ZXR1cm4gYHske3MgPyBgICR7c30gYCA6ICIifX1gOwoJCX0gY2F0Y2ggKGUpIHsKCQkJcmV0dXJuIFN0\ -cmluZyhlKTsKCQl9Cgl9CglyZXR1cm4gX2Zvcm1hdCh2YWx1ZSk7Cn0KCi8vI2VuZHJlZ2lvbgovLyNy\ -ZWdpb24gc3JjL2hlbHBlcnMvZXJyb3JzLnRzCmNvbnN0IGFsbG93U3RyaW5nT3JVbmRlZmluZWQgPSB7\ -CglzdHJpbmc6IHRydWUsCgl1bmRlZmluZWQ6IHRydWUKfTsKZnVuY3Rpb24gaXNFcnJvckxpa2UoZSkg\ -ewoJaWYgKGUgaW5zdGFuY2VvZiBFcnJvcikgcmV0dXJuIHRydWU7CglpZiAoIWUgfHwgdHlwZW9mIGUg\ -IT09ICJvYmplY3QiKSByZXR1cm4gZmFsc2U7Cgljb25zdCBleCA9IGU7CglyZXR1cm4gdHlwZW9mIGV4\ -Lm5hbWUgPT0gInN0cmluZyIgJiYgdHlwZW9mIGV4Lm1lc3NhZ2UgPT0gInN0cmluZyIgJiYgdHlwZW9m\ -IGV4LnN0YWNrIGluIGFsbG93U3RyaW5nT3JVbmRlZmluZWQ7Cn0KZnVuY3Rpb24gdG9FcnJvcihlKSB7\ -CglpZiAoZSBpbnN0YW5jZW9mIEVycm9yKSByZXR1cm4gZTsKCWlmIChpc0Vycm9yTGlrZShlKSkgcmV0\ -dXJuIG5ldyBFcnJvcihlLm1lc3NhZ2UgfHwgIlVua25vd24gZXJyb3IiLCB7IGNhdXNlOiBlIH0pOwoJ\ -aWYgKHR5cGVvZiBlID09PSAic3RyaW5nIikgcmV0dXJuIG5ldyBFcnJvcihlKTsKCXJldHVybiBuZXcg\ -RXJyb3IoU3RyaW5nKGUpLCB7IGNhdXNlOiBlIH0pOwp9CgovLyNlbmRyZWdpb24KLy8jcmVnaW9uIHNy\ -Yy9Qcm9jZWR1cmVzL3VuaXF1ZUlkLnRzCmxldCBpZCA9IHR0KClbMF07CmZ1bmN0aW9uIHR0KCkgewoJ\ -Y29uc3QgbiA9IHBlcmZvcm1hbmNlLm5vdygpOwoJY29uc3QgcyA9IE1hdGguZmxvb3IobiAvIDFlMyk7\ -CglyZXR1cm4gW3MsIE1hdGguZmxvb3IoKG4gLSBzICogMWUzKSAqIDFlNildOwp9CmZ1bmN0aW9uIGlz\ -SWQodmFsdWUpIHsKCWlmICh0eXBlb2YgdmFsdWUgPT09ICJzdHJpbmciICYmIC9eSUQtXGQrLVxkKy1c\ -ZCstXGQrJC8udGVzdCh2YWx1ZSkpIHsKCQljb25zdCBbYSwgYiwgYywgZF0gPSB2YWx1ZS5zcGxpdCgi\ -LSIpLnNsaWNlKDEpLm1hcCgodikgPT4gTnVtYmVyLnBhcnNlSW50KHYsIDEwKSk7CgkJcmV0dXJuIGEg\ -PT09IGIgJSAxMyAmJiBkID09PSAoYiArIGMpICUgNzsKCX0KCXJldHVybiBmYWxzZTsKfQpjb25zdCBO\ -dWxsSUQgPSAiSUQtMC0wLTAtMCI7CgovLyNlbmRyZWdpb24KLy8jcmVnaW9uIHNyYy9Qcm9jZWR1cmVz\ -L3Byb2NlZHVyZS50cwpmdW5jdGlvbiBpc1JlcXVlc3RUeXBlKHYsIHJlcXVlc3RUeXBlKSB7CglyZXR1\ -cm4gaXNSZXF1ZXN0KHYpICYmIHYucmVxdWVzdFR5cGUgPT09IHJlcXVlc3RUeXBlOwp9CmZ1bmN0aW9u\ -IGlzUmVxdWVzdCh2KSB7CglpZiAoIXYgfHwgdHlwZW9mIHYgIT09ICJvYmplY3QiKSByZXR1cm4gZmFs\ -c2U7Cgljb25zdCByID0gdjsKCXJldHVybiB0eXBlb2Ygci5yZXF1ZXN0VHlwZSA9PT0gInN0cmluZyIg\ -JiYgaXNJZChyLmlkKTsKfQpmdW5jdGlvbiBjcmVhdGVSZXNwb25zZShpZCQxLCByZXNwb25zZVR5cGUs\ -IGRhdGEpIHsKCXJldHVybiB7CgkJaWQ6IGlkJDEsCgkJdGltZXN0YW1wOiBEYXRlLm5vdygpLAoJCXJl\ -c3BvbnNlVHlwZSwKCQlkYXRhCgl9Owp9CmNvbnN0IHJlc3BvbnNlVHlwZUVycm9yID0gIkVycm9yIjsK\ -ZnVuY3Rpb24gY3JlYXRlRXJyb3JSZXNwb25zZShyZXF1ZXN0LCBtZXNzYWdlLCBlcnJvcikgewoJaWYg\ -KCFpc1JlcXVlc3QocmVxdWVzdCkpIHsKCQljb25zdCByID0gcmVxdWVzdCAmJiB0eXBlb2YgcmVxdWVz\ -dCA9PT0gIm9iamVjdCIgPyByZXF1ZXN0IDoge307CgkJY29uc3QgaWQkMiA9IGlzSWQoci5pZCkgPyBy\ -LmlkIDogTnVsbElEOwoJCXJldHVybiBjcmVhdGVSZXNwb25zZShpZCQyLCByZXNwb25zZVR5cGVFcnJv\ -ciwgewoJCQlyZXF1ZXN0VHlwZTogdHlwZW9mIHIucmVxdWVzdFR5cGUgPT09ICJzdHJpbmciID8gci5y\ -ZXF1ZXN0VHlwZSA6IHZvaWQgMCwKCQkJbWVzc2FnZSwKCQkJZXJyb3I6IHRvRXJyb3IoZXJyb3IpCgkJ\ -fSk7Cgl9Cgljb25zdCB7IGlkOiBpZCQxLCByZXF1ZXN0VHlwZSB9ID0gcmVxdWVzdDsKCXJldHVybiBj\ -cmVhdGVSZXNwb25zZShpZCQxLCByZXNwb25zZVR5cGVFcnJvciwgewoJCXJlcXVlc3RUeXBlLAoJCW1l\ -c3NhZ2UsCgkJZXJyb3I6IHRvRXJyb3IoZXJyb3IpCgl9KTsKfQoKLy8jZW5kcmVnaW9uCi8vI3JlZ2lv\ -biBzcmMvUHJvY2VkdXJlcy9wcm9jRXhlY1JlZ0V4cC50cwpjb25zdCByZXF1ZXN0VHlwZUV4ZWNSZWdF\ -eHAgPSAiRXhlY1JlZ0V4cCI7CmZ1bmN0aW9uIGlzRXhlY1JlZ0V4cFJlcXVlc3QodikgewoJcmV0dXJu\ -IGlzUmVxdWVzdFR5cGUodiwgcmVxdWVzdFR5cGVFeGVjUmVnRXhwKTsKfQpmdW5jdGlvbiBwcm9jRXhl\ -Y1JlZ0V4cChyKSB7CglpZiAoIWlzRXhlY1JlZ0V4cFJlcXVlc3QocikpIHJldHVybiB2b2lkIDA7Cgl0\ -cnkgewoJCWNvbnN0IHJlZ2V4ID0gdG9SZWdFeHAoci5kYXRhLnJlZ2V4cCk7CgkJcmV0dXJuIGNyZWF0\ -ZVJlc3BvbnNlRXhlY1JlZ0V4cChyLCBleGVjUmVnRXhwKHJlZ2V4LCByLmRhdGEudGV4dCkpOwoJfSBj\ -YXRjaCAoZSkgewoJCXJldHVybiBjcmVhdGVFcnJvclJlc3BvbnNlKHIsIGZvcm1hdChlKSk7Cgl9Cn0K\ -ZnVuY3Rpb24gY3JlYXRlUmVzcG9uc2VFeGVjUmVnRXhwKHJlcXVlc3QsIGRhdGEpIHsKCXJldHVybiBj\ -cmVhdGVSZXNwb25zZShyZXF1ZXN0LmlkLCByZXF1ZXN0LnJlcXVlc3RUeXBlLCBkYXRhKTsKfQoKLy8j\ -ZW5kcmVnaW9uCi8vI3JlZ2lvbiBzcmMvUHJvY2VkdXJlcy9wcm9jTWF0Y2hBbGxSZWdFeHAudHMKY29u\ -c3QgcmVxdWVzdFR5cGVNYXRjaEFsbFJlZ0V4cCA9ICJNYXRjaEFsbFJlZ0V4cCI7CmZ1bmN0aW9uIGlz\ -TWF0Y2hBbGxSZWdFeHBSZXF1ZXN0KHYpIHsKCXJldHVybiBpc1JlcXVlc3RUeXBlKHYsIHJlcXVlc3RU\ -eXBlTWF0Y2hBbGxSZWdFeHApOwp9CmZ1bmN0aW9uIHByb2NNYXRjaEFsbFJlZ0V4cChyKSB7CglpZiAo\ -IWlzTWF0Y2hBbGxSZWdFeHBSZXF1ZXN0KHIpKSByZXR1cm4gdm9pZCAwOwoJdHJ5IHsKCQljb25zdCBy\ -ZWdleCA9IHRvUmVnRXhwKHIuZGF0YS5yZWdleHApOwoJCWNvbnN0IHJlZ2V4UmVzdWx0ID0gbWF0Y2hB\ -bGxSZWdFeHAoci5kYXRhLnRleHQsIHJlZ2V4KTsKCQlyZXR1cm4gY3JlYXRlUmVzcG9uc2VNYXRjaEFs\ -bFJlZ0V4cChyLCByZWdleFJlc3VsdCk7Cgl9IGNhdGNoIChlKSB7CgkJcmV0dXJuIGNyZWF0ZUVycm9y\ -UmVzcG9uc2UociwgZm9ybWF0KGUpKTsKCX0KfQpmdW5jdGlvbiBjcmVhdGVSZXNwb25zZU1hdGNoQWxs\ -UmVnRXhwKHJlcXVlc3QsIGRhdGEpIHsKCXJldHVybiBjcmVhdGVSZXNwb25zZShyZXF1ZXN0LmlkLCBy\ -ZXF1ZXN0LnJlcXVlc3RUeXBlLCBkYXRhKTsKfQoKLy8jZW5kcmVnaW9uCi8vI3JlZ2lvbiBzcmMvUHJv\ -Y2VkdXJlcy9wcm9jTWF0Y2hBbGxSZWdFeHBBcnJheS50cwpjb25zdCByZXF1ZXN0VHlwZU1hdGNoUmVn\ -RXhwQXJyYXkgPSAiTWF0Y2hBbGxSZWdFeHBBcnJheSI7CmZ1bmN0aW9uIGlzTWF0Y2hSZWdFeHBBcnJh\ -eVJlcXVlc3QodikgewoJcmV0dXJuIGlzUmVxdWVzdFR5cGUodiwgcmVxdWVzdFR5cGVNYXRjaFJlZ0V4\ -cEFycmF5KTsKfQpmdW5jdGlvbiBwcm9jTWF0Y2hBbGxSZWdFeHBBcnJheShyKSB7CglpZiAoIWlzTWF0\ -Y2hSZWdFeHBBcnJheVJlcXVlc3QocikpIHJldHVybiB2b2lkIDA7Cgl0cnkgewoJCWNvbnN0IHJlZ2V4\ -ID0gci5kYXRhLnJlZ2V4cHMubWFwKChyJDEpID0+IHRvUmVnRXhwKHIkMSkpOwoJCWNvbnN0IHJlZ2V4\ -UmVzdWx0ID0gbWF0Y2hBbGxSZWdFeHBBcnJheShyLmRhdGEudGV4dCwgcmVnZXgpOwoJCXJldHVybiBj\ -cmVhdGVSZXNwb25zZU1hdGNoUmVnRXhwQXJyYXkociwgcmVnZXhSZXN1bHQpOwoJfSBjYXRjaCAoZSkg\ -ewoJCXJldHVybiBjcmVhdGVFcnJvclJlc3BvbnNlKHIsIGZvcm1hdChlKSk7Cgl9Cn0KZnVuY3Rpb24g\ -Y3JlYXRlUmVzcG9uc2VNYXRjaFJlZ0V4cEFycmF5KHJlcXVlc3QsIGRhdGEpIHsKCXJldHVybiBjcmVh\ -dGVSZXNwb25zZShyZXF1ZXN0LmlkLCByZXF1ZXN0LnJlcXVlc3RUeXBlLCBkYXRhKTsKfQoKLy8jZW5k\ -cmVnaW9uCi8vI3JlZ2lvbiBzcmMvUHJvY2VkdXJlcy9wcm9jTWF0Y2hBbGxSZWdFeHBBc1JhbmdlLnRz\ -CmNvbnN0IHJlcXVlc3RUeXBlTWF0Y2hBbGxSZWdFeHBBc1JhbmdlID0gIk1hdGNoQWxsUmVnRXhwQXNS\ -YW5nZSI7CmZ1bmN0aW9uIGlzTWF0Y2hBbGxSZWdFeHBBc1JhbmdlUmVxdWVzdCh2KSB7CglyZXR1cm4g\ -aXNSZXF1ZXN0VHlwZSh2LCByZXF1ZXN0VHlwZU1hdGNoQWxsUmVnRXhwQXNSYW5nZSk7Cn0KZnVuY3Rp\ -b24gcHJvY01hdGNoQWxsUmVnRXhwQXNSYW5nZShyKSB7CglpZiAoIWlzTWF0Y2hBbGxSZWdFeHBBc1Jh\ -bmdlUmVxdWVzdChyKSkgcmV0dXJuIHZvaWQgMDsKCXRyeSB7CgkJY29uc3QgcmVnZXggPSB0b1JlZ0V4\ -cChyLmRhdGEucmVnZXhwKTsKCQljb25zdCByZWdleFJlc3VsdCA9IG1hdGNoQWxsVG9SYW5nZXNSZWdF\ -eHAoci5kYXRhLnRleHQsIHJlZ2V4KTsKCQlyZXR1cm4gY3JlYXRlUmVzcG9uc2VNYXRjaEFsbFJlZ0V4\ -cEFzUmFuZ2UociwgcmVnZXhSZXN1bHQpOwoJfSBjYXRjaCAoZSkgewoJCXJldHVybiBjcmVhdGVFcnJv\ -clJlc3BvbnNlKHIsIGZvcm1hdChlKSk7Cgl9Cn0KZnVuY3Rpb24gY3JlYXRlUmVzcG9uc2VNYXRjaEFs\ -bFJlZ0V4cEFzUmFuZ2UocmVxdWVzdCwgZGF0YSkgewoJcmV0dXJuIGNyZWF0ZVJlc3BvbnNlKHJlcXVl\ -c3QuaWQsIHJlcXVlc3QucmVxdWVzdFR5cGUsIGRhdGEpOwp9CgovLyNlbmRyZWdpb24KLy8jcmVnaW9u\ -IHNyYy9Qcm9jZWR1cmVzL3Byb2NNYXRjaFJlZ0V4cC50cwpjb25zdCByZXF1ZXN0VHlwZU1hdGNoUmVn\ -RXhwID0gIk1hdGNoUmVnRXhwIjsKZnVuY3Rpb24gaXNNYXRjaFJlZ0V4cFJlcXVlc3QodikgewoJcmV0\ -dXJuIGlzUmVxdWVzdFR5cGUodiwgcmVxdWVzdFR5cGVNYXRjaFJlZ0V4cCk7Cn0KZnVuY3Rpb24gcHJv\ -Y01hdGNoUmVnRXhwKHIpIHsKCWlmICghaXNNYXRjaFJlZ0V4cFJlcXVlc3QocikpIHJldHVybiB2b2lk\ -IDA7Cgl0cnkgewoJCWNvbnN0IHJlZ2V4ID0gdG9SZWdFeHAoci5kYXRhLnJlZ2V4cCk7CgkJY29uc3Qg\ -cmVnZXhSZXN1bHQgPSBtYXRjaFJlZ0V4cChyLmRhdGEudGV4dCwgcmVnZXgpOwoJCXJldHVybiBjcmVh\ -dGVSZXNwb25zZU1hdGNoUmVnRXhwKHIsIHJlZ2V4UmVzdWx0KTsKCX0gY2F0Y2ggKGUpIHsKCQlyZXR1\ -cm4gY3JlYXRlRXJyb3JSZXNwb25zZShyLCBmb3JtYXQoZSkpOwoJfQp9CmZ1bmN0aW9uIGNyZWF0ZVJl\ -c3BvbnNlTWF0Y2hSZWdFeHAocmVxdWVzdCwgZGF0YSkgewoJcmV0dXJuIGNyZWF0ZVJlc3BvbnNlKHJl\ -cXVlc3QuaWQsIHJlcXVlc3QucmVxdWVzdFR5cGUsIGRhdGEpOwp9CgovLyNlbmRyZWdpb24KLy8jcmVn\ -aW9uIHNyYy9Qcm9jZWR1cmVzL3Byb2NlZHVyZXMudHMKLyoqCiogQ29sbGVjdGlvbiBvZiBhbGwgcHJv\ -Y2VkdXJlcyByZWxhdGVkIHRvIHJlZ3VsYXIgZXhwcmVzc2lvbnMuCiogT3JkZXIgaXMgaW1wb3J0YW50\ -IGZvciB0aGUgd29ya2VyLCBhcyBpdCBwcm9jZXNzZXMgdGhlbSBzZXF1ZW50aWFsbHkuCiovCmNvbnN0\ -IHByb2NlZHVyZXMgPSBbCglwcm9jTWF0Y2hBbGxSZWdFeHAsCglwcm9jTWF0Y2hBbGxSZWdFeHBBc1Jh\ -bmdlLAoJcHJvY0V4ZWNSZWdFeHAsCglwcm9jTWF0Y2hBbGxSZWdFeHBBcnJheSwKCXByb2NNYXRjaFJl\ -Z0V4cApdOwoKLy8jZW5kcmVnaW9uCi8vI3JlZ2lvbiBzcmMvd29ya2VyL0xvZ0xldmVsLnRzCmxldCBM\ -b2dMZXZlbCA9IC8qIEBfX1BVUkVfXyAqLyBmdW5jdGlvbihMb2dMZXZlbCQxKSB7CglMb2dMZXZlbCQx\ -W0xvZ0xldmVsJDFbIkxvZ0xldmVsTm9uZSJdID0gMF0gPSAiTG9nTGV2ZWxOb25lIjsKCUxvZ0xldmVs\ -JDFbTG9nTGV2ZWwkMVsiTG9nTGV2ZWxFcnJvciJdID0gMV0gPSAiTG9nTGV2ZWxFcnJvciI7CglMb2dM\ -ZXZlbCQxW0xvZ0xldmVsJDFbIkxvZ0xldmVsV2FybiJdID0gMl0gPSAiTG9nTGV2ZWxXYXJuIjsKCUxv\ -Z0xldmVsJDFbTG9nTGV2ZWwkMVsiTG9nTGV2ZWxJbmZvIl0gPSAzXSA9ICJMb2dMZXZlbEluZm8iOwoJ\ -TG9nTGV2ZWwkMVtMb2dMZXZlbCQxWyJMb2dMZXZlbERlYnVnIl0gPSA0XSA9ICJMb2dMZXZlbERlYnVn\ -IjsKCXJldHVybiBMb2dMZXZlbCQxOwp9KHt9KTsKCi8vI2VuZHJlZ2lvbgovLyNyZWdpb24gc3JjL3dv\ -cmtlci9NZXNzYWdlUG9ydC50cwpjb25zdCBudWxsUG9ydCA9IE9iamVjdC5mcmVlemUoewoJcG9zdE1l\ -c3NhZ2UoKSB7fSwKCW9uKCkgewoJCXJldHVybiBudWxsUG9ydDsKCX0sCglvZmYoKSB7CgkJcmV0dXJu\ -IG51bGxQb3J0OwoJfSwKCW9uY2UoKSB7CgkJcmV0dXJuIG51bGxQb3J0OwoJfQp9KTsKCi8vI2VuZHJl\ -Z2lvbgovLyNyZWdpb24gc3JjL3dvcmtlci9Xb3JrZXJNZXNzYWdlSGFuZGxlci50cwp2YXIgV29ya2Vy\ -TWVzc2FnZUhhbmRsZXIgPSBjbGFzcyB7Cglsb2dMZXZlbCA9IExvZ0xldmVsLkxvZ0xldmVsRXJyb3I7\ -CglsaXN0ZW5lcjsKCWNvbnN0cnVjdG9yKHBvcnQsIHByb2NlZHVyZXMkMSkgewoJCXRoaXMucG9ydCA9\ -IHBvcnQ7CgkJdGhpcy5wcm9jZWR1cmVzID0gcHJvY2VkdXJlcyQxOwoJCXRoaXMubGlzdGVuZXIgPSAo\ -dikgPT4gdGhpcy5saXN0ZW5lck1lc3NhZ2Uodik7CgkJcG9ydC5vbigibWVzc2FnZSIsIHRoaXMubGlz\ -dGVuZXIpOwoJfQoJZGlzcG9zZSgpIHsKCQl0aGlzLnBvcnQub2ZmKCJtZXNzYWdlIiwgdGhpcy5saXN0\ -ZW5lcik7CgkJdGhpcy5wb3J0ID0gbnVsbFBvcnQ7Cgl9Cglwb3N0KG1zZykgewoJCXRoaXMubG9nKExv\ -Z0xldmVsLkxvZ0xldmVsRGVidWcsICJQb3N0OiAiICsgSlNPTi5zdHJpbmdpZnkobXNnKSk7CgkJdGhp\ -cy5wb3J0LnBvc3RNZXNzYWdlKG1zZyk7Cgl9Cglsb2cobGV2ZWwsIC4uLnBhcmFtcykgewoJCWlmIChs\ -ZXZlbCA+IHRoaXMubG9nTGV2ZWwpIHJldHVybjsKCQlzd2l0Y2ggKGxldmVsKSB7CgkJCWNhc2UgTG9n\ -TGV2ZWwuTG9nTGV2ZWxFcnJvcjoKCQkJCWNvbnNvbGUuZXJyb3IoLi4ucGFyYW1zKTsKCQkJCWJyZWFr\ -OwoJCQljYXNlIExvZ0xldmVsLkxvZ0xldmVsV2FybjoKCQkJCWNvbnNvbGUud2FybiguLi5wYXJhbXMp\ -OwoJCQkJYnJlYWs7CgkJCWRlZmF1bHQ6IGNvbnNvbGUubG9nKC4uLnBhcmFtcyk7CgkJfQoJfQoJbGlz\ -dGVuZXJNZXNzYWdlKHZhbHVlKSB7CgkJdGhpcy5sb2coTG9nTGV2ZWwuTG9nTGV2ZWxEZWJ1ZywgYG1l\ -c3NhZ2U6ICR7SlNPTi5zdHJpbmdpZnkodmFsdWUpfWApOwoJCWlmICghaXNSZXF1ZXN0KHZhbHVlKSkg\ -ewoJCQljb25zdCBtc2cgPSBgQmFkbHkgZm9ybWVkIFJlcXVlc3Q6ICR7SlNPTi5zdHJpbmdpZnkodmFs\ -dWUpfWA7CgkJCXRoaXMubG9nKExvZ0xldmVsLkxvZ0xldmVsRXJyb3IsIG1zZyk7CgkJCXRoaXMucG9z\ -dChjcmVhdGVFcnJvclJlc3BvbnNlKHZhbHVlLCBtc2cpKTsKCQkJcmV0dXJuOwoJCX0KCQljb25zdCBy\ -ZXF1ZXN0ID0gdmFsdWU7CgkJZm9yIChjb25zdCBwcm9jIG9mIHRoaXMucHJvY2VkdXJlcykgdHJ5IHsK\ -CQkJY29uc3QgcmVzcG9uc2UgPSBwcm9jKHJlcXVlc3QpOwoJCQlpZiAocmVzcG9uc2UgIT09IHZvaWQg\ -MCkgewoJCQkJUHJvbWlzZS5yZXNvbHZlKHJlc3BvbnNlKS5jYXRjaCgocmVhc29uKSA9PiBjcmVhdGVF\ -cnJvclJlc3BvbnNlKHJlcXVlc3QsIFN0cmluZyhyZWFzb24pLCB0b0Vycm9yKHJlYXNvbikpKS50aGVu\ -KChyKSA9PiB0aGlzLnBvc3QocikpLmNhdGNoKCgpID0+IHt9KTsKCQkJCXJldHVybjsKCQkJfQoJCX0g\ -Y2F0Y2ggKGUpIHsKCQkJY29uc3QgbXNnID0gaXNFcnJvckxpa2UoZSkgPyBlLm1lc3NhZ2UgOiBmb3Jt\ -YXQoZSk7CgkJCXRoaXMucG9zdChjcmVhdGVFcnJvclJlc3BvbnNlKHJlcXVlc3QsIG1zZywgaXNFcnJv\ -ckxpa2UoZSkgPyBlIDogdm9pZCAwKSk7CgkJCXJldHVybjsKCQl9CgkJdGhpcy5sb2coTG9nTGV2ZWwu\ -TG9nTGV2ZWxXYXJuLCBgVW5oYW5kbGVkIFJlcXVlc3QgIiR7dmFsdWUucmVxdWVzdFR5cGV9ImApOwoJ\ -CXRoaXMucG9zdChjcmVhdGVFcnJvclJlc3BvbnNlKHJlcXVlc3QsICJVbmhhbmRsZWQgUmVxdWVzdCIp\ -KTsKCX0KfTsKZnVuY3Rpb24gY3JlYXRlSGFuZGxlcihwb3J0LCBwcm9jZWR1cmVzJDEpIHsKCXJldHVy\ -biBuZXcgV29ya2VyTWVzc2FnZUhhbmRsZXIocG9ydCwgcHJvY2VkdXJlcyQxKTsKfQoKLy8jZW5kcmVn\ -aW9uCi8vI3JlZ2lvbiBzcmMvd29ya2VyL3dvcmtlckNvZGVCcm93c2VyLnRzCnZhciBCcm93c2VyTWVz\ -c2FnZVBvcnQgPSBjbGFzcyB7CgkjbWVzc2FnZUxpc3RlbmVycyA9IC8qIEBfX1BVUkVfXyAqLyBuZXcg\ -U2V0KCk7Cgljb25zdHJ1Y3RvcigpIHsKCQlnbG9iYWxUaGlzLmFkZEV2ZW50TGlzdGVuZXIoIm1lc3Nh\ -Z2UiLCB0aGlzLiNsaXN0ZW5lck1lc3NhZ2UpOwoJfQoJcG9zdE1lc3NhZ2UodmFsdWUpIHsKCQlnbG9i\ -YWxUaGlzLnBvc3RNZXNzYWdlKHZhbHVlKTsKCX0KCW9uKGV2ZW50LCBsaXN0ZW5lcikgewoJCWlmIChl\ -dmVudCA9PT0gIm1lc3NhZ2UiKSB0aGlzLiNtZXNzYWdlTGlzdGVuZXJzLmFkZChsaXN0ZW5lcik7CgkJ\ -cmV0dXJuIHRoaXM7Cgl9CglvZmYoZXZlbnQsIGxpc3RlbmVyKSB7CgkJaWYgKGV2ZW50ID09PSAibWVz\ -c2FnZSIpIHRoaXMuI21lc3NhZ2VMaXN0ZW5lcnMuZGVsZXRlKGxpc3RlbmVyKTsKCQlyZXR1cm4gdGhp\ -czsKCX0KCSNsaXN0ZW5lck1lc3NhZ2UgPSAoZXZlbnQpID0+IHsKCQlmb3IgKGNvbnN0IGxpc3RlbmVy\ -IG9mIHRoaXMuI21lc3NhZ2VMaXN0ZW5lcnMpIGxpc3RlbmVyKGV2ZW50LmRhdGEpOwoJfTsKfTsKY3Jl\ -YXRlSGFuZGxlcihuZXcgQnJvd3Nlck1lc3NhZ2VQb3J0KCksIHByb2NlZHVyZXMpOwoKLy8jZW5kcmVn\ -aW9u\ +dHMKCX07Cn0KLyoqCiogQ29udmVydHMgYSBSZWdFeHAgb3IgUmVnRXhwTGlrZSBvYmplY3QgdG8gYSBS\ +ZWdFeHAgb2JqZWN0LgoqIElmIHRoZSBpbnB1dCBpcyBhIFJlZ0V4cCwgaXQgaXMgcmV0dXJuZWQgYXMg\ +aXMuCiogSWYgdGhlIGlucHV0IGlzIGEgUmVnRXhwTGlrZSBvYmplY3QsIGl0IHNob3VsZCBoYXZlIGBz\ +b3VyY2VgIGFuZCBgZmxhZ3NgIHByb3BlcnRpZXMuCiogVGhlIGB4YCBmbGFnIGlzIE5PVCBzdXBwb3J0\ +ZWQgYW5kIHdpbGwgdGhyb3cgYW4gZXJyb3IgaWYgcHJlc2VudC4KKiBAcGFyYW0gcmVnZXhwCiogQHBh\ +cmFtIGRlZmF1bHRGbGFncwoqIEByZXR1cm5zCiovCmZ1bmN0aW9uIHRvUmVnRXhwKHJlZ2V4cCkgewoJ\ +aWYgKHJlZ2V4cCBpbnN0YW5jZW9mIFJlZ0V4cCkgcmV0dXJuIHJlZ2V4cDsKCWNvbnN0IHsgc291cmNl\ +LCBmbGFncyB9ID0gcmVnZXhwOwoJaWYgKHR5cGVvZiBzb3VyY2UgIT09ICJzdHJpbmciKSB0aHJvdyBu\ +ZXcgVHlwZUVycm9yKCJJbnZhbGlkIFJlZ0V4cC4iKTsKCWNvbnN0IHJlZyA9IG5ldyBSZWdFeHAoc291\ +cmNlLCBmbGFncyk7CglpZiAocmVnZXhwLmxhc3RJbmRleCAhPT0gdm9pZCAwKSByZWcubGFzdEluZGV4\ +ID0gcmVnZXhwLmxhc3RJbmRleDsKCXJldHVybiByZWc7Cn0KZnVuY3Rpb24gZG9FeGVjUmVnRXhwKHJl\ +Z0V4cCwgdGV4dCkgewoJcmV0dXJuIHJlZ0V4cC5leGVjKHRleHQpOwp9CmZ1bmN0aW9uIGRvTWF0Y2hB\ +bGxSZWdFeHAocmVnRXhwLCB0ZXh0KSB7CglyZXR1cm4gcmVnRXhwW1N5bWJvbC5tYXRjaEFsbF0odGV4\ +dCk7Cn0KZnVuY3Rpb24gZG9NYXRjaFJlZ0V4cChyZWdFeHAsIHRleHQpIHsKCXJldHVybiB0ZXh0Lm1h\ +dGNoKHJlZ0V4cCk7Cn0KZnVuY3Rpb24gdG9SYW5nZXMocikgewoJY29uc3QgcmFuZ2VzID0gbmV3IFVp\ +bnQzMkFycmF5KHIubWF0Y2hlcy5sZW5ndGggKiAyKTsKCWxldCBpID0gMDsKCWZvciAoY29uc3QgbSBv\ +ZiByLm1hdGNoZXMpIHsKCQljb25zdCBpbmRleCA9IG0uaW5kZXggfHwgMDsKCQlyYW5nZXNbaSsrXSA9\ +IGluZGV4OwoJCXJhbmdlc1tpKytdID0gaW5kZXggKyBtWzBdLmxlbmd0aDsKCX0KCXJldHVybiB7CgkJ\ +ZWxhcHNlZFRpbWVNczogci5lbGFwc2VkVGltZU1zLAoJCXJhbmdlcwoJfTsKfQpmdW5jdGlvbiBtYXRj\ +aEFsbFRvUmFuZ2VzUmVnRXhwKHRleHQsIHJlZ0V4cCkgewoJcmV0dXJuIHRvUmFuZ2VzKG1hdGNoQWxs\ +UmVnRXhwKHRleHQsIHJlZ0V4cCkpOwp9CgovLyNlbmRyZWdpb24KLy8jcmVnaW9uIHNyYy91dGlsL2Zv\ +cm1hdC50cwpmdW5jdGlvbiBmb3JtYXQodmFsdWUpIHsKCWNvbnN0IHZpc2l0ZWQgPSAvKiBAX19QVVJF\ +X18gKi8gbmV3IFNldCgpOwoJZnVuY3Rpb24gX2Zvcm1hdCh2YWx1ZSQxKSB7CgkJaWYgKHZpc2l0ZWQu\ +aGFzKHZhbHVlJDEpKSByZXR1cm4gIltDaXJjdWxhcl0iOwoJCXZpc2l0ZWQuYWRkKHZhbHVlJDEpOwoJ\ +CXN3aXRjaCAodHlwZW9mIHZhbHVlJDEpIHsKCQkJY2FzZSAiYmlnaW50IjogcmV0dXJuIHZhbHVlJDEu\ +dG9TdHJpbmcoKSArICJuIjsKCQkJY2FzZSAic3ltYm9sIjogcmV0dXJuIHZhbHVlJDEudG9TdHJpbmco\ +KTsKCQkJY2FzZSAiZnVuY3Rpb24iOiByZXR1cm4gdmFsdWUkMS5uYW1lID8gYFtGdW5jdGlvbjogJHt2\ +YWx1ZSQxLm5hbWV9XWAgOiAiW0Z1bmN0aW9uXSI7CgkJCWNhc2UgInVuZGVmaW5lZCI6IHJldHVybiAi\ +dW5kZWZpbmVkIjsKCQkJY2FzZSAib2JqZWN0IjogcmV0dXJuIG9ialRvU3RyaW5nKHZhbHVlJDEpOwoJ\ +CQljYXNlICJzdHJpbmciOiByZXR1cm4gSlNPTi5zdHJpbmdpZnkodmFsdWUkMSk7CgkJfQoJCXJldHVy\ +biBTdHJpbmcodmFsdWUkMSk7Cgl9CglmdW5jdGlvbiBvYmpUb1N0cmluZyh2YWx1ZSQxKSB7CgkJdHJ5\ +IHsKCQkJaWYgKHZhbHVlJDEgPT09IG51bGwpIHJldHVybiAibnVsbCI7CgkJCWlmICh2YWx1ZSQxIGlu\ +c3RhbmNlb2YgRXJyb3IpIHJldHVybiBgRXJyb3I6ICR7dmFsdWUkMS5tZXNzYWdlfVxuU3RhY2s6ICR7\ +dmFsdWUkMS5zdGFja31gOwoJCQlpZiAoQXJyYXkuaXNBcnJheSh2YWx1ZSQxKSkgcmV0dXJuIGBbJHt2\ +YWx1ZSQxLm1hcChfZm9ybWF0KS5qb2luKCIsICIpfV1gOwoJCQlpZiAoInRvU3RyaW5nIiBpbiB2YWx1\ +ZSQxICYmIHR5cGVvZiB2YWx1ZSQxWyJ0b1N0cmluZyJdID09PSAiZnVuY3Rpb24iICYmIE9iamVjdC5w\ +cm90b3R5cGUudG9TdHJpbmcgIT09IHZhbHVlJDFbInRvU3RyaW5nIl0pIHJldHVybiB2YWx1ZSQxWyJ0\ +b1N0cmluZyJdKCk7CgkJCWNvbnN0IHMgPSBPYmplY3QuZW50cmllcyh2YWx1ZSQxKS5tYXAoKFtrZXks\ +IHZhbF0pID0+IGAke2tleX06ICR7X2Zvcm1hdCh2YWwpfWApLmpvaW4oIiwgIik7CgkJCXJldHVybiBg\ +eyR7cyA/IGAgJHtzfSBgIDogIiJ9fWA7CgkJfSBjYXRjaCAoZSkgewoJCQlyZXR1cm4gU3RyaW5nKGUp\ +OwoJCX0KCX0KCXJldHVybiBfZm9ybWF0KHZhbHVlKTsKfQoKLy8jZW5kcmVnaW9uCi8vI3JlZ2lvbiBz\ +cmMvaGVscGVycy9lcnJvcnMudHMKY29uc3QgYWxsb3dTdHJpbmdPclVuZGVmaW5lZCA9IHsKCXN0cmlu\ +ZzogdHJ1ZSwKCXVuZGVmaW5lZDogdHJ1ZQp9OwpmdW5jdGlvbiBpc0Vycm9yTGlrZShlKSB7CglpZiAo\ +ZSBpbnN0YW5jZW9mIEVycm9yKSByZXR1cm4gdHJ1ZTsKCWlmICghZSB8fCB0eXBlb2YgZSAhPT0gIm9i\ +amVjdCIpIHJldHVybiBmYWxzZTsKCWNvbnN0IGV4ID0gZTsKCXJldHVybiB0eXBlb2YgZXgubmFtZSA9\ +PSAic3RyaW5nIiAmJiB0eXBlb2YgZXgubWVzc2FnZSA9PSAic3RyaW5nIiAmJiB0eXBlb2YgZXguc3Rh\ +Y2sgaW4gYWxsb3dTdHJpbmdPclVuZGVmaW5lZDsKfQpmdW5jdGlvbiB0b0Vycm9yKGUpIHsKCWlmIChl\ +IGluc3RhbmNlb2YgRXJyb3IpIHJldHVybiBlOwoJaWYgKGlzRXJyb3JMaWtlKGUpKSByZXR1cm4gbmV3\ +IEVycm9yKGUubWVzc2FnZSB8fCAiVW5rbm93biBlcnJvciIsIHsgY2F1c2U6IGUgfSk7CglpZiAodHlw\ +ZW9mIGUgPT09ICJzdHJpbmciKSByZXR1cm4gbmV3IEVycm9yKGUpOwoJcmV0dXJuIG5ldyBFcnJvcihT\ +dHJpbmcoZSksIHsgY2F1c2U6IGUgfSk7Cn0KCi8vI2VuZHJlZ2lvbgovLyNyZWdpb24gc3JjL1Byb2Nl\ +ZHVyZXMvdW5pcXVlSWQudHMKbGV0IGlkID0gdHQoKVswXTsKZnVuY3Rpb24gdHQoKSB7Cgljb25zdCBu\ +ID0gcGVyZm9ybWFuY2Uubm93KCk7Cgljb25zdCBzID0gTWF0aC5mbG9vcihuIC8gMWUzKTsKCXJldHVy\ +biBbcywgTWF0aC5mbG9vcigobiAtIHMgKiAxZTMpICogMWU2KV07Cn0KZnVuY3Rpb24gaXNJZCh2YWx1\ +ZSkgewoJaWYgKHR5cGVvZiB2YWx1ZSA9PT0gInN0cmluZyIgJiYgL15JRC1cZCstXGQrLVxkKy1cZCsk\ +Ly50ZXN0KHZhbHVlKSkgewoJCWNvbnN0IFthLCBiLCBjLCBkXSA9IHZhbHVlLnNwbGl0KCItIikuc2xp\ +Y2UoMSkubWFwKCh2KSA9PiBOdW1iZXIucGFyc2VJbnQodiwgMTApKTsKCQlyZXR1cm4gYSA9PT0gYiAl\ +IDEzICYmIGQgPT09IChiICsgYykgJSA3OwoJfQoJcmV0dXJuIGZhbHNlOwp9CmNvbnN0IE51bGxJRCA9\ +ICJJRC0wLTAtMC0wIjsKCi8vI2VuZHJlZ2lvbgovLyNyZWdpb24gc3JjL1Byb2NlZHVyZXMvcHJvY2Vk\ +dXJlLnRzCmZ1bmN0aW9uIGlzUmVxdWVzdFR5cGUodiwgcmVxdWVzdFR5cGUpIHsKCXJldHVybiBpc1Jl\ +cXVlc3QodikgJiYgdi5yZXF1ZXN0VHlwZSA9PT0gcmVxdWVzdFR5cGU7Cn0KZnVuY3Rpb24gaXNSZXF1\ +ZXN0KHYpIHsKCWlmICghdiB8fCB0eXBlb2YgdiAhPT0gIm9iamVjdCIpIHJldHVybiBmYWxzZTsKCWNv\ +bnN0IHIgPSB2OwoJcmV0dXJuIHR5cGVvZiByLnJlcXVlc3RUeXBlID09PSAic3RyaW5nIiAmJiBpc0lk\ +KHIuaWQpOwp9CmZ1bmN0aW9uIGNyZWF0ZVJlc3BvbnNlKGlkJDEsIHJlc3BvbnNlVHlwZSwgZGF0YSkg\ +ewoJcmV0dXJuIHsKCQlpZDogaWQkMSwKCQl0aW1lc3RhbXA6IERhdGUubm93KCksCgkJcmVzcG9uc2VU\ +eXBlLAoJCWRhdGEKCX07Cn0KY29uc3QgcmVzcG9uc2VUeXBlRXJyb3IgPSAiRXJyb3IiOwpmdW5jdGlv\ +biBjcmVhdGVFcnJvclJlc3BvbnNlKHJlcXVlc3QsIG1lc3NhZ2UsIGVycm9yKSB7CglpZiAoIWlzUmVx\ +dWVzdChyZXF1ZXN0KSkgewoJCWNvbnN0IHIgPSByZXF1ZXN0ICYmIHR5cGVvZiByZXF1ZXN0ID09PSAi\ +b2JqZWN0IiA/IHJlcXVlc3QgOiB7fTsKCQljb25zdCBpZCQyID0gaXNJZChyLmlkKSA/IHIuaWQgOiBO\ +dWxsSUQ7CgkJcmV0dXJuIGNyZWF0ZVJlc3BvbnNlKGlkJDIsIHJlc3BvbnNlVHlwZUVycm9yLCB7CgkJ\ +CXJlcXVlc3RUeXBlOiB0eXBlb2Ygci5yZXF1ZXN0VHlwZSA9PT0gInN0cmluZyIgPyByLnJlcXVlc3RU\ +eXBlIDogdm9pZCAwLAoJCQltZXNzYWdlLAoJCQllcnJvcjogdG9FcnJvcihlcnJvcikKCQl9KTsKCX0K\ +CWNvbnN0IHsgaWQ6IGlkJDEsIHJlcXVlc3RUeXBlIH0gPSByZXF1ZXN0OwoJcmV0dXJuIGNyZWF0ZVJl\ +c3BvbnNlKGlkJDEsIHJlc3BvbnNlVHlwZUVycm9yLCB7CgkJcmVxdWVzdFR5cGUsCgkJbWVzc2FnZSwK\ +CQllcnJvcjogdG9FcnJvcihlcnJvcikKCX0pOwp9CgovLyNlbmRyZWdpb24KLy8jcmVnaW9uIHNyYy9Q\ +cm9jZWR1cmVzL3Byb2NFeGVjUmVnRXhwLnRzCmNvbnN0IHJlcXVlc3RUeXBlRXhlY1JlZ0V4cCA9ICJF\ +eGVjUmVnRXhwIjsKZnVuY3Rpb24gaXNFeGVjUmVnRXhwUmVxdWVzdCh2KSB7CglyZXR1cm4gaXNSZXF1\ +ZXN0VHlwZSh2LCByZXF1ZXN0VHlwZUV4ZWNSZWdFeHApOwp9CmZ1bmN0aW9uIHByb2NFeGVjUmVnRXhw\ +KHIpIHsKCWlmICghaXNFeGVjUmVnRXhwUmVxdWVzdChyKSkgcmV0dXJuIHZvaWQgMDsKCXRyeSB7CgkJ\ +Y29uc3QgcmVnZXggPSB0b1JlZ0V4cChyLmRhdGEucmVnZXhwKTsKCQlyZXR1cm4gY3JlYXRlUmVzcG9u\ +c2VFeGVjUmVnRXhwKHIsIGV4ZWNSZWdFeHAocmVnZXgsIHIuZGF0YS50ZXh0KSk7Cgl9IGNhdGNoIChl\ +KSB7CgkJcmV0dXJuIGNyZWF0ZUVycm9yUmVzcG9uc2UociwgZm9ybWF0KGUpKTsKCX0KfQpmdW5jdGlv\ +biBjcmVhdGVSZXNwb25zZUV4ZWNSZWdFeHAocmVxdWVzdCwgZGF0YSkgewoJcmV0dXJuIGNyZWF0ZVJl\ +c3BvbnNlKHJlcXVlc3QuaWQsIHJlcXVlc3QucmVxdWVzdFR5cGUsIGRhdGEpOwp9CgovLyNlbmRyZWdp\ +b24KLy8jcmVnaW9uIHNyYy9Qcm9jZWR1cmVzL3Byb2NNYXRjaEFsbFJlZ0V4cC50cwpjb25zdCByZXF1\ +ZXN0VHlwZU1hdGNoQWxsUmVnRXhwID0gIk1hdGNoQWxsUmVnRXhwIjsKZnVuY3Rpb24gaXNNYXRjaEFs\ +bFJlZ0V4cFJlcXVlc3QodikgewoJcmV0dXJuIGlzUmVxdWVzdFR5cGUodiwgcmVxdWVzdFR5cGVNYXRj\ +aEFsbFJlZ0V4cCk7Cn0KZnVuY3Rpb24gcHJvY01hdGNoQWxsUmVnRXhwKHIpIHsKCWlmICghaXNNYXRj\ +aEFsbFJlZ0V4cFJlcXVlc3QocikpIHJldHVybiB2b2lkIDA7Cgl0cnkgewoJCWNvbnN0IHJlZ2V4ID0g\ +dG9SZWdFeHAoci5kYXRhLnJlZ2V4cCk7CgkJY29uc3QgcmVnZXhSZXN1bHQgPSBtYXRjaEFsbFJlZ0V4\ +cChyLmRhdGEudGV4dCwgcmVnZXgpOwoJCXJldHVybiBjcmVhdGVSZXNwb25zZU1hdGNoQWxsUmVnRXhw\ +KHIsIHJlZ2V4UmVzdWx0KTsKCX0gY2F0Y2ggKGUpIHsKCQlyZXR1cm4gY3JlYXRlRXJyb3JSZXNwb25z\ +ZShyLCBmb3JtYXQoZSkpOwoJfQp9CmZ1bmN0aW9uIGNyZWF0ZVJlc3BvbnNlTWF0Y2hBbGxSZWdFeHAo\ +cmVxdWVzdCwgZGF0YSkgewoJcmV0dXJuIGNyZWF0ZVJlc3BvbnNlKHJlcXVlc3QuaWQsIHJlcXVlc3Qu\ +cmVxdWVzdFR5cGUsIGRhdGEpOwp9CgovLyNlbmRyZWdpb24KLy8jcmVnaW9uIHNyYy9Qcm9jZWR1cmVz\ +L3Byb2NNYXRjaEFsbFJlZ0V4cEFycmF5LnRzCmNvbnN0IHJlcXVlc3RUeXBlTWF0Y2hSZWdFeHBBcnJh\ +eSA9ICJNYXRjaEFsbFJlZ0V4cEFycmF5IjsKZnVuY3Rpb24gaXNNYXRjaFJlZ0V4cEFycmF5UmVxdWVz\ +dCh2KSB7CglyZXR1cm4gaXNSZXF1ZXN0VHlwZSh2LCByZXF1ZXN0VHlwZU1hdGNoUmVnRXhwQXJyYXkp\ +Owp9CmZ1bmN0aW9uIHByb2NNYXRjaEFsbFJlZ0V4cEFycmF5KHIpIHsKCWlmICghaXNNYXRjaFJlZ0V4\ +cEFycmF5UmVxdWVzdChyKSkgcmV0dXJuIHZvaWQgMDsKCXRyeSB7CgkJY29uc3QgcmVnZXggPSByLmRh\ +dGEucmVnZXhwcy5tYXAoKHIkMSkgPT4gdG9SZWdFeHAociQxKSk7CgkJY29uc3QgcmVnZXhSZXN1bHQg\ +PSBtYXRjaEFsbFJlZ0V4cEFycmF5KHIuZGF0YS50ZXh0LCByZWdleCk7CgkJcmV0dXJuIGNyZWF0ZVJl\ +c3BvbnNlTWF0Y2hSZWdFeHBBcnJheShyLCByZWdleFJlc3VsdCk7Cgl9IGNhdGNoIChlKSB7CgkJcmV0\ +dXJuIGNyZWF0ZUVycm9yUmVzcG9uc2UociwgZm9ybWF0KGUpKTsKCX0KfQpmdW5jdGlvbiBjcmVhdGVS\ +ZXNwb25zZU1hdGNoUmVnRXhwQXJyYXkocmVxdWVzdCwgZGF0YSkgewoJcmV0dXJuIGNyZWF0ZVJlc3Bv\ +bnNlKHJlcXVlc3QuaWQsIHJlcXVlc3QucmVxdWVzdFR5cGUsIGRhdGEpOwp9CgovLyNlbmRyZWdpb24K\ +Ly8jcmVnaW9uIHNyYy9Qcm9jZWR1cmVzL3Byb2NNYXRjaEFsbFJlZ0V4cEFzUmFuZ2UudHMKY29uc3Qg\ +cmVxdWVzdFR5cGVNYXRjaEFsbFJlZ0V4cEFzUmFuZ2UgPSAiTWF0Y2hBbGxSZWdFeHBBc1JhbmdlIjsK\ +ZnVuY3Rpb24gaXNNYXRjaEFsbFJlZ0V4cEFzUmFuZ2VSZXF1ZXN0KHYpIHsKCXJldHVybiBpc1JlcXVl\ +c3RUeXBlKHYsIHJlcXVlc3RUeXBlTWF0Y2hBbGxSZWdFeHBBc1JhbmdlKTsKfQpmdW5jdGlvbiBwcm9j\ +TWF0Y2hBbGxSZWdFeHBBc1JhbmdlKHIpIHsKCWlmICghaXNNYXRjaEFsbFJlZ0V4cEFzUmFuZ2VSZXF1\ +ZXN0KHIpKSByZXR1cm4gdm9pZCAwOwoJdHJ5IHsKCQljb25zdCByZWdleCA9IHRvUmVnRXhwKHIuZGF0\ +YS5yZWdleHApOwoJCWNvbnN0IHJlZ2V4UmVzdWx0ID0gbWF0Y2hBbGxUb1Jhbmdlc1JlZ0V4cChyLmRh\ +dGEudGV4dCwgcmVnZXgpOwoJCXJldHVybiBjcmVhdGVSZXNwb25zZU1hdGNoQWxsUmVnRXhwQXNSYW5n\ +ZShyLCByZWdleFJlc3VsdCk7Cgl9IGNhdGNoIChlKSB7CgkJcmV0dXJuIGNyZWF0ZUVycm9yUmVzcG9u\ +c2UociwgZm9ybWF0KGUpKTsKCX0KfQpmdW5jdGlvbiBjcmVhdGVSZXNwb25zZU1hdGNoQWxsUmVnRXhw\ +QXNSYW5nZShyZXF1ZXN0LCBkYXRhKSB7CglyZXR1cm4gY3JlYXRlUmVzcG9uc2UocmVxdWVzdC5pZCwg\ +cmVxdWVzdC5yZXF1ZXN0VHlwZSwgZGF0YSk7Cn0KCi8vI2VuZHJlZ2lvbgovLyNyZWdpb24gc3JjL1By\ +b2NlZHVyZXMvcHJvY01hdGNoUmVnRXhwLnRzCmNvbnN0IHJlcXVlc3RUeXBlTWF0Y2hSZWdFeHAgPSAi\ +TWF0Y2hSZWdFeHAiOwpmdW5jdGlvbiBpc01hdGNoUmVnRXhwUmVxdWVzdCh2KSB7CglyZXR1cm4gaXNS\ +ZXF1ZXN0VHlwZSh2LCByZXF1ZXN0VHlwZU1hdGNoUmVnRXhwKTsKfQpmdW5jdGlvbiBwcm9jTWF0Y2hS\ +ZWdFeHAocikgewoJaWYgKCFpc01hdGNoUmVnRXhwUmVxdWVzdChyKSkgcmV0dXJuIHZvaWQgMDsKCXRy\ +eSB7CgkJY29uc3QgcmVnZXggPSB0b1JlZ0V4cChyLmRhdGEucmVnZXhwKTsKCQljb25zdCByZWdleFJl\ +c3VsdCA9IG1hdGNoUmVnRXhwKHIuZGF0YS50ZXh0LCByZWdleCk7CgkJcmV0dXJuIGNyZWF0ZVJlc3Bv\ +bnNlTWF0Y2hSZWdFeHAociwgcmVnZXhSZXN1bHQpOwoJfSBjYXRjaCAoZSkgewoJCXJldHVybiBjcmVh\ +dGVFcnJvclJlc3BvbnNlKHIsIGZvcm1hdChlKSk7Cgl9Cn0KZnVuY3Rpb24gY3JlYXRlUmVzcG9uc2VN\ +YXRjaFJlZ0V4cChyZXF1ZXN0LCBkYXRhKSB7CglyZXR1cm4gY3JlYXRlUmVzcG9uc2UocmVxdWVzdC5p\ +ZCwgcmVxdWVzdC5yZXF1ZXN0VHlwZSwgZGF0YSk7Cn0KCi8vI2VuZHJlZ2lvbgovLyNyZWdpb24gc3Jj\ +L1Byb2NlZHVyZXMvcHJvY2VkdXJlcy50cwovKioKKiBDb2xsZWN0aW9uIG9mIGFsbCBwcm9jZWR1cmVz\ +IHJlbGF0ZWQgdG8gcmVndWxhciBleHByZXNzaW9ucy4KKiBPcmRlciBpcyBpbXBvcnRhbnQgZm9yIHRo\ +ZSB3b3JrZXIsIGFzIGl0IHByb2Nlc3NlcyB0aGVtIHNlcXVlbnRpYWxseS4KKi8KY29uc3QgcHJvY2Vk\ +dXJlcyA9IFsKCXByb2NNYXRjaEFsbFJlZ0V4cCwKCXByb2NNYXRjaEFsbFJlZ0V4cEFzUmFuZ2UsCglw\ +cm9jRXhlY1JlZ0V4cCwKCXByb2NNYXRjaEFsbFJlZ0V4cEFycmF5LAoJcHJvY01hdGNoUmVnRXhwCl07\ +CgovLyNlbmRyZWdpb24KLy8jcmVnaW9uIHNyYy93b3JrZXIvTG9nTGV2ZWwudHMKbGV0IExvZ0xldmVs\ +ID0gLyogQF9fUFVSRV9fICovIGZ1bmN0aW9uKExvZ0xldmVsJDEpIHsKCUxvZ0xldmVsJDFbTG9nTGV2\ +ZWwkMVsiTG9nTGV2ZWxOb25lIl0gPSAwXSA9ICJMb2dMZXZlbE5vbmUiOwoJTG9nTGV2ZWwkMVtMb2dM\ +ZXZlbCQxWyJMb2dMZXZlbEVycm9yIl0gPSAxXSA9ICJMb2dMZXZlbEVycm9yIjsKCUxvZ0xldmVsJDFb\ +TG9nTGV2ZWwkMVsiTG9nTGV2ZWxXYXJuIl0gPSAyXSA9ICJMb2dMZXZlbFdhcm4iOwoJTG9nTGV2ZWwk\ +MVtMb2dMZXZlbCQxWyJMb2dMZXZlbEluZm8iXSA9IDNdID0gIkxvZ0xldmVsSW5mbyI7CglMb2dMZXZl\ +bCQxW0xvZ0xldmVsJDFbIkxvZ0xldmVsRGVidWciXSA9IDRdID0gIkxvZ0xldmVsRGVidWciOwoJcmV0\ +dXJuIExvZ0xldmVsJDE7Cn0oe30pOwoKLy8jZW5kcmVnaW9uCi8vI3JlZ2lvbiBzcmMvd29ya2VyL01l\ +c3NhZ2VQb3J0LnRzCmNvbnN0IG51bGxQb3J0ID0gT2JqZWN0LmZyZWV6ZSh7Cglwb3N0TWVzc2FnZSgp\ +IHt9LAoJb24oKSB7CgkJcmV0dXJuIG51bGxQb3J0OwoJfSwKCW9mZigpIHsKCQlyZXR1cm4gbnVsbFBv\ +cnQ7Cgl9LAoJb25jZSgpIHsKCQlyZXR1cm4gbnVsbFBvcnQ7Cgl9Cn0pOwoKLy8jZW5kcmVnaW9uCi8v\ +I3JlZ2lvbiBzcmMvd29ya2VyL1dvcmtlck1lc3NhZ2VIYW5kbGVyLnRzCnZhciBXb3JrZXJNZXNzYWdl\ +SGFuZGxlciA9IGNsYXNzIHsKCWxvZ0xldmVsID0gTG9nTGV2ZWwuTG9nTGV2ZWxFcnJvcjsKCWxpc3Rl\ +bmVyOwoJY29uc3RydWN0b3IocG9ydCwgcHJvY2VkdXJlcyQxKSB7CgkJdGhpcy5wb3J0ID0gcG9ydDsK\ +CQl0aGlzLnByb2NlZHVyZXMgPSBwcm9jZWR1cmVzJDE7CgkJdGhpcy5saXN0ZW5lciA9ICh2KSA9PiB0\ +aGlzLmxpc3RlbmVyTWVzc2FnZSh2KTsKCQlwb3J0Lm9uKCJtZXNzYWdlIiwgdGhpcy5saXN0ZW5lcik7\ +Cgl9CglkaXNwb3NlKCkgewoJCXRoaXMucG9ydC5vZmYoIm1lc3NhZ2UiLCB0aGlzLmxpc3RlbmVyKTsK\ +CQl0aGlzLnBvcnQgPSBudWxsUG9ydDsKCX0KCXBvc3QobXNnKSB7CgkJdGhpcy5sb2coTG9nTGV2ZWwu\ +TG9nTGV2ZWxEZWJ1ZywgIlBvc3Q6ICIgKyBKU09OLnN0cmluZ2lmeShtc2cpKTsKCQl0aGlzLnBvcnQu\ +cG9zdE1lc3NhZ2UobXNnKTsKCX0KCWxvZyhsZXZlbCwgLi4ucGFyYW1zKSB7CgkJaWYgKGxldmVsID4g\ +dGhpcy5sb2dMZXZlbCkgcmV0dXJuOwoJCXN3aXRjaCAobGV2ZWwpIHsKCQkJY2FzZSBMb2dMZXZlbC5M\ +b2dMZXZlbEVycm9yOgoJCQkJY29uc29sZS5lcnJvciguLi5wYXJhbXMpOwoJCQkJYnJlYWs7CgkJCWNh\ +c2UgTG9nTGV2ZWwuTG9nTGV2ZWxXYXJuOgoJCQkJY29uc29sZS53YXJuKC4uLnBhcmFtcyk7CgkJCQli\ +cmVhazsKCQkJZGVmYXVsdDogY29uc29sZS5sb2coLi4ucGFyYW1zKTsKCQl9Cgl9CglsaXN0ZW5lck1l\ +c3NhZ2UodmFsdWUpIHsKCQl0aGlzLmxvZyhMb2dMZXZlbC5Mb2dMZXZlbERlYnVnLCBgbWVzc2FnZTog\ +JHtKU09OLnN0cmluZ2lmeSh2YWx1ZSl9YCk7CgkJaWYgKCFpc1JlcXVlc3QodmFsdWUpKSB7CgkJCWNv\ +bnN0IG1zZyA9IGBCYWRseSBmb3JtZWQgUmVxdWVzdDogJHtKU09OLnN0cmluZ2lmeSh2YWx1ZSl9YDsK\ +CQkJdGhpcy5sb2coTG9nTGV2ZWwuTG9nTGV2ZWxFcnJvciwgbXNnKTsKCQkJdGhpcy5wb3N0KGNyZWF0\ +ZUVycm9yUmVzcG9uc2UodmFsdWUsIG1zZykpOwoJCQlyZXR1cm47CgkJfQoJCWNvbnN0IHJlcXVlc3Qg\ +PSB2YWx1ZTsKCQlmb3IgKGNvbnN0IHByb2Mgb2YgdGhpcy5wcm9jZWR1cmVzKSB0cnkgewoJCQljb25z\ +dCByZXNwb25zZSA9IHByb2MocmVxdWVzdCk7CgkJCWlmIChyZXNwb25zZSAhPT0gdm9pZCAwKSB7CgkJ\ +CQlQcm9taXNlLnJlc29sdmUocmVzcG9uc2UpLmNhdGNoKChyZWFzb24pID0+IGNyZWF0ZUVycm9yUmVz\ +cG9uc2UocmVxdWVzdCwgU3RyaW5nKHJlYXNvbiksIHRvRXJyb3IocmVhc29uKSkpLnRoZW4oKHIpID0+\ +IHRoaXMucG9zdChyKSkuY2F0Y2goKCkgPT4ge30pOwoJCQkJcmV0dXJuOwoJCQl9CgkJfSBjYXRjaCAo\ +ZSkgewoJCQljb25zdCBtc2cgPSBpc0Vycm9yTGlrZShlKSA/IGUubWVzc2FnZSA6IGZvcm1hdChlKTsK\ +CQkJdGhpcy5wb3N0KGNyZWF0ZUVycm9yUmVzcG9uc2UocmVxdWVzdCwgbXNnLCBpc0Vycm9yTGlrZShl\ +KSA/IGUgOiB2b2lkIDApKTsKCQkJcmV0dXJuOwoJCX0KCQl0aGlzLmxvZyhMb2dMZXZlbC5Mb2dMZXZl\ +bFdhcm4sIGBVbmhhbmRsZWQgUmVxdWVzdCAiJHt2YWx1ZS5yZXF1ZXN0VHlwZX0iYCk7CgkJdGhpcy5w\ +b3N0KGNyZWF0ZUVycm9yUmVzcG9uc2UocmVxdWVzdCwgIlVuaGFuZGxlZCBSZXF1ZXN0IikpOwoJfQp9\ +OwpmdW5jdGlvbiBjcmVhdGVIYW5kbGVyKHBvcnQsIHByb2NlZHVyZXMkMSkgewoJcmV0dXJuIG5ldyBX\ +b3JrZXJNZXNzYWdlSGFuZGxlcihwb3J0LCBwcm9jZWR1cmVzJDEpOwp9CgovLyNlbmRyZWdpb24KLy8j\ +cmVnaW9uIHNyYy93b3JrZXIvd29ya2VyQ29kZUJyb3dzZXIudHMKdmFyIEJyb3dzZXJNZXNzYWdlUG9y\ +dCA9IGNsYXNzIHsKCSNtZXNzYWdlTGlzdGVuZXJzID0gLyogQF9fUFVSRV9fICovIG5ldyBTZXQoKTsK\ +CWNvbnN0cnVjdG9yKCkgewoJCWdsb2JhbFRoaXMuYWRkRXZlbnRMaXN0ZW5lcigibWVzc2FnZSIsIHRo\ +aXMuI2xpc3RlbmVyTWVzc2FnZSk7Cgl9Cglwb3N0TWVzc2FnZSh2YWx1ZSkgewoJCWdsb2JhbFRoaXMu\ +cG9zdE1lc3NhZ2UodmFsdWUpOwoJfQoJb24oZXZlbnQsIGxpc3RlbmVyKSB7CgkJaWYgKGV2ZW50ID09\ +PSAibWVzc2FnZSIpIHRoaXMuI21lc3NhZ2VMaXN0ZW5lcnMuYWRkKGxpc3RlbmVyKTsKCQlyZXR1cm4g\ +dGhpczsKCX0KCW9mZihldmVudCwgbGlzdGVuZXIpIHsKCQlpZiAoZXZlbnQgPT09ICJtZXNzYWdlIikg\ +dGhpcy4jbWVzc2FnZUxpc3RlbmVycy5kZWxldGUobGlzdGVuZXIpOwoJCXJldHVybiB0aGlzOwoJfQoJ\ +I2xpc3RlbmVyTWVzc2FnZSA9IChldmVudCkgPT4gewoJCWZvciAoY29uc3QgbGlzdGVuZXIgb2YgdGhp\ +cy4jbWVzc2FnZUxpc3RlbmVycykgbGlzdGVuZXIoZXZlbnQuZGF0YSk7Cgl9Owp9OwpjcmVhdGVIYW5k\ +bGVyKG5ldyBCcm93c2VyTWVzc2FnZVBvcnQoKSwgcHJvY2VkdXJlcyk7CgovLyNlbmRyZWdpb24=\ `; diff --git a/src/worker/workerCodeNodeDataURL.ts b/src/worker/workerCodeNodeDataURL.ts index 8b79cc0..b4cc224 100644 --- a/src/worker/workerCodeNodeDataURL.ts +++ b/src/worker/workerCodeNodeDataURL.ts @@ -28,184 +28,182 @@ YW0gcmVnRXhwQXJyYXkKKiBAcmV0dXJucwoqLwpmdW5jdGlvbiBtYXRjaEFsbFJlZ0V4cEFycmF5KHRl eHQsIHJlZ0V4cEFycmF5KSB7Cgljb25zdCB7IGVsYXBzZWRUaW1lTXMsIHI6IHJlc3VsdHMgfSA9IG1l\ YXN1cmVFeGVjdXRpb24oKCkgPT4gewoJCXJldHVybiByZWdFeHBBcnJheS5tYXAoKHIpID0+IG1hdGNo\ QWxsUmVnRXhwKHRleHQsIHIpKTsKCX0pOwoJcmV0dXJuIHsKCQllbGFwc2VkVGltZU1zLAoJCXJlc3Vs\ -dHMKCX07Cn0KLyoqCioKKiBAcGFyYW0gcgoqIEBwYXJhbSBkZWZhdWx0RmxhZ3MKKiBAcmV0dXJucwoq\ -LwpmdW5jdGlvbiB0b1JlZ0V4cChyLCBkZWZhdWx0RmxhZ3MpIHsKCWlmIChyIGluc3RhbmNlb2YgUmVn\ -RXhwKSByZXR1cm4gcjsKCWlmICh0eXBlb2YgciA9PT0gInN0cmluZyIpIHsKCQljb25zdCBtYXRjaCA9\ -IHIubWF0Y2goL15cLyguKilcLyhbZ2ltc3V5XSopJC8pOwoJCWlmIChtYXRjaCkgcmV0dXJuIG5ldyBS\ -ZWdFeHAobWF0Y2hbMV0sIG1hdGNoWzJdIHx8IGRlZmF1bHRGbGFncyk7CgkJcmV0dXJuIG5ldyBSZWdF\ -eHAociwgZGVmYXVsdEZsYWdzKTsKCX0KCWlmIChpc1JlZ0V4cExpa2UocikpIHsKCQljb25zdCByZWcg\ -PSBuZXcgUmVnRXhwKHIuc291cmNlLCByLmZsYWdzIHx8IGRlZmF1bHRGbGFncyk7CgkJaWYgKHIubGFz\ -dEluZGV4ICE9PSB2b2lkIDApIHJlZy5sYXN0SW5kZXggPSByLmxhc3RJbmRleDsKCQlyZXR1cm4gcmVn\ -OwoJfQoJdGhyb3cgbmV3IFR5cGVFcnJvcigiSW52YWxpZCBSZWdFeHAgb3Igc3RyaW5nLiIpOwp9CmZ1\ -bmN0aW9uIGlzUmVnRXhwTGlrZShyKSB7CglpZiAociBpbnN0YW5jZW9mIFJlZ0V4cCkgcmV0dXJuIHRy\ -dWU7CglpZiAociAmJiB0eXBlb2YgciA9PT0gIm9iamVjdCIgJiYgInNvdXJjZSIgaW4gciAmJiAiZmxh\ -Z3MiIGluIHIgJiYgdHlwZW9mIHIuc291cmNlID09PSAic3RyaW5nIiAmJiB0eXBlb2Ygci5mbGFncyA9\ -PT0gInN0cmluZyIpIHJldHVybiB0cnVlOwoJcmV0dXJuIGZhbHNlOwp9CmZ1bmN0aW9uIGRvRXhlY1Jl\ -Z0V4cChyZWdFeHAsIHRleHQpIHsKCXJldHVybiByZWdFeHAuZXhlYyh0ZXh0KTsKfQpmdW5jdGlvbiBk\ -b01hdGNoQWxsUmVnRXhwKHJlZ0V4cCwgdGV4dCkgewoJcmV0dXJuIHJlZ0V4cFtTeW1ib2wubWF0Y2hB\ -bGxdKHRleHQpOwp9CmZ1bmN0aW9uIGRvTWF0Y2hSZWdFeHAocmVnRXhwLCB0ZXh0KSB7CglyZXR1cm4g\ -dGV4dC5tYXRjaChyZWdFeHApOwp9CmZ1bmN0aW9uIHRvUmFuZ2VzKHIpIHsKCWNvbnN0IHJhbmdlcyA9\ -IG5ldyBVaW50MzJBcnJheShyLm1hdGNoZXMubGVuZ3RoICogMik7CglsZXQgaSA9IDA7Cglmb3IgKGNv\ -bnN0IG0gb2Ygci5tYXRjaGVzKSB7CgkJY29uc3QgaW5kZXggPSBtLmluZGV4IHx8IDA7CgkJcmFuZ2Vz\ -W2krK10gPSBpbmRleDsKCQlyYW5nZXNbaSsrXSA9IGluZGV4ICsgbVswXS5sZW5ndGg7Cgl9CglyZXR1\ -cm4gewoJCWVsYXBzZWRUaW1lTXM6IHIuZWxhcHNlZFRpbWVNcywKCQlyYW5nZXMKCX07Cn0KZnVuY3Rp\ -b24gbWF0Y2hBbGxUb1Jhbmdlc1JlZ0V4cCh0ZXh0LCByZWdFeHApIHsKCXJldHVybiB0b1Jhbmdlcyht\ -YXRjaEFsbFJlZ0V4cCh0ZXh0LCByZWdFeHApKTsKfQoKLy8jZW5kcmVnaW9uCi8vI3JlZ2lvbiBzcmMv\ -dXRpbC9mb3JtYXQudHMKZnVuY3Rpb24gZm9ybWF0KHZhbHVlKSB7Cgljb25zdCB2aXNpdGVkID0gLyog\ -QF9fUFVSRV9fICovIG5ldyBTZXQoKTsKCWZ1bmN0aW9uIF9mb3JtYXQodmFsdWUkMSkgewoJCWlmICh2\ -aXNpdGVkLmhhcyh2YWx1ZSQxKSkgcmV0dXJuICJbQ2lyY3VsYXJdIjsKCQl2aXNpdGVkLmFkZCh2YWx1\ -ZSQxKTsKCQlzd2l0Y2ggKHR5cGVvZiB2YWx1ZSQxKSB7CgkJCWNhc2UgImJpZ2ludCI6IHJldHVybiB2\ -YWx1ZSQxLnRvU3RyaW5nKCkgKyAibiI7CgkJCWNhc2UgInN5bWJvbCI6IHJldHVybiB2YWx1ZSQxLnRv\ -U3RyaW5nKCk7CgkJCWNhc2UgImZ1bmN0aW9uIjogcmV0dXJuIHZhbHVlJDEubmFtZSA/IGBbRnVuY3Rp\ -b246ICR7dmFsdWUkMS5uYW1lfV1gIDogIltGdW5jdGlvbl0iOwoJCQljYXNlICJ1bmRlZmluZWQiOiBy\ -ZXR1cm4gInVuZGVmaW5lZCI7CgkJCWNhc2UgIm9iamVjdCI6IHJldHVybiBvYmpUb1N0cmluZyh2YWx1\ -ZSQxKTsKCQkJY2FzZSAic3RyaW5nIjogcmV0dXJuIEpTT04uc3RyaW5naWZ5KHZhbHVlJDEpOwoJCX0K\ -CQlyZXR1cm4gU3RyaW5nKHZhbHVlJDEpOwoJfQoJZnVuY3Rpb24gb2JqVG9TdHJpbmcodmFsdWUkMSkg\ -ewoJCXRyeSB7CgkJCWlmICh2YWx1ZSQxID09PSBudWxsKSByZXR1cm4gIm51bGwiOwoJCQlpZiAodmFs\ -dWUkMSBpbnN0YW5jZW9mIEVycm9yKSByZXR1cm4gYEVycm9yOiAke3ZhbHVlJDEubWVzc2FnZX1cblN0\ -YWNrOiAke3ZhbHVlJDEuc3RhY2t9YDsKCQkJaWYgKEFycmF5LmlzQXJyYXkodmFsdWUkMSkpIHJldHVy\ -biBgWyR7dmFsdWUkMS5tYXAoX2Zvcm1hdCkuam9pbigiLCAiKX1dYDsKCQkJaWYgKCJ0b1N0cmluZyIg\ -aW4gdmFsdWUkMSAmJiB0eXBlb2YgdmFsdWUkMVsidG9TdHJpbmciXSA9PT0gImZ1bmN0aW9uIiAmJiBP\ -YmplY3QucHJvdG90eXBlLnRvU3RyaW5nICE9PSB2YWx1ZSQxWyJ0b1N0cmluZyJdKSByZXR1cm4gdmFs\ -dWUkMVsidG9TdHJpbmciXSgpOwoJCQljb25zdCBzID0gT2JqZWN0LmVudHJpZXModmFsdWUkMSkubWFw\ -KChba2V5LCB2YWxdKSA9PiBgJHtrZXl9OiAke19mb3JtYXQodmFsKX1gKS5qb2luKCIsICIpOwoJCQly\ -ZXR1cm4gYHske3MgPyBgICR7c30gYCA6ICIifX1gOwoJCX0gY2F0Y2ggKGUpIHsKCQkJcmV0dXJuIFN0\ -cmluZyhlKTsKCQl9Cgl9CglyZXR1cm4gX2Zvcm1hdCh2YWx1ZSk7Cn0KCi8vI2VuZHJlZ2lvbgovLyNy\ -ZWdpb24gc3JjL2hlbHBlcnMvZXJyb3JzLnRzCmNvbnN0IGFsbG93U3RyaW5nT3JVbmRlZmluZWQgPSB7\ -CglzdHJpbmc6IHRydWUsCgl1bmRlZmluZWQ6IHRydWUKfTsKZnVuY3Rpb24gaXNFcnJvckxpa2UoZSkg\ -ewoJaWYgKGUgaW5zdGFuY2VvZiBFcnJvcikgcmV0dXJuIHRydWU7CglpZiAoIWUgfHwgdHlwZW9mIGUg\ -IT09ICJvYmplY3QiKSByZXR1cm4gZmFsc2U7Cgljb25zdCBleCA9IGU7CglyZXR1cm4gdHlwZW9mIGV4\ -Lm5hbWUgPT0gInN0cmluZyIgJiYgdHlwZW9mIGV4Lm1lc3NhZ2UgPT0gInN0cmluZyIgJiYgdHlwZW9m\ -IGV4LnN0YWNrIGluIGFsbG93U3RyaW5nT3JVbmRlZmluZWQ7Cn0KZnVuY3Rpb24gdG9FcnJvcihlKSB7\ -CglpZiAoZSBpbnN0YW5jZW9mIEVycm9yKSByZXR1cm4gZTsKCWlmIChpc0Vycm9yTGlrZShlKSkgcmV0\ -dXJuIG5ldyBFcnJvcihlLm1lc3NhZ2UgfHwgIlVua25vd24gZXJyb3IiLCB7IGNhdXNlOiBlIH0pOwoJ\ -aWYgKHR5cGVvZiBlID09PSAic3RyaW5nIikgcmV0dXJuIG5ldyBFcnJvcihlKTsKCXJldHVybiBuZXcg\ -RXJyb3IoU3RyaW5nKGUpLCB7IGNhdXNlOiBlIH0pOwp9CgovLyNlbmRyZWdpb24KLy8jcmVnaW9uIHNy\ -Yy9Qcm9jZWR1cmVzL3VuaXF1ZUlkLnRzCmxldCBpZCA9IHR0KClbMF07CmZ1bmN0aW9uIHR0KCkgewoJ\ -Y29uc3QgbiA9IHBlcmZvcm1hbmNlLm5vdygpOwoJY29uc3QgcyA9IE1hdGguZmxvb3IobiAvIDFlMyk7\ -CglyZXR1cm4gW3MsIE1hdGguZmxvb3IoKG4gLSBzICogMWUzKSAqIDFlNildOwp9CmZ1bmN0aW9uIGlz\ -SWQodmFsdWUpIHsKCWlmICh0eXBlb2YgdmFsdWUgPT09ICJzdHJpbmciICYmIC9eSUQtXGQrLVxkKy1c\ -ZCstXGQrJC8udGVzdCh2YWx1ZSkpIHsKCQljb25zdCBbYSwgYiwgYywgZF0gPSB2YWx1ZS5zcGxpdCgi\ -LSIpLnNsaWNlKDEpLm1hcCgodikgPT4gTnVtYmVyLnBhcnNlSW50KHYsIDEwKSk7CgkJcmV0dXJuIGEg\ -PT09IGIgJSAxMyAmJiBkID09PSAoYiArIGMpICUgNzsKCX0KCXJldHVybiBmYWxzZTsKfQpjb25zdCBO\ -dWxsSUQgPSAiSUQtMC0wLTAtMCI7CgovLyNlbmRyZWdpb24KLy8jcmVnaW9uIHNyYy9Qcm9jZWR1cmVz\ -L3Byb2NlZHVyZS50cwpmdW5jdGlvbiBpc1JlcXVlc3RUeXBlKHYsIHJlcXVlc3RUeXBlKSB7CglyZXR1\ -cm4gaXNSZXF1ZXN0KHYpICYmIHYucmVxdWVzdFR5cGUgPT09IHJlcXVlc3RUeXBlOwp9CmZ1bmN0aW9u\ -IGlzUmVxdWVzdCh2KSB7CglpZiAoIXYgfHwgdHlwZW9mIHYgIT09ICJvYmplY3QiKSByZXR1cm4gZmFs\ -c2U7Cgljb25zdCByID0gdjsKCXJldHVybiB0eXBlb2Ygci5yZXF1ZXN0VHlwZSA9PT0gInN0cmluZyIg\ -JiYgaXNJZChyLmlkKTsKfQpmdW5jdGlvbiBjcmVhdGVSZXNwb25zZShpZCQxLCByZXNwb25zZVR5cGUs\ -IGRhdGEpIHsKCXJldHVybiB7CgkJaWQ6IGlkJDEsCgkJdGltZXN0YW1wOiBEYXRlLm5vdygpLAoJCXJl\ -c3BvbnNlVHlwZSwKCQlkYXRhCgl9Owp9CmNvbnN0IHJlc3BvbnNlVHlwZUVycm9yID0gIkVycm9yIjsK\ -ZnVuY3Rpb24gY3JlYXRlRXJyb3JSZXNwb25zZShyZXF1ZXN0LCBtZXNzYWdlLCBlcnJvcikgewoJaWYg\ -KCFpc1JlcXVlc3QocmVxdWVzdCkpIHsKCQljb25zdCByID0gcmVxdWVzdCAmJiB0eXBlb2YgcmVxdWVz\ -dCA9PT0gIm9iamVjdCIgPyByZXF1ZXN0IDoge307CgkJY29uc3QgaWQkMiA9IGlzSWQoci5pZCkgPyBy\ -LmlkIDogTnVsbElEOwoJCXJldHVybiBjcmVhdGVSZXNwb25zZShpZCQyLCByZXNwb25zZVR5cGVFcnJv\ -ciwgewoJCQlyZXF1ZXN0VHlwZTogdHlwZW9mIHIucmVxdWVzdFR5cGUgPT09ICJzdHJpbmciID8gci5y\ -ZXF1ZXN0VHlwZSA6IHZvaWQgMCwKCQkJbWVzc2FnZSwKCQkJZXJyb3I6IHRvRXJyb3IoZXJyb3IpCgkJ\ -fSk7Cgl9Cgljb25zdCB7IGlkOiBpZCQxLCByZXF1ZXN0VHlwZSB9ID0gcmVxdWVzdDsKCXJldHVybiBj\ -cmVhdGVSZXNwb25zZShpZCQxLCByZXNwb25zZVR5cGVFcnJvciwgewoJCXJlcXVlc3RUeXBlLAoJCW1l\ -c3NhZ2UsCgkJZXJyb3I6IHRvRXJyb3IoZXJyb3IpCgl9KTsKfQoKLy8jZW5kcmVnaW9uCi8vI3JlZ2lv\ -biBzcmMvUHJvY2VkdXJlcy9wcm9jRXhlY1JlZ0V4cC50cwpjb25zdCByZXF1ZXN0VHlwZUV4ZWNSZWdF\ -eHAgPSAiRXhlY1JlZ0V4cCI7CmZ1bmN0aW9uIGlzRXhlY1JlZ0V4cFJlcXVlc3QodikgewoJcmV0dXJu\ -IGlzUmVxdWVzdFR5cGUodiwgcmVxdWVzdFR5cGVFeGVjUmVnRXhwKTsKfQpmdW5jdGlvbiBwcm9jRXhl\ -Y1JlZ0V4cChyKSB7CglpZiAoIWlzRXhlY1JlZ0V4cFJlcXVlc3QocikpIHJldHVybiB2b2lkIDA7Cgl0\ -cnkgewoJCWNvbnN0IHJlZ2V4ID0gdG9SZWdFeHAoci5kYXRhLnJlZ2V4cCk7CgkJcmV0dXJuIGNyZWF0\ -ZVJlc3BvbnNlRXhlY1JlZ0V4cChyLCBleGVjUmVnRXhwKHJlZ2V4LCByLmRhdGEudGV4dCkpOwoJfSBj\ -YXRjaCAoZSkgewoJCXJldHVybiBjcmVhdGVFcnJvclJlc3BvbnNlKHIsIGZvcm1hdChlKSk7Cgl9Cn0K\ -ZnVuY3Rpb24gY3JlYXRlUmVzcG9uc2VFeGVjUmVnRXhwKHJlcXVlc3QsIGRhdGEpIHsKCXJldHVybiBj\ -cmVhdGVSZXNwb25zZShyZXF1ZXN0LmlkLCByZXF1ZXN0LnJlcXVlc3RUeXBlLCBkYXRhKTsKfQoKLy8j\ -ZW5kcmVnaW9uCi8vI3JlZ2lvbiBzcmMvUHJvY2VkdXJlcy9wcm9jTWF0Y2hBbGxSZWdFeHAudHMKY29u\ -c3QgcmVxdWVzdFR5cGVNYXRjaEFsbFJlZ0V4cCA9ICJNYXRjaEFsbFJlZ0V4cCI7CmZ1bmN0aW9uIGlz\ -TWF0Y2hBbGxSZWdFeHBSZXF1ZXN0KHYpIHsKCXJldHVybiBpc1JlcXVlc3RUeXBlKHYsIHJlcXVlc3RU\ -eXBlTWF0Y2hBbGxSZWdFeHApOwp9CmZ1bmN0aW9uIHByb2NNYXRjaEFsbFJlZ0V4cChyKSB7CglpZiAo\ -IWlzTWF0Y2hBbGxSZWdFeHBSZXF1ZXN0KHIpKSByZXR1cm4gdm9pZCAwOwoJdHJ5IHsKCQljb25zdCBy\ -ZWdleCA9IHRvUmVnRXhwKHIuZGF0YS5yZWdleHApOwoJCWNvbnN0IHJlZ2V4UmVzdWx0ID0gbWF0Y2hB\ -bGxSZWdFeHAoci5kYXRhLnRleHQsIHJlZ2V4KTsKCQlyZXR1cm4gY3JlYXRlUmVzcG9uc2VNYXRjaEFs\ -bFJlZ0V4cChyLCByZWdleFJlc3VsdCk7Cgl9IGNhdGNoIChlKSB7CgkJcmV0dXJuIGNyZWF0ZUVycm9y\ -UmVzcG9uc2UociwgZm9ybWF0KGUpKTsKCX0KfQpmdW5jdGlvbiBjcmVhdGVSZXNwb25zZU1hdGNoQWxs\ -UmVnRXhwKHJlcXVlc3QsIGRhdGEpIHsKCXJldHVybiBjcmVhdGVSZXNwb25zZShyZXF1ZXN0LmlkLCBy\ -ZXF1ZXN0LnJlcXVlc3RUeXBlLCBkYXRhKTsKfQoKLy8jZW5kcmVnaW9uCi8vI3JlZ2lvbiBzcmMvUHJv\ -Y2VkdXJlcy9wcm9jTWF0Y2hBbGxSZWdFeHBBcnJheS50cwpjb25zdCByZXF1ZXN0VHlwZU1hdGNoUmVn\ -RXhwQXJyYXkgPSAiTWF0Y2hBbGxSZWdFeHBBcnJheSI7CmZ1bmN0aW9uIGlzTWF0Y2hSZWdFeHBBcnJh\ -eVJlcXVlc3QodikgewoJcmV0dXJuIGlzUmVxdWVzdFR5cGUodiwgcmVxdWVzdFR5cGVNYXRjaFJlZ0V4\ -cEFycmF5KTsKfQpmdW5jdGlvbiBwcm9jTWF0Y2hBbGxSZWdFeHBBcnJheShyKSB7CglpZiAoIWlzTWF0\ -Y2hSZWdFeHBBcnJheVJlcXVlc3QocikpIHJldHVybiB2b2lkIDA7Cgl0cnkgewoJCWNvbnN0IHJlZ2V4\ -ID0gci5kYXRhLnJlZ2V4cHMubWFwKChyJDEpID0+IHRvUmVnRXhwKHIkMSkpOwoJCWNvbnN0IHJlZ2V4\ -UmVzdWx0ID0gbWF0Y2hBbGxSZWdFeHBBcnJheShyLmRhdGEudGV4dCwgcmVnZXgpOwoJCXJldHVybiBj\ -cmVhdGVSZXNwb25zZU1hdGNoUmVnRXhwQXJyYXkociwgcmVnZXhSZXN1bHQpOwoJfSBjYXRjaCAoZSkg\ -ewoJCXJldHVybiBjcmVhdGVFcnJvclJlc3BvbnNlKHIsIGZvcm1hdChlKSk7Cgl9Cn0KZnVuY3Rpb24g\ -Y3JlYXRlUmVzcG9uc2VNYXRjaFJlZ0V4cEFycmF5KHJlcXVlc3QsIGRhdGEpIHsKCXJldHVybiBjcmVh\ -dGVSZXNwb25zZShyZXF1ZXN0LmlkLCByZXF1ZXN0LnJlcXVlc3RUeXBlLCBkYXRhKTsKfQoKLy8jZW5k\ -cmVnaW9uCi8vI3JlZ2lvbiBzcmMvUHJvY2VkdXJlcy9wcm9jTWF0Y2hBbGxSZWdFeHBBc1JhbmdlLnRz\ -CmNvbnN0IHJlcXVlc3RUeXBlTWF0Y2hBbGxSZWdFeHBBc1JhbmdlID0gIk1hdGNoQWxsUmVnRXhwQXNS\ -YW5nZSI7CmZ1bmN0aW9uIGlzTWF0Y2hBbGxSZWdFeHBBc1JhbmdlUmVxdWVzdCh2KSB7CglyZXR1cm4g\ -aXNSZXF1ZXN0VHlwZSh2LCByZXF1ZXN0VHlwZU1hdGNoQWxsUmVnRXhwQXNSYW5nZSk7Cn0KZnVuY3Rp\ -b24gcHJvY01hdGNoQWxsUmVnRXhwQXNSYW5nZShyKSB7CglpZiAoIWlzTWF0Y2hBbGxSZWdFeHBBc1Jh\ -bmdlUmVxdWVzdChyKSkgcmV0dXJuIHZvaWQgMDsKCXRyeSB7CgkJY29uc3QgcmVnZXggPSB0b1JlZ0V4\ -cChyLmRhdGEucmVnZXhwKTsKCQljb25zdCByZWdleFJlc3VsdCA9IG1hdGNoQWxsVG9SYW5nZXNSZWdF\ -eHAoci5kYXRhLnRleHQsIHJlZ2V4KTsKCQlyZXR1cm4gY3JlYXRlUmVzcG9uc2VNYXRjaEFsbFJlZ0V4\ -cEFzUmFuZ2UociwgcmVnZXhSZXN1bHQpOwoJfSBjYXRjaCAoZSkgewoJCXJldHVybiBjcmVhdGVFcnJv\ -clJlc3BvbnNlKHIsIGZvcm1hdChlKSk7Cgl9Cn0KZnVuY3Rpb24gY3JlYXRlUmVzcG9uc2VNYXRjaEFs\ -bFJlZ0V4cEFzUmFuZ2UocmVxdWVzdCwgZGF0YSkgewoJcmV0dXJuIGNyZWF0ZVJlc3BvbnNlKHJlcXVl\ -c3QuaWQsIHJlcXVlc3QucmVxdWVzdFR5cGUsIGRhdGEpOwp9CgovLyNlbmRyZWdpb24KLy8jcmVnaW9u\ -IHNyYy9Qcm9jZWR1cmVzL3Byb2NNYXRjaFJlZ0V4cC50cwpjb25zdCByZXF1ZXN0VHlwZU1hdGNoUmVn\ -RXhwID0gIk1hdGNoUmVnRXhwIjsKZnVuY3Rpb24gaXNNYXRjaFJlZ0V4cFJlcXVlc3QodikgewoJcmV0\ -dXJuIGlzUmVxdWVzdFR5cGUodiwgcmVxdWVzdFR5cGVNYXRjaFJlZ0V4cCk7Cn0KZnVuY3Rpb24gcHJv\ -Y01hdGNoUmVnRXhwKHIpIHsKCWlmICghaXNNYXRjaFJlZ0V4cFJlcXVlc3QocikpIHJldHVybiB2b2lk\ -IDA7Cgl0cnkgewoJCWNvbnN0IHJlZ2V4ID0gdG9SZWdFeHAoci5kYXRhLnJlZ2V4cCk7CgkJY29uc3Qg\ -cmVnZXhSZXN1bHQgPSBtYXRjaFJlZ0V4cChyLmRhdGEudGV4dCwgcmVnZXgpOwoJCXJldHVybiBjcmVh\ -dGVSZXNwb25zZU1hdGNoUmVnRXhwKHIsIHJlZ2V4UmVzdWx0KTsKCX0gY2F0Y2ggKGUpIHsKCQlyZXR1\ -cm4gY3JlYXRlRXJyb3JSZXNwb25zZShyLCBmb3JtYXQoZSkpOwoJfQp9CmZ1bmN0aW9uIGNyZWF0ZVJl\ -c3BvbnNlTWF0Y2hSZWdFeHAocmVxdWVzdCwgZGF0YSkgewoJcmV0dXJuIGNyZWF0ZVJlc3BvbnNlKHJl\ -cXVlc3QuaWQsIHJlcXVlc3QucmVxdWVzdFR5cGUsIGRhdGEpOwp9CgovLyNlbmRyZWdpb24KLy8jcmVn\ -aW9uIHNyYy9Qcm9jZWR1cmVzL3Byb2NlZHVyZXMudHMKLyoqCiogQ29sbGVjdGlvbiBvZiBhbGwgcHJv\ -Y2VkdXJlcyByZWxhdGVkIHRvIHJlZ3VsYXIgZXhwcmVzc2lvbnMuCiogT3JkZXIgaXMgaW1wb3J0YW50\ -IGZvciB0aGUgd29ya2VyLCBhcyBpdCBwcm9jZXNzZXMgdGhlbSBzZXF1ZW50aWFsbHkuCiovCmNvbnN0\ -IHByb2NlZHVyZXMgPSBbCglwcm9jTWF0Y2hBbGxSZWdFeHAsCglwcm9jTWF0Y2hBbGxSZWdFeHBBc1Jh\ -bmdlLAoJcHJvY0V4ZWNSZWdFeHAsCglwcm9jTWF0Y2hBbGxSZWdFeHBBcnJheSwKCXByb2NNYXRjaFJl\ -Z0V4cApdOwoKLy8jZW5kcmVnaW9uCi8vI3JlZ2lvbiBzcmMvd29ya2VyL0xvZ0xldmVsLnRzCmxldCBM\ -b2dMZXZlbCA9IC8qIEBfX1BVUkVfXyAqLyBmdW5jdGlvbihMb2dMZXZlbCQxKSB7CglMb2dMZXZlbCQx\ -W0xvZ0xldmVsJDFbIkxvZ0xldmVsTm9uZSJdID0gMF0gPSAiTG9nTGV2ZWxOb25lIjsKCUxvZ0xldmVs\ -JDFbTG9nTGV2ZWwkMVsiTG9nTGV2ZWxFcnJvciJdID0gMV0gPSAiTG9nTGV2ZWxFcnJvciI7CglMb2dM\ -ZXZlbCQxW0xvZ0xldmVsJDFbIkxvZ0xldmVsV2FybiJdID0gMl0gPSAiTG9nTGV2ZWxXYXJuIjsKCUxv\ -Z0xldmVsJDFbTG9nTGV2ZWwkMVsiTG9nTGV2ZWxJbmZvIl0gPSAzXSA9ICJMb2dMZXZlbEluZm8iOwoJ\ -TG9nTGV2ZWwkMVtMb2dMZXZlbCQxWyJMb2dMZXZlbERlYnVnIl0gPSA0XSA9ICJMb2dMZXZlbERlYnVn\ -IjsKCXJldHVybiBMb2dMZXZlbCQxOwp9KHt9KTsKCi8vI2VuZHJlZ2lvbgovLyNyZWdpb24gc3JjL3dv\ -cmtlci9NZXNzYWdlUG9ydC50cwpjb25zdCBudWxsUG9ydCA9IE9iamVjdC5mcmVlemUoewoJcG9zdE1l\ -c3NhZ2UoKSB7fSwKCW9uKCkgewoJCXJldHVybiBudWxsUG9ydDsKCX0sCglvZmYoKSB7CgkJcmV0dXJu\ -IG51bGxQb3J0OwoJfSwKCW9uY2UoKSB7CgkJcmV0dXJuIG51bGxQb3J0OwoJfQp9KTsKCi8vI2VuZHJl\ -Z2lvbgovLyNyZWdpb24gc3JjL3dvcmtlci9Xb3JrZXJNZXNzYWdlSGFuZGxlci50cwp2YXIgV29ya2Vy\ -TWVzc2FnZUhhbmRsZXIgPSBjbGFzcyB7Cglsb2dMZXZlbCA9IExvZ0xldmVsLkxvZ0xldmVsRXJyb3I7\ -CglsaXN0ZW5lcjsKCWNvbnN0cnVjdG9yKHBvcnQsIHByb2NlZHVyZXMkMSkgewoJCXRoaXMucG9ydCA9\ -IHBvcnQ7CgkJdGhpcy5wcm9jZWR1cmVzID0gcHJvY2VkdXJlcyQxOwoJCXRoaXMubGlzdGVuZXIgPSAo\ -dikgPT4gdGhpcy5saXN0ZW5lck1lc3NhZ2Uodik7CgkJcG9ydC5vbigibWVzc2FnZSIsIHRoaXMubGlz\ -dGVuZXIpOwoJfQoJZGlzcG9zZSgpIHsKCQl0aGlzLnBvcnQub2ZmKCJtZXNzYWdlIiwgdGhpcy5saXN0\ -ZW5lcik7CgkJdGhpcy5wb3J0ID0gbnVsbFBvcnQ7Cgl9Cglwb3N0KG1zZykgewoJCXRoaXMubG9nKExv\ -Z0xldmVsLkxvZ0xldmVsRGVidWcsICJQb3N0OiAiICsgSlNPTi5zdHJpbmdpZnkobXNnKSk7CgkJdGhp\ -cy5wb3J0LnBvc3RNZXNzYWdlKG1zZyk7Cgl9Cglsb2cobGV2ZWwsIC4uLnBhcmFtcykgewoJCWlmIChs\ -ZXZlbCA+IHRoaXMubG9nTGV2ZWwpIHJldHVybjsKCQlzd2l0Y2ggKGxldmVsKSB7CgkJCWNhc2UgTG9n\ -TGV2ZWwuTG9nTGV2ZWxFcnJvcjoKCQkJCWNvbnNvbGUuZXJyb3IoLi4ucGFyYW1zKTsKCQkJCWJyZWFr\ -OwoJCQljYXNlIExvZ0xldmVsLkxvZ0xldmVsV2FybjoKCQkJCWNvbnNvbGUud2FybiguLi5wYXJhbXMp\ -OwoJCQkJYnJlYWs7CgkJCWRlZmF1bHQ6IGNvbnNvbGUubG9nKC4uLnBhcmFtcyk7CgkJfQoJfQoJbGlz\ -dGVuZXJNZXNzYWdlKHZhbHVlKSB7CgkJdGhpcy5sb2coTG9nTGV2ZWwuTG9nTGV2ZWxEZWJ1ZywgYG1l\ -c3NhZ2U6ICR7SlNPTi5zdHJpbmdpZnkodmFsdWUpfWApOwoJCWlmICghaXNSZXF1ZXN0KHZhbHVlKSkg\ -ewoJCQljb25zdCBtc2cgPSBgQmFkbHkgZm9ybWVkIFJlcXVlc3Q6ICR7SlNPTi5zdHJpbmdpZnkodmFs\ -dWUpfWA7CgkJCXRoaXMubG9nKExvZ0xldmVsLkxvZ0xldmVsRXJyb3IsIG1zZyk7CgkJCXRoaXMucG9z\ -dChjcmVhdGVFcnJvclJlc3BvbnNlKHZhbHVlLCBtc2cpKTsKCQkJcmV0dXJuOwoJCX0KCQljb25zdCBy\ -ZXF1ZXN0ID0gdmFsdWU7CgkJZm9yIChjb25zdCBwcm9jIG9mIHRoaXMucHJvY2VkdXJlcykgdHJ5IHsK\ -CQkJY29uc3QgcmVzcG9uc2UgPSBwcm9jKHJlcXVlc3QpOwoJCQlpZiAocmVzcG9uc2UgIT09IHZvaWQg\ -MCkgewoJCQkJUHJvbWlzZS5yZXNvbHZlKHJlc3BvbnNlKS5jYXRjaCgocmVhc29uKSA9PiBjcmVhdGVF\ -cnJvclJlc3BvbnNlKHJlcXVlc3QsIFN0cmluZyhyZWFzb24pLCB0b0Vycm9yKHJlYXNvbikpKS50aGVu\ -KChyKSA9PiB0aGlzLnBvc3QocikpLmNhdGNoKCgpID0+IHt9KTsKCQkJCXJldHVybjsKCQkJfQoJCX0g\ -Y2F0Y2ggKGUpIHsKCQkJY29uc3QgbXNnID0gaXNFcnJvckxpa2UoZSkgPyBlLm1lc3NhZ2UgOiBmb3Jt\ -YXQoZSk7CgkJCXRoaXMucG9zdChjcmVhdGVFcnJvclJlc3BvbnNlKHJlcXVlc3QsIG1zZywgaXNFcnJv\ -ckxpa2UoZSkgPyBlIDogdm9pZCAwKSk7CgkJCXJldHVybjsKCQl9CgkJdGhpcy5sb2coTG9nTGV2ZWwu\ -TG9nTGV2ZWxXYXJuLCBgVW5oYW5kbGVkIFJlcXVlc3QgIiR7dmFsdWUucmVxdWVzdFR5cGV9ImApOwoJ\ -CXRoaXMucG9zdChjcmVhdGVFcnJvclJlc3BvbnNlKHJlcXVlc3QsICJVbmhhbmRsZWQgUmVxdWVzdCIp\ -KTsKCX0KfTsKZnVuY3Rpb24gY3JlYXRlSGFuZGxlcihwb3J0LCBwcm9jZWR1cmVzJDEpIHsKCXJldHVy\ -biBuZXcgV29ya2VyTWVzc2FnZUhhbmRsZXIocG9ydCwgcHJvY2VkdXJlcyQxKTsKfQoKLy8jZW5kcmVn\ -aW9uCi8vI3JlZ2lvbiBzcmMvd29ya2VyL3dvcmtlckNvZGVOb2RlLnRzCmlmICghaXNNYWluVGhyZWFk\ -ICYmIHBhcmVudFBvcnQpIHsKCWNvbnN0IGhhbmRsZXIgPSBjcmVhdGVIYW5kbGVyKHBhcmVudFBvcnQs\ -IHByb2NlZHVyZXMpOwoJcGFyZW50UG9ydC5vbmNlKCJjbG9zZSIsICgpID0+IGhhbmRsZXIuZGlzcG9z\ -ZSgpKTsKfQoKLy8jZW5kcmVnaW9u\ +dHMKCX07Cn0KLyoqCiogQ29udmVydHMgYSBSZWdFeHAgb3IgUmVnRXhwTGlrZSBvYmplY3QgdG8gYSBS\ +ZWdFeHAgb2JqZWN0LgoqIElmIHRoZSBpbnB1dCBpcyBhIFJlZ0V4cCwgaXQgaXMgcmV0dXJuZWQgYXMg\ +aXMuCiogSWYgdGhlIGlucHV0IGlzIGEgUmVnRXhwTGlrZSBvYmplY3QsIGl0IHNob3VsZCBoYXZlIGBz\ +b3VyY2VgIGFuZCBgZmxhZ3NgIHByb3BlcnRpZXMuCiogVGhlIGB4YCBmbGFnIGlzIE5PVCBzdXBwb3J0\ +ZWQgYW5kIHdpbGwgdGhyb3cgYW4gZXJyb3IgaWYgcHJlc2VudC4KKiBAcGFyYW0gcmVnZXhwCiogQHBh\ +cmFtIGRlZmF1bHRGbGFncwoqIEByZXR1cm5zCiovCmZ1bmN0aW9uIHRvUmVnRXhwKHJlZ2V4cCkgewoJ\ +aWYgKHJlZ2V4cCBpbnN0YW5jZW9mIFJlZ0V4cCkgcmV0dXJuIHJlZ2V4cDsKCWNvbnN0IHsgc291cmNl\ +LCBmbGFncyB9ID0gcmVnZXhwOwoJaWYgKHR5cGVvZiBzb3VyY2UgIT09ICJzdHJpbmciKSB0aHJvdyBu\ +ZXcgVHlwZUVycm9yKCJJbnZhbGlkIFJlZ0V4cC4iKTsKCWNvbnN0IHJlZyA9IG5ldyBSZWdFeHAoc291\ +cmNlLCBmbGFncyk7CglpZiAocmVnZXhwLmxhc3RJbmRleCAhPT0gdm9pZCAwKSByZWcubGFzdEluZGV4\ +ID0gcmVnZXhwLmxhc3RJbmRleDsKCXJldHVybiByZWc7Cn0KZnVuY3Rpb24gZG9FeGVjUmVnRXhwKHJl\ +Z0V4cCwgdGV4dCkgewoJcmV0dXJuIHJlZ0V4cC5leGVjKHRleHQpOwp9CmZ1bmN0aW9uIGRvTWF0Y2hB\ +bGxSZWdFeHAocmVnRXhwLCB0ZXh0KSB7CglyZXR1cm4gcmVnRXhwW1N5bWJvbC5tYXRjaEFsbF0odGV4\ +dCk7Cn0KZnVuY3Rpb24gZG9NYXRjaFJlZ0V4cChyZWdFeHAsIHRleHQpIHsKCXJldHVybiB0ZXh0Lm1h\ +dGNoKHJlZ0V4cCk7Cn0KZnVuY3Rpb24gdG9SYW5nZXMocikgewoJY29uc3QgcmFuZ2VzID0gbmV3IFVp\ +bnQzMkFycmF5KHIubWF0Y2hlcy5sZW5ndGggKiAyKTsKCWxldCBpID0gMDsKCWZvciAoY29uc3QgbSBv\ +ZiByLm1hdGNoZXMpIHsKCQljb25zdCBpbmRleCA9IG0uaW5kZXggfHwgMDsKCQlyYW5nZXNbaSsrXSA9\ +IGluZGV4OwoJCXJhbmdlc1tpKytdID0gaW5kZXggKyBtWzBdLmxlbmd0aDsKCX0KCXJldHVybiB7CgkJ\ +ZWxhcHNlZFRpbWVNczogci5lbGFwc2VkVGltZU1zLAoJCXJhbmdlcwoJfTsKfQpmdW5jdGlvbiBtYXRj\ +aEFsbFRvUmFuZ2VzUmVnRXhwKHRleHQsIHJlZ0V4cCkgewoJcmV0dXJuIHRvUmFuZ2VzKG1hdGNoQWxs\ +UmVnRXhwKHRleHQsIHJlZ0V4cCkpOwp9CgovLyNlbmRyZWdpb24KLy8jcmVnaW9uIHNyYy91dGlsL2Zv\ +cm1hdC50cwpmdW5jdGlvbiBmb3JtYXQodmFsdWUpIHsKCWNvbnN0IHZpc2l0ZWQgPSAvKiBAX19QVVJF\ +X18gKi8gbmV3IFNldCgpOwoJZnVuY3Rpb24gX2Zvcm1hdCh2YWx1ZSQxKSB7CgkJaWYgKHZpc2l0ZWQu\ +aGFzKHZhbHVlJDEpKSByZXR1cm4gIltDaXJjdWxhcl0iOwoJCXZpc2l0ZWQuYWRkKHZhbHVlJDEpOwoJ\ +CXN3aXRjaCAodHlwZW9mIHZhbHVlJDEpIHsKCQkJY2FzZSAiYmlnaW50IjogcmV0dXJuIHZhbHVlJDEu\ +dG9TdHJpbmcoKSArICJuIjsKCQkJY2FzZSAic3ltYm9sIjogcmV0dXJuIHZhbHVlJDEudG9TdHJpbmco\ +KTsKCQkJY2FzZSAiZnVuY3Rpb24iOiByZXR1cm4gdmFsdWUkMS5uYW1lID8gYFtGdW5jdGlvbjogJHt2\ +YWx1ZSQxLm5hbWV9XWAgOiAiW0Z1bmN0aW9uXSI7CgkJCWNhc2UgInVuZGVmaW5lZCI6IHJldHVybiAi\ +dW5kZWZpbmVkIjsKCQkJY2FzZSAib2JqZWN0IjogcmV0dXJuIG9ialRvU3RyaW5nKHZhbHVlJDEpOwoJ\ +CQljYXNlICJzdHJpbmciOiByZXR1cm4gSlNPTi5zdHJpbmdpZnkodmFsdWUkMSk7CgkJfQoJCXJldHVy\ +biBTdHJpbmcodmFsdWUkMSk7Cgl9CglmdW5jdGlvbiBvYmpUb1N0cmluZyh2YWx1ZSQxKSB7CgkJdHJ5\ +IHsKCQkJaWYgKHZhbHVlJDEgPT09IG51bGwpIHJldHVybiAibnVsbCI7CgkJCWlmICh2YWx1ZSQxIGlu\ +c3RhbmNlb2YgRXJyb3IpIHJldHVybiBgRXJyb3I6ICR7dmFsdWUkMS5tZXNzYWdlfVxuU3RhY2s6ICR7\ +dmFsdWUkMS5zdGFja31gOwoJCQlpZiAoQXJyYXkuaXNBcnJheSh2YWx1ZSQxKSkgcmV0dXJuIGBbJHt2\ +YWx1ZSQxLm1hcChfZm9ybWF0KS5qb2luKCIsICIpfV1gOwoJCQlpZiAoInRvU3RyaW5nIiBpbiB2YWx1\ +ZSQxICYmIHR5cGVvZiB2YWx1ZSQxWyJ0b1N0cmluZyJdID09PSAiZnVuY3Rpb24iICYmIE9iamVjdC5w\ +cm90b3R5cGUudG9TdHJpbmcgIT09IHZhbHVlJDFbInRvU3RyaW5nIl0pIHJldHVybiB2YWx1ZSQxWyJ0\ +b1N0cmluZyJdKCk7CgkJCWNvbnN0IHMgPSBPYmplY3QuZW50cmllcyh2YWx1ZSQxKS5tYXAoKFtrZXks\ +IHZhbF0pID0+IGAke2tleX06ICR7X2Zvcm1hdCh2YWwpfWApLmpvaW4oIiwgIik7CgkJCXJldHVybiBg\ +eyR7cyA/IGAgJHtzfSBgIDogIiJ9fWA7CgkJfSBjYXRjaCAoZSkgewoJCQlyZXR1cm4gU3RyaW5nKGUp\ +OwoJCX0KCX0KCXJldHVybiBfZm9ybWF0KHZhbHVlKTsKfQoKLy8jZW5kcmVnaW9uCi8vI3JlZ2lvbiBz\ +cmMvaGVscGVycy9lcnJvcnMudHMKY29uc3QgYWxsb3dTdHJpbmdPclVuZGVmaW5lZCA9IHsKCXN0cmlu\ +ZzogdHJ1ZSwKCXVuZGVmaW5lZDogdHJ1ZQp9OwpmdW5jdGlvbiBpc0Vycm9yTGlrZShlKSB7CglpZiAo\ +ZSBpbnN0YW5jZW9mIEVycm9yKSByZXR1cm4gdHJ1ZTsKCWlmICghZSB8fCB0eXBlb2YgZSAhPT0gIm9i\ +amVjdCIpIHJldHVybiBmYWxzZTsKCWNvbnN0IGV4ID0gZTsKCXJldHVybiB0eXBlb2YgZXgubmFtZSA9\ +PSAic3RyaW5nIiAmJiB0eXBlb2YgZXgubWVzc2FnZSA9PSAic3RyaW5nIiAmJiB0eXBlb2YgZXguc3Rh\ +Y2sgaW4gYWxsb3dTdHJpbmdPclVuZGVmaW5lZDsKfQpmdW5jdGlvbiB0b0Vycm9yKGUpIHsKCWlmIChl\ +IGluc3RhbmNlb2YgRXJyb3IpIHJldHVybiBlOwoJaWYgKGlzRXJyb3JMaWtlKGUpKSByZXR1cm4gbmV3\ +IEVycm9yKGUubWVzc2FnZSB8fCAiVW5rbm93biBlcnJvciIsIHsgY2F1c2U6IGUgfSk7CglpZiAodHlw\ +ZW9mIGUgPT09ICJzdHJpbmciKSByZXR1cm4gbmV3IEVycm9yKGUpOwoJcmV0dXJuIG5ldyBFcnJvcihT\ +dHJpbmcoZSksIHsgY2F1c2U6IGUgfSk7Cn0KCi8vI2VuZHJlZ2lvbgovLyNyZWdpb24gc3JjL1Byb2Nl\ +ZHVyZXMvdW5pcXVlSWQudHMKbGV0IGlkID0gdHQoKVswXTsKZnVuY3Rpb24gdHQoKSB7Cgljb25zdCBu\ +ID0gcGVyZm9ybWFuY2Uubm93KCk7Cgljb25zdCBzID0gTWF0aC5mbG9vcihuIC8gMWUzKTsKCXJldHVy\ +biBbcywgTWF0aC5mbG9vcigobiAtIHMgKiAxZTMpICogMWU2KV07Cn0KZnVuY3Rpb24gaXNJZCh2YWx1\ +ZSkgewoJaWYgKHR5cGVvZiB2YWx1ZSA9PT0gInN0cmluZyIgJiYgL15JRC1cZCstXGQrLVxkKy1cZCsk\ +Ly50ZXN0KHZhbHVlKSkgewoJCWNvbnN0IFthLCBiLCBjLCBkXSA9IHZhbHVlLnNwbGl0KCItIikuc2xp\ +Y2UoMSkubWFwKCh2KSA9PiBOdW1iZXIucGFyc2VJbnQodiwgMTApKTsKCQlyZXR1cm4gYSA9PT0gYiAl\ +IDEzICYmIGQgPT09IChiICsgYykgJSA3OwoJfQoJcmV0dXJuIGZhbHNlOwp9CmNvbnN0IE51bGxJRCA9\ +ICJJRC0wLTAtMC0wIjsKCi8vI2VuZHJlZ2lvbgovLyNyZWdpb24gc3JjL1Byb2NlZHVyZXMvcHJvY2Vk\ +dXJlLnRzCmZ1bmN0aW9uIGlzUmVxdWVzdFR5cGUodiwgcmVxdWVzdFR5cGUpIHsKCXJldHVybiBpc1Jl\ +cXVlc3QodikgJiYgdi5yZXF1ZXN0VHlwZSA9PT0gcmVxdWVzdFR5cGU7Cn0KZnVuY3Rpb24gaXNSZXF1\ +ZXN0KHYpIHsKCWlmICghdiB8fCB0eXBlb2YgdiAhPT0gIm9iamVjdCIpIHJldHVybiBmYWxzZTsKCWNv\ +bnN0IHIgPSB2OwoJcmV0dXJuIHR5cGVvZiByLnJlcXVlc3RUeXBlID09PSAic3RyaW5nIiAmJiBpc0lk\ +KHIuaWQpOwp9CmZ1bmN0aW9uIGNyZWF0ZVJlc3BvbnNlKGlkJDEsIHJlc3BvbnNlVHlwZSwgZGF0YSkg\ +ewoJcmV0dXJuIHsKCQlpZDogaWQkMSwKCQl0aW1lc3RhbXA6IERhdGUubm93KCksCgkJcmVzcG9uc2VU\ +eXBlLAoJCWRhdGEKCX07Cn0KY29uc3QgcmVzcG9uc2VUeXBlRXJyb3IgPSAiRXJyb3IiOwpmdW5jdGlv\ +biBjcmVhdGVFcnJvclJlc3BvbnNlKHJlcXVlc3QsIG1lc3NhZ2UsIGVycm9yKSB7CglpZiAoIWlzUmVx\ +dWVzdChyZXF1ZXN0KSkgewoJCWNvbnN0IHIgPSByZXF1ZXN0ICYmIHR5cGVvZiByZXF1ZXN0ID09PSAi\ +b2JqZWN0IiA/IHJlcXVlc3QgOiB7fTsKCQljb25zdCBpZCQyID0gaXNJZChyLmlkKSA/IHIuaWQgOiBO\ +dWxsSUQ7CgkJcmV0dXJuIGNyZWF0ZVJlc3BvbnNlKGlkJDIsIHJlc3BvbnNlVHlwZUVycm9yLCB7CgkJ\ +CXJlcXVlc3RUeXBlOiB0eXBlb2Ygci5yZXF1ZXN0VHlwZSA9PT0gInN0cmluZyIgPyByLnJlcXVlc3RU\ +eXBlIDogdm9pZCAwLAoJCQltZXNzYWdlLAoJCQllcnJvcjogdG9FcnJvcihlcnJvcikKCQl9KTsKCX0K\ +CWNvbnN0IHsgaWQ6IGlkJDEsIHJlcXVlc3RUeXBlIH0gPSByZXF1ZXN0OwoJcmV0dXJuIGNyZWF0ZVJl\ +c3BvbnNlKGlkJDEsIHJlc3BvbnNlVHlwZUVycm9yLCB7CgkJcmVxdWVzdFR5cGUsCgkJbWVzc2FnZSwK\ +CQllcnJvcjogdG9FcnJvcihlcnJvcikKCX0pOwp9CgovLyNlbmRyZWdpb24KLy8jcmVnaW9uIHNyYy9Q\ +cm9jZWR1cmVzL3Byb2NFeGVjUmVnRXhwLnRzCmNvbnN0IHJlcXVlc3RUeXBlRXhlY1JlZ0V4cCA9ICJF\ +eGVjUmVnRXhwIjsKZnVuY3Rpb24gaXNFeGVjUmVnRXhwUmVxdWVzdCh2KSB7CglyZXR1cm4gaXNSZXF1\ +ZXN0VHlwZSh2LCByZXF1ZXN0VHlwZUV4ZWNSZWdFeHApOwp9CmZ1bmN0aW9uIHByb2NFeGVjUmVnRXhw\ +KHIpIHsKCWlmICghaXNFeGVjUmVnRXhwUmVxdWVzdChyKSkgcmV0dXJuIHZvaWQgMDsKCXRyeSB7CgkJ\ +Y29uc3QgcmVnZXggPSB0b1JlZ0V4cChyLmRhdGEucmVnZXhwKTsKCQlyZXR1cm4gY3JlYXRlUmVzcG9u\ +c2VFeGVjUmVnRXhwKHIsIGV4ZWNSZWdFeHAocmVnZXgsIHIuZGF0YS50ZXh0KSk7Cgl9IGNhdGNoIChl\ +KSB7CgkJcmV0dXJuIGNyZWF0ZUVycm9yUmVzcG9uc2UociwgZm9ybWF0KGUpKTsKCX0KfQpmdW5jdGlv\ +biBjcmVhdGVSZXNwb25zZUV4ZWNSZWdFeHAocmVxdWVzdCwgZGF0YSkgewoJcmV0dXJuIGNyZWF0ZVJl\ +c3BvbnNlKHJlcXVlc3QuaWQsIHJlcXVlc3QucmVxdWVzdFR5cGUsIGRhdGEpOwp9CgovLyNlbmRyZWdp\ +b24KLy8jcmVnaW9uIHNyYy9Qcm9jZWR1cmVzL3Byb2NNYXRjaEFsbFJlZ0V4cC50cwpjb25zdCByZXF1\ +ZXN0VHlwZU1hdGNoQWxsUmVnRXhwID0gIk1hdGNoQWxsUmVnRXhwIjsKZnVuY3Rpb24gaXNNYXRjaEFs\ +bFJlZ0V4cFJlcXVlc3QodikgewoJcmV0dXJuIGlzUmVxdWVzdFR5cGUodiwgcmVxdWVzdFR5cGVNYXRj\ +aEFsbFJlZ0V4cCk7Cn0KZnVuY3Rpb24gcHJvY01hdGNoQWxsUmVnRXhwKHIpIHsKCWlmICghaXNNYXRj\ +aEFsbFJlZ0V4cFJlcXVlc3QocikpIHJldHVybiB2b2lkIDA7Cgl0cnkgewoJCWNvbnN0IHJlZ2V4ID0g\ +dG9SZWdFeHAoci5kYXRhLnJlZ2V4cCk7CgkJY29uc3QgcmVnZXhSZXN1bHQgPSBtYXRjaEFsbFJlZ0V4\ +cChyLmRhdGEudGV4dCwgcmVnZXgpOwoJCXJldHVybiBjcmVhdGVSZXNwb25zZU1hdGNoQWxsUmVnRXhw\ +KHIsIHJlZ2V4UmVzdWx0KTsKCX0gY2F0Y2ggKGUpIHsKCQlyZXR1cm4gY3JlYXRlRXJyb3JSZXNwb25z\ +ZShyLCBmb3JtYXQoZSkpOwoJfQp9CmZ1bmN0aW9uIGNyZWF0ZVJlc3BvbnNlTWF0Y2hBbGxSZWdFeHAo\ +cmVxdWVzdCwgZGF0YSkgewoJcmV0dXJuIGNyZWF0ZVJlc3BvbnNlKHJlcXVlc3QuaWQsIHJlcXVlc3Qu\ +cmVxdWVzdFR5cGUsIGRhdGEpOwp9CgovLyNlbmRyZWdpb24KLy8jcmVnaW9uIHNyYy9Qcm9jZWR1cmVz\ +L3Byb2NNYXRjaEFsbFJlZ0V4cEFycmF5LnRzCmNvbnN0IHJlcXVlc3RUeXBlTWF0Y2hSZWdFeHBBcnJh\ +eSA9ICJNYXRjaEFsbFJlZ0V4cEFycmF5IjsKZnVuY3Rpb24gaXNNYXRjaFJlZ0V4cEFycmF5UmVxdWVz\ +dCh2KSB7CglyZXR1cm4gaXNSZXF1ZXN0VHlwZSh2LCByZXF1ZXN0VHlwZU1hdGNoUmVnRXhwQXJyYXkp\ +Owp9CmZ1bmN0aW9uIHByb2NNYXRjaEFsbFJlZ0V4cEFycmF5KHIpIHsKCWlmICghaXNNYXRjaFJlZ0V4\ +cEFycmF5UmVxdWVzdChyKSkgcmV0dXJuIHZvaWQgMDsKCXRyeSB7CgkJY29uc3QgcmVnZXggPSByLmRh\ +dGEucmVnZXhwcy5tYXAoKHIkMSkgPT4gdG9SZWdFeHAociQxKSk7CgkJY29uc3QgcmVnZXhSZXN1bHQg\ +PSBtYXRjaEFsbFJlZ0V4cEFycmF5KHIuZGF0YS50ZXh0LCByZWdleCk7CgkJcmV0dXJuIGNyZWF0ZVJl\ +c3BvbnNlTWF0Y2hSZWdFeHBBcnJheShyLCByZWdleFJlc3VsdCk7Cgl9IGNhdGNoIChlKSB7CgkJcmV0\ +dXJuIGNyZWF0ZUVycm9yUmVzcG9uc2UociwgZm9ybWF0KGUpKTsKCX0KfQpmdW5jdGlvbiBjcmVhdGVS\ +ZXNwb25zZU1hdGNoUmVnRXhwQXJyYXkocmVxdWVzdCwgZGF0YSkgewoJcmV0dXJuIGNyZWF0ZVJlc3Bv\ +bnNlKHJlcXVlc3QuaWQsIHJlcXVlc3QucmVxdWVzdFR5cGUsIGRhdGEpOwp9CgovLyNlbmRyZWdpb24K\ +Ly8jcmVnaW9uIHNyYy9Qcm9jZWR1cmVzL3Byb2NNYXRjaEFsbFJlZ0V4cEFzUmFuZ2UudHMKY29uc3Qg\ +cmVxdWVzdFR5cGVNYXRjaEFsbFJlZ0V4cEFzUmFuZ2UgPSAiTWF0Y2hBbGxSZWdFeHBBc1JhbmdlIjsK\ +ZnVuY3Rpb24gaXNNYXRjaEFsbFJlZ0V4cEFzUmFuZ2VSZXF1ZXN0KHYpIHsKCXJldHVybiBpc1JlcXVl\ +c3RUeXBlKHYsIHJlcXVlc3RUeXBlTWF0Y2hBbGxSZWdFeHBBc1JhbmdlKTsKfQpmdW5jdGlvbiBwcm9j\ +TWF0Y2hBbGxSZWdFeHBBc1JhbmdlKHIpIHsKCWlmICghaXNNYXRjaEFsbFJlZ0V4cEFzUmFuZ2VSZXF1\ +ZXN0KHIpKSByZXR1cm4gdm9pZCAwOwoJdHJ5IHsKCQljb25zdCByZWdleCA9IHRvUmVnRXhwKHIuZGF0\ +YS5yZWdleHApOwoJCWNvbnN0IHJlZ2V4UmVzdWx0ID0gbWF0Y2hBbGxUb1Jhbmdlc1JlZ0V4cChyLmRh\ +dGEudGV4dCwgcmVnZXgpOwoJCXJldHVybiBjcmVhdGVSZXNwb25zZU1hdGNoQWxsUmVnRXhwQXNSYW5n\ +ZShyLCByZWdleFJlc3VsdCk7Cgl9IGNhdGNoIChlKSB7CgkJcmV0dXJuIGNyZWF0ZUVycm9yUmVzcG9u\ +c2UociwgZm9ybWF0KGUpKTsKCX0KfQpmdW5jdGlvbiBjcmVhdGVSZXNwb25zZU1hdGNoQWxsUmVnRXhw\ +QXNSYW5nZShyZXF1ZXN0LCBkYXRhKSB7CglyZXR1cm4gY3JlYXRlUmVzcG9uc2UocmVxdWVzdC5pZCwg\ +cmVxdWVzdC5yZXF1ZXN0VHlwZSwgZGF0YSk7Cn0KCi8vI2VuZHJlZ2lvbgovLyNyZWdpb24gc3JjL1By\ +b2NlZHVyZXMvcHJvY01hdGNoUmVnRXhwLnRzCmNvbnN0IHJlcXVlc3RUeXBlTWF0Y2hSZWdFeHAgPSAi\ +TWF0Y2hSZWdFeHAiOwpmdW5jdGlvbiBpc01hdGNoUmVnRXhwUmVxdWVzdCh2KSB7CglyZXR1cm4gaXNS\ +ZXF1ZXN0VHlwZSh2LCByZXF1ZXN0VHlwZU1hdGNoUmVnRXhwKTsKfQpmdW5jdGlvbiBwcm9jTWF0Y2hS\ +ZWdFeHAocikgewoJaWYgKCFpc01hdGNoUmVnRXhwUmVxdWVzdChyKSkgcmV0dXJuIHZvaWQgMDsKCXRy\ +eSB7CgkJY29uc3QgcmVnZXggPSB0b1JlZ0V4cChyLmRhdGEucmVnZXhwKTsKCQljb25zdCByZWdleFJl\ +c3VsdCA9IG1hdGNoUmVnRXhwKHIuZGF0YS50ZXh0LCByZWdleCk7CgkJcmV0dXJuIGNyZWF0ZVJlc3Bv\ +bnNlTWF0Y2hSZWdFeHAociwgcmVnZXhSZXN1bHQpOwoJfSBjYXRjaCAoZSkgewoJCXJldHVybiBjcmVh\ +dGVFcnJvclJlc3BvbnNlKHIsIGZvcm1hdChlKSk7Cgl9Cn0KZnVuY3Rpb24gY3JlYXRlUmVzcG9uc2VN\ +YXRjaFJlZ0V4cChyZXF1ZXN0LCBkYXRhKSB7CglyZXR1cm4gY3JlYXRlUmVzcG9uc2UocmVxdWVzdC5p\ +ZCwgcmVxdWVzdC5yZXF1ZXN0VHlwZSwgZGF0YSk7Cn0KCi8vI2VuZHJlZ2lvbgovLyNyZWdpb24gc3Jj\ +L1Byb2NlZHVyZXMvcHJvY2VkdXJlcy50cwovKioKKiBDb2xsZWN0aW9uIG9mIGFsbCBwcm9jZWR1cmVz\ +IHJlbGF0ZWQgdG8gcmVndWxhciBleHByZXNzaW9ucy4KKiBPcmRlciBpcyBpbXBvcnRhbnQgZm9yIHRo\ +ZSB3b3JrZXIsIGFzIGl0IHByb2Nlc3NlcyB0aGVtIHNlcXVlbnRpYWxseS4KKi8KY29uc3QgcHJvY2Vk\ +dXJlcyA9IFsKCXByb2NNYXRjaEFsbFJlZ0V4cCwKCXByb2NNYXRjaEFsbFJlZ0V4cEFzUmFuZ2UsCglw\ +cm9jRXhlY1JlZ0V4cCwKCXByb2NNYXRjaEFsbFJlZ0V4cEFycmF5LAoJcHJvY01hdGNoUmVnRXhwCl07\ +CgovLyNlbmRyZWdpb24KLy8jcmVnaW9uIHNyYy93b3JrZXIvTG9nTGV2ZWwudHMKbGV0IExvZ0xldmVs\ +ID0gLyogQF9fUFVSRV9fICovIGZ1bmN0aW9uKExvZ0xldmVsJDEpIHsKCUxvZ0xldmVsJDFbTG9nTGV2\ +ZWwkMVsiTG9nTGV2ZWxOb25lIl0gPSAwXSA9ICJMb2dMZXZlbE5vbmUiOwoJTG9nTGV2ZWwkMVtMb2dM\ +ZXZlbCQxWyJMb2dMZXZlbEVycm9yIl0gPSAxXSA9ICJMb2dMZXZlbEVycm9yIjsKCUxvZ0xldmVsJDFb\ +TG9nTGV2ZWwkMVsiTG9nTGV2ZWxXYXJuIl0gPSAyXSA9ICJMb2dMZXZlbFdhcm4iOwoJTG9nTGV2ZWwk\ +MVtMb2dMZXZlbCQxWyJMb2dMZXZlbEluZm8iXSA9IDNdID0gIkxvZ0xldmVsSW5mbyI7CglMb2dMZXZl\ +bCQxW0xvZ0xldmVsJDFbIkxvZ0xldmVsRGVidWciXSA9IDRdID0gIkxvZ0xldmVsRGVidWciOwoJcmV0\ +dXJuIExvZ0xldmVsJDE7Cn0oe30pOwoKLy8jZW5kcmVnaW9uCi8vI3JlZ2lvbiBzcmMvd29ya2VyL01l\ +c3NhZ2VQb3J0LnRzCmNvbnN0IG51bGxQb3J0ID0gT2JqZWN0LmZyZWV6ZSh7Cglwb3N0TWVzc2FnZSgp\ +IHt9LAoJb24oKSB7CgkJcmV0dXJuIG51bGxQb3J0OwoJfSwKCW9mZigpIHsKCQlyZXR1cm4gbnVsbFBv\ +cnQ7Cgl9LAoJb25jZSgpIHsKCQlyZXR1cm4gbnVsbFBvcnQ7Cgl9Cn0pOwoKLy8jZW5kcmVnaW9uCi8v\ +I3JlZ2lvbiBzcmMvd29ya2VyL1dvcmtlck1lc3NhZ2VIYW5kbGVyLnRzCnZhciBXb3JrZXJNZXNzYWdl\ +SGFuZGxlciA9IGNsYXNzIHsKCWxvZ0xldmVsID0gTG9nTGV2ZWwuTG9nTGV2ZWxFcnJvcjsKCWxpc3Rl\ +bmVyOwoJY29uc3RydWN0b3IocG9ydCwgcHJvY2VkdXJlcyQxKSB7CgkJdGhpcy5wb3J0ID0gcG9ydDsK\ +CQl0aGlzLnByb2NlZHVyZXMgPSBwcm9jZWR1cmVzJDE7CgkJdGhpcy5saXN0ZW5lciA9ICh2KSA9PiB0\ +aGlzLmxpc3RlbmVyTWVzc2FnZSh2KTsKCQlwb3J0Lm9uKCJtZXNzYWdlIiwgdGhpcy5saXN0ZW5lcik7\ +Cgl9CglkaXNwb3NlKCkgewoJCXRoaXMucG9ydC5vZmYoIm1lc3NhZ2UiLCB0aGlzLmxpc3RlbmVyKTsK\ +CQl0aGlzLnBvcnQgPSBudWxsUG9ydDsKCX0KCXBvc3QobXNnKSB7CgkJdGhpcy5sb2coTG9nTGV2ZWwu\ +TG9nTGV2ZWxEZWJ1ZywgIlBvc3Q6ICIgKyBKU09OLnN0cmluZ2lmeShtc2cpKTsKCQl0aGlzLnBvcnQu\ +cG9zdE1lc3NhZ2UobXNnKTsKCX0KCWxvZyhsZXZlbCwgLi4ucGFyYW1zKSB7CgkJaWYgKGxldmVsID4g\ +dGhpcy5sb2dMZXZlbCkgcmV0dXJuOwoJCXN3aXRjaCAobGV2ZWwpIHsKCQkJY2FzZSBMb2dMZXZlbC5M\ +b2dMZXZlbEVycm9yOgoJCQkJY29uc29sZS5lcnJvciguLi5wYXJhbXMpOwoJCQkJYnJlYWs7CgkJCWNh\ +c2UgTG9nTGV2ZWwuTG9nTGV2ZWxXYXJuOgoJCQkJY29uc29sZS53YXJuKC4uLnBhcmFtcyk7CgkJCQli\ +cmVhazsKCQkJZGVmYXVsdDogY29uc29sZS5sb2coLi4ucGFyYW1zKTsKCQl9Cgl9CglsaXN0ZW5lck1l\ +c3NhZ2UodmFsdWUpIHsKCQl0aGlzLmxvZyhMb2dMZXZlbC5Mb2dMZXZlbERlYnVnLCBgbWVzc2FnZTog\ +JHtKU09OLnN0cmluZ2lmeSh2YWx1ZSl9YCk7CgkJaWYgKCFpc1JlcXVlc3QodmFsdWUpKSB7CgkJCWNv\ +bnN0IG1zZyA9IGBCYWRseSBmb3JtZWQgUmVxdWVzdDogJHtKU09OLnN0cmluZ2lmeSh2YWx1ZSl9YDsK\ +CQkJdGhpcy5sb2coTG9nTGV2ZWwuTG9nTGV2ZWxFcnJvciwgbXNnKTsKCQkJdGhpcy5wb3N0KGNyZWF0\ +ZUVycm9yUmVzcG9uc2UodmFsdWUsIG1zZykpOwoJCQlyZXR1cm47CgkJfQoJCWNvbnN0IHJlcXVlc3Qg\ +PSB2YWx1ZTsKCQlmb3IgKGNvbnN0IHByb2Mgb2YgdGhpcy5wcm9jZWR1cmVzKSB0cnkgewoJCQljb25z\ +dCByZXNwb25zZSA9IHByb2MocmVxdWVzdCk7CgkJCWlmIChyZXNwb25zZSAhPT0gdm9pZCAwKSB7CgkJ\ +CQlQcm9taXNlLnJlc29sdmUocmVzcG9uc2UpLmNhdGNoKChyZWFzb24pID0+IGNyZWF0ZUVycm9yUmVz\ +cG9uc2UocmVxdWVzdCwgU3RyaW5nKHJlYXNvbiksIHRvRXJyb3IocmVhc29uKSkpLnRoZW4oKHIpID0+\ +IHRoaXMucG9zdChyKSkuY2F0Y2goKCkgPT4ge30pOwoJCQkJcmV0dXJuOwoJCQl9CgkJfSBjYXRjaCAo\ +ZSkgewoJCQljb25zdCBtc2cgPSBpc0Vycm9yTGlrZShlKSA/IGUubWVzc2FnZSA6IGZvcm1hdChlKTsK\ +CQkJdGhpcy5wb3N0KGNyZWF0ZUVycm9yUmVzcG9uc2UocmVxdWVzdCwgbXNnLCBpc0Vycm9yTGlrZShl\ +KSA/IGUgOiB2b2lkIDApKTsKCQkJcmV0dXJuOwoJCX0KCQl0aGlzLmxvZyhMb2dMZXZlbC5Mb2dMZXZl\ +bFdhcm4sIGBVbmhhbmRsZWQgUmVxdWVzdCAiJHt2YWx1ZS5yZXF1ZXN0VHlwZX0iYCk7CgkJdGhpcy5w\ +b3N0KGNyZWF0ZUVycm9yUmVzcG9uc2UocmVxdWVzdCwgIlVuaGFuZGxlZCBSZXF1ZXN0IikpOwoJfQp9\ +OwpmdW5jdGlvbiBjcmVhdGVIYW5kbGVyKHBvcnQsIHByb2NlZHVyZXMkMSkgewoJcmV0dXJuIG5ldyBX\ +b3JrZXJNZXNzYWdlSGFuZGxlcihwb3J0LCBwcm9jZWR1cmVzJDEpOwp9CgovLyNlbmRyZWdpb24KLy8j\ +cmVnaW9uIHNyYy93b3JrZXIvd29ya2VyQ29kZU5vZGUudHMKaWYgKCFpc01haW5UaHJlYWQgJiYgcGFy\ +ZW50UG9ydCkgewoJY29uc3QgaGFuZGxlciA9IGNyZWF0ZUhhbmRsZXIocGFyZW50UG9ydCwgcHJvY2Vk\ +dXJlcyk7CglwYXJlbnRQb3J0Lm9uY2UoImNsb3NlIiwgKCkgPT4gaGFuZGxlci5kaXNwb3NlKCkpOwp9\ +CgovLyNlbmRyZWdpb24=\ `; diff --git a/test-packages/svelte-regexp/src/App.svelte b/test-packages/svelte-regexp/src/App.svelte index faf850c..4aa788a 100644 --- a/test-packages/svelte-regexp/src/App.svelte +++ b/test-packages/svelte-regexp/src/App.svelte @@ -1,11 +1,10 @@ +{#snippet snipFragments()}{#each fragments as frag (frag.id)}{#if frag.type === 'mark'}{frag.content}{:else}{frag.content}{/if}{/each}{/snippet} + +{#snippet fixedWidth(value: number, fixed: number = 4, width: number = 9)} + {value.toFixed(fixed).padStart(width, ' ')}{/snippet} +

RegExp Worker

-
-
-
- {#each fragments as frag (frag.id)}{#if frag.type === 'mark'}{frag.text}{:else if frag.type === 'br'}
{:else}{frag.text}{/if}{/each} +
+
+ RegExp: + +
+
+ Flags: +
+ +
+
+
+
+
{@render snipFragments()}
+
-
@@ -194,13 +196,6 @@ word-wrap: break-word; } - dd > input { - width: 80%; - /* box-sizing: border-box; */ - padding: 4px; - margin: 4px 0; - } - mark { background-color: #cf4; color: #0000; @@ -261,4 +256,52 @@ box-sizing: border-box; overflow-wrap: break-word; } + + .nested-wrapper { + width: 100%; + display: grid; + box-sizing: border-box; + grid-gap: 0px; + grid-template-columns: 33% 33% auto; + grid-template-areas: + 'regexp regexp flags' + 'editor editor editor'; + background-color: #fff; + color: #444; + } + + .nested-wrapper.box { + box-sizing: border-box; + background-color: #444; + color: #fff; + border-radius: 0px; + padding: 5px; + font-size: 100%; + } + + .regexp-input { + /* display: inline-block; */ + width: 98%; + /* box-sizing: border-box; */ + min-height: 2em; + height: 4em; + field-sizing: content; + padding: 4px; + resize: vertical; + font-family: monospace; + } + + .grid-regexp { + grid-area: regexp; + /* position: relative; */ + padding-top: 0.5em; + } + + .grid-flags { + position: relative; + } + .grid-editor { + grid-area: editor; + position: relative; + } diff --git a/test-packages/svelte-regexp/src/app.css b/test-packages/svelte-regexp/src/app.css index f3df420..2276158 100644 --- a/test-packages/svelte-regexp/src/app.css +++ b/test-packages/svelte-regexp/src/app.css @@ -66,11 +66,11 @@ h1 { display: grid; box-sizing: border-box; grid-gap: 10px; - grid-template-columns: 25% 25% 25% auto; + grid-template-columns: 260px 33% auto; grid-template-areas: - 'header header header header' - 'sidebar content content content' - 'footer footer footer footer'; + 'header header header' + 'sidebar content content' + 'footer footer footer'; background-color: #fff; color: #444; } diff --git a/test-packages/svelte-regexp/src/lib/Counter.svelte b/test-packages/svelte-regexp/src/lib/Counter.svelte deleted file mode 100644 index 37d75ce..0000000 --- a/test-packages/svelte-regexp/src/lib/Counter.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/test-packages/svelte-regexp/src/lib/RegExpFlags.svelte b/test-packages/svelte-regexp/src/lib/RegExpFlags.svelte index a36d1c6..f59112a 100644 --- a/test-packages/svelte-regexp/src/lib/RegExpFlags.svelte +++ b/test-packages/svelte-regexp/src/lib/RegExpFlags.svelte @@ -6,30 +6,87 @@ description: string; } - const flags: FlagInfo[] = [ + const flagInfo: FlagInfo[] = [ { flag: 'd', description: 'Generate indices for substring matches.' }, { flag: 'g', description: 'Global search.' }, { flag: 'i', description: 'Case-insensitive search.' }, - { flag: 'm', description: 'Makes ^ and $ match the start and end of each line instead of those of the entire string.' }, + { + flag: 'm', + description: 'Line Matching - Makes ^ and $ match the start and end of each line instead of those of the entire string.', + }, { flag: 's', description: 'Allows . to match newline characters.' }, - { flag: 'u', description: '"Unicode"; treat a pattern as a sequence of Unicode code points.' }, - { flag: 'v', description: 'An upgrade to the u mode with more Unicode features.' }, - { flag: 'y', description: 'Perform a "sticky" search that matches starting at the current position in the target string.' }, + { flag: 'u', description: 'Unicode - treat a pattern as a sequence of Unicode code points.' }, + { flag: 'v', description: 'Extended Unicode - An upgrade to the u mode with more Unicode features.' }, + { + flag: 'y', + description: 'Sticky - Perform a "sticky" search that matches starting at the current position in the target string.', + }, + { flag: 'x', description: 'Extended (non-standard) - allow free-spacing and line comments.' }, ]; + + let { value = $bindable() } = $props(); + let flagSet = $derived(calcFlags(value)); + + function calcFlags(flags: string): Set { + const current = new Set([...flags]); + return new Set(flagInfo.map((info) => info.flag).filter((flag) => current.has(flag))); + } + + function toggleFlag(flag: string) { + if (flagSet.has(flag)) { + flagSet.delete(flag); + } else { + // Special handling for 'v' and 'u' flags to ensure they are mutually exclusive + if (flag === 'v' || flag === 'u') { + flagSet.delete('v'); + flagSet.delete('u'); + } + flagSet.add(flag); + } + value = Array.from(flagSet).join(''); + } -{#each flags as { flag, description }} - {flag} -{/each} +{#snippet Flag(flag: string, description: string, enabled: boolean)} + +{/snippet} + +
+ {#each flagInfo as { flag, description }} + {@render Flag(flag, description, flagSet.has(flag))} + {/each} +
diff --git a/test-packages/svelte-regexp/src/lib/compareContent.ts b/test-packages/svelte-regexp/src/lib/compareContent.ts deleted file mode 100644 index f9cb7b5..0000000 --- a/test-packages/svelte-regexp/src/lib/compareContent.ts +++ /dev/null @@ -1,67 +0,0 @@ -export type Range = [number, number]; -export type Segment = [Range, Range]; -export type ContentMap = Segment[]; - -/** - * - * @param innerText - the inner text of an element - * @param innerContent - the inner content of an element, may include special characters - */ -export function compareContent(innerText: string, innerContent: string) { - const map: ContentMap = []; - - let tI = 0; - let cI = 0; - let lastTI = 0; - let lastCI = 0; - - for (; tI < innerText.length && cI < innerContent.length; tI++, cI++) { - if (innerText[tI] !== innerContent[cI]) { - addSegment(); - findMatch(); - addSegment(); - } - } - - tI = innerText.length; - cI = innerContent.length; - - addSegment(); - return map; - - function findMatch() { - let t = tI; - let c = cI; - while (t < innerText.length && c < innerContent.length) { - if (innerText[t] === innerContent[cI]) { - // Match found by moving t forward - tI = t; - return; - } - if (innerText[tI] === innerContent[c]) { - // Match found by moving c forward - cI = c; - return; - } - ++t; - ++c; - } - // No match found, pick one. - if (t < innerText.length) { - cI = c; - } else { - tI = t; - } - } - - function addSegment() { - if (lastTI < tI || lastCI < cI) { - map.push([ - [lastTI, tI], - [lastCI, cI], - ]); - lastTI = tI; - lastCI = cI; - } - } -} diff --git a/test-packages/svelte-regexp/src/lib/tooltip.svelte.ts b/test-packages/svelte-regexp/src/lib/tooltip.svelte.ts index 3ebd1d1..7f6b81e 100644 --- a/test-packages/svelte-regexp/src/lib/tooltip.svelte.ts +++ b/test-packages/svelte-regexp/src/lib/tooltip.svelte.ts @@ -11,31 +11,30 @@ export function tooltip(node: HTMLElement) { function _tooltip(element: HTMLElement) { let div: HTMLDivElement | undefined; let title: string | null = ''; + function mouseOver(event: MouseEvent) { // NOTE: remove the `title` attribute, to prevent showing the default browser tooltip // remember to set it back on `mouseleave` title = element.getAttribute('title'); element.removeAttribute('title'); + const rect = element.getBoundingClientRect(); div = document.createElement('div'); div.textContent = title; div.style = ` + color: #333; border: 1px solid #ccc; box-shadow: 1px 1px 3px #ccc; - background: #f0f0f0; + background: #e0e0e0; border-radius: 4px; padding: 4px; position: absolute; - top: ${event.pageX + 5}px; - left: ${event.pageY + 5}px; + top: ${rect.top + 70}px; + left: ${rect.left + 5}px; `; document.body.appendChild(div); } - function mouseMove(event: MouseEvent) { - if (!div) return; - div.style.left = `${event.pageX + 5}px`; - div.style.top = `${event.pageY + 5}px`; - } + function mouseLeave() { if (div) { document.body.removeChild(div); @@ -49,13 +48,11 @@ function _tooltip(element: HTMLElement) { element.addEventListener('mouseover', mouseOver); element.addEventListener('mouseleave', mouseLeave); - element.addEventListener('mousemove', mouseMove); return { destroy() { element.removeEventListener('mouseover', mouseOver); element.removeEventListener('mouseleave', mouseLeave); - element.removeEventListener('mousemove', mouseMove); }, }; } diff --git a/test-packages/svelte-regexp/src/lib/usage-text.ts b/test-packages/svelte-regexp/src/lib/usage-text.ts index 0381009..69116e6 100644 --- a/test-packages/svelte-regexp/src/lib/usage-text.ts +++ b/test-packages/svelte-regexp/src/lib/usage-text.ts @@ -1,3 +1,5 @@ +import type { RegExpLike } from 'regexp-worker'; + export const usageText = `\ The \`regexp-worker\` worker will run regular expressions in a web worker. @@ -10,6 +12,22 @@ The example regular expression \`(?<=(^|\s)\`).*?(?=\`(\s|$))\` matches everythi Example Regular Expression with Catastrophic Backtracking: \`(x+x+)+y\` <-- use this expression. Sample text: \`xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxy\` <-- remove the \`y\` to cause it to time out. + +\`\`\`javascript +import { createRegExpWorker } from 'regexp-worker'; + +const worker = createRegExpWorker(); +\`\`\` + + `; // cspell:ignore xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxy + +export const defaultRegexp: RegExpLike = { + source: `(^\`{3,}).*$[\\s\\S]*?\\1$ # Match against code blocks +| +(?<=(^|\\s)\`).*?(?=\`(\\s|$)) # Match against back tick quotes +`, + flags: 'gmx', +};