Skip to content

Commit

Permalink
Use ObjectCanon to speed up stable stringification.
Browse files Browse the repository at this point in the history
This reimplementation of the stable `stringify` function used by
`getStoreKeyName` builds on the `ObjectCanon` introduced by my PR #7439,
which guarantees canonical objects keys are always in sorted order.
  • Loading branch information
benjamn committed May 14, 2021
1 parent c82e939 commit 842eb5e
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 3 deletions.
27 changes: 24 additions & 3 deletions src/cache/inmemory/object-canon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Trie } from "@wry/trie";
import { canUseWeakMap } from "../../utilities";
import { objToStr } from "./helpers";

function isObjectOrArray(value: any): boolean {
function isObjectOrArray(value: any): value is object {
return !!value && typeof value === "object";
}

Expand Down Expand Up @@ -109,7 +109,7 @@ export class ObjectCanon {
switch (objToStr.call(value)) {
case "[object Array]": {
if (this.known.has(value)) return value;
const array: any[] = value.map(this.admit, this);
const array: any[] = (value as any[]).map(this.admit, this);
// Arrays are looked up in the Trie using their recursively
// canonicalized elements, and the known version of the array is
// preserved as node.array.
Expand All @@ -134,7 +134,7 @@ export class ObjectCanon {
array.push(keys.json);
const firstValueIndex = array.length;
keys.sorted.forEach(key => {
array.push(this.admit(value[key]));
array.push(this.admit((value as any)[key]));
});
// Objects are looked up in the Trie by their prototype (which
// is *not* recursively canonicalized), followed by a JSON
Expand Down Expand Up @@ -193,3 +193,24 @@ type SortedKeysInfo = {
sorted: string[];
json: string;
};

const stringifyCanon = new ObjectCanon;
const stringifyCache = new WeakMap<object, string>();

// Since the keys of canonical objects are always created in lexicographically
// sorted order, we can use the ObjectCanon to implement a fast and stable
// version of JSON.stringify, which automatically sorts object keys.
export function canonicalStringify(value: any): string {
if (isObjectOrArray(value)) {
const canonical = stringifyCanon.admit(value);
let json = stringifyCache.get(canonical);
if (json === void 0) {
stringifyCache.set(
canonical,
json = JSON.stringify(canonical),
);
}
return json;
}
return JSON.stringify(value);
}
6 changes: 6 additions & 0 deletions src/cache/inmemory/policies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ import {
} from '../core/types/common';
import { WriteContext } from './writeToStore';

// Upgrade to a faster version of the default stable JSON.stringify function
// used by getStoreKeyName. This function is used when computing storeFieldName
// strings (when no keyArgs has been configured for a field).
import { canonicalStringify } from './object-canon';
getStoreKeyName.setStringify(canonicalStringify);

export type TypePolicies = {
[__typename: string]: TypePolicy;
}
Expand Down

0 comments on commit 842eb5e

Please sign in to comment.