Skip to content

Commit

Permalink
⚡️ Faster initialization of string with pre-cached slices (#5389)
Browse files Browse the repository at this point in the history
**Description**

<!-- Please provide a short description and potentially linked issues
justifying the need for this PR -->

Follow-up of #5388

<!-- * Your PR is fixing a bug or regression? Check for existing issues
related to this bug and link them -->
<!-- * Your PR is adding a new feature? Make sure there is a related
issue or discussion attached to it -->

<!-- You can provide any additional context to help into understanding
what's this PR is attempting to solve: reproduction of a bug, code
snippets... -->

**Checklist** — _Don't delete this checklist and make sure you do the
following before opening the PR_

- [x] The name of my PR follows [gitmoji](https://gitmoji.dev/)
specification
- [x] My PR references one of several related issues (if any)
- [x] New features or breaking changes must come with an associated
Issue or Discussion
- [x] My PR does not add any new dependency without an associated Issue
or Discussion
- [x] My PR includes bumps details, please run `yarn bump` and flag the
impacts properly
- [x] My PR adds relevant tests and they would have failed without my PR
(when applicable)

<!-- More about contributing at
https://github.com/dubzzz/fast-check/blob/main/CONTRIBUTING.md -->

**Advanced**

<!-- How to fill the advanced section is detailed below! -->

- [x] Category: ⚡️ Improve performance
- [x] Impacts: Faster init of `string` arbitrary

<!-- [Category] Please use one of the categories below, it will help us
into better understanding the urgency of the PR -->
<!-- * ✨ Introduce new features -->
<!-- * 📝 Add or update documentation -->
<!-- * ✅ Add or update tests -->
<!-- * 🐛 Fix a bug -->
<!-- * 🏷️ Add or update types -->
<!-- * ⚡️ Improve performance -->
<!-- * _Other(s):_ ... -->

<!-- [Impacts] Please provide a comma separated list of the potential
impacts that might be introduced by this change -->
<!-- * Generated values: Can your change impact any of the existing
generators in terms of generated values, if so which ones? when? -->
<!-- * Shrink values: Can your change impact any of the existing
generators in terms of shrink values, if so which ones? when? -->
<!-- * Performance: Can it require some typings changes on user side?
Please give more details -->
<!-- * Typings: Is there a potential performance impact? In which cases?
-->
  • Loading branch information
dubzzz authored Oct 31, 2024
1 parent 16377a8 commit 837548f
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .changeset/three-eggs-doubt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fast-check": minor
---

⚡️ Faster initialization of `string` with pre-cached slices
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Arbitrary } from '../../../check/arbitrary/definition/Arbitrary';
import { safePush } from '../../../utils/globals';
import { safeGet, safePush, safeSet } from '../../../utils/globals';
import type { StringSharedConstraints } from '../../_shared/StringSharedConstraints';
import { patternsToStringUnmapperIsValidLength } from '../mappers/PatternsToString';
import { tokenizeString } from './TokenizeString';
Expand Down Expand Up @@ -71,16 +71,35 @@ export function createSlicesForStringLegacy(
}

/** @internal */
export function createSlicesForString(
charArbitrary: Arbitrary<string>,
constraints: StringSharedConstraints,
): string[][] {
const slicesPerArbitrary = new WeakMap<Arbitrary<string>, string[][]>();

/** @internal */
function createSlicesForStringNoConstraints(charArbitrary: Arbitrary<string>): string[][] {
const slicesForString: string[][] = [];
for (const dangerous of dangerousStrings) {
const candidate = tokenizeString(charArbitrary, dangerous);
if (candidate !== undefined && patternsToStringUnmapperIsValidLength(candidate, constraints)) {
if (candidate !== undefined) {
safePush(slicesForString, candidate);
}
}
return slicesForString;
}

/** @internal */
export function createSlicesForString(
charArbitrary: Arbitrary<string>,
constraints: StringSharedConstraints,
): string[][] {
let slices = safeGet(slicesPerArbitrary, charArbitrary);
if (slices === undefined) {
slices = createSlicesForStringNoConstraints(charArbitrary);
safeSet(slicesPerArbitrary, charArbitrary, slices);
}
const slicesForConstraints: string[][] = [];
for (const slice of slices) {
if (patternsToStringUnmapperIsValidLength(slice, constraints)) {
safePush(slicesForConstraints, slice);
}
}
return slicesForConstraints;
}
31 changes: 31 additions & 0 deletions packages/fast-check/src/utils/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,37 @@ export function safeHas<T>(instance: Set<T>, value: T): boolean {
return safeApply(untouchedHas, instance, [value]);
}

// WeakMap

const untouchedSet = WeakMap.prototype.set;
const untouchedGet = WeakMap.prototype.get;
function extractSet(instance: WeakMap<object, unknown>) {
try {
return instance.set;
} catch (err) {
return undefined;
}
}
function extractGet(instance: WeakMap<object, unknown>) {
try {
return instance.get;
} catch (err) {
return undefined;
}
}
export function safeSet<T extends object, U>(instance: WeakMap<T, U>, key: T, value: U): WeakMap<T, U> {
if (extractSet(instance) === untouchedSet) {
return instance.set(key, value);
}
return safeApply(untouchedSet, instance, [key, value]);
}
export function safeGet<T extends object, U>(instance: WeakMap<T, U>, key: T): U | undefined {
if (extractGet(instance) === untouchedGet) {
return instance.get(key);
}
return safeApply(untouchedGet, instance, [key]);
}

// String

const untouchedSplit: (separator: string | RegExp, limit?: number) => string[] = String.prototype.split;
Expand Down

0 comments on commit 837548f

Please sign in to comment.