Skip to content

Commit

Permalink
fix(patch): fix patch generator issue (#60)
Browse files Browse the repository at this point in the history
  • Loading branch information
unadlib authored Sep 18, 2024
1 parent 7e95b49 commit 5396f53
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 49 deletions.
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,23 +110,23 @@ Mutative is up to 6x faster than naive handcrafted reducer for updating immutabl

> Mutative passed all of Immer's test cases.
Measure(ops/sec) to update 50K arrays and 1K objects, bigger is better([view source](https://github.com/unadlib/mutative/blob/main/test/performance/benchmark.ts)). [Mutative v1.0.9 vs Immer v10.1.1]
Measure(ops/sec) to update 50K arrays and 1K objects, bigger is better([view source](https://github.com/unadlib/mutative/blob/main/test/performance/benchmark.ts)). [Mutative v1.0.10 vs Immer v10.1.1]

![Benchmark](benchmark.jpg)

```
Naive handcrafted reducer - No Freeze x 4,447 ops/sec ±0.85% (96 runs sampled)
Mutative - No Freeze x 6,246 ops/sec ±1.29% (92 runs sampled)
Immer - No Freeze x 5.26 ops/sec ±0.56% (18 runs sampled)
Naive handcrafted reducer - No Freeze x 4,439 ops/sec ±0.65% (98 runs sampled)
Mutative - No Freeze x 6,300 ops/sec ±1.19% (94 runs sampled)
Immer - No Freeze x 5.26 ops/sec ±0.59% (18 runs sampled)
Mutative - Freeze x 950 ops/sec ±0.95% (96 runs sampled)
Immer - Freeze x 377 ops/sec ±0.37% (93 runs sampled)
Mutative - Freeze x 937 ops/sec ±1.25% (95 runs sampled)
Immer - Freeze x 378 ops/sec ±0.66% (93 runs sampled)
Mutative - Patches and No Freeze x 978 ops/sec ±0.16% (97 runs sampled)
Immer - Patches and No Freeze x 5.23 ops/sec ±0.25% (18 runs sampled)
Mutative - Patches and No Freeze x 975 ops/sec ±0.17% (99 runs sampled)
Immer - Patches and No Freeze x 5.29 ops/sec ±0.30% (18 runs sampled)
Mutative - Patches and Freeze x 504 ops/sec ±1.01% (94 runs sampled)
Immer - Patches and Freeze x 272 ops/sec ±0.83% (89 runs sampled)
Mutative - Patches and Freeze x 512 ops/sec ±0.85% (98 runs sampled)
Immer - Patches and Freeze x 278 ops/sec ±0.57% (90 runs sampled)
The fastest method is Mutative - No Freeze
```
Expand All @@ -137,7 +137,7 @@ Run `yarn benchmark` to measure performance.
Immer relies on auto-freeze to be enabled, if auto-freeze is disabled, Immer will have a huge performance drop and Mutative will have a huge performance lead, especially with large data structures it will have a performance lead of more than 50x.

So if you are using Immer, you will have to enable auto-freeze for performance. Mutative is disabled auto-freeze by default. With the default configuration of both, we can see the 16x performance gap between Mutative (`6,246 ops/sec`) and Immer (`377 ops/sec`).
So if you are using Immer, you will have to enable auto-freeze for performance. Mutative is disabled auto-freeze by default. With the default configuration of both, we can see the 16x performance gap between Mutative (`6,300 ops/sec`) and Immer (`378 ops/sec`).

Overall, Mutative has a huge performance lead over Immer in [more performance testing scenarios](https://github.com/unadlib/mutative/tree/main/test/performance). Run `yarn performance` to get all the performance results locally.

Expand Down
Binary file modified benchmark.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 1 addition & 4 deletions src/apply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,7 @@ export function apply<T extends object, F extends boolean = false>(
);
}
// use `index` in Set draft
base = get(
getType(base) === DraftType.Set ? Array.from(base) : base,
key
);
base = get(parentType === DraftType.Set ? Array.from(base) : base, key);
if (typeof base !== 'object') {
throw new Error(`Cannot apply patch at '${path.join('/')}'.`);
}
Expand Down
22 changes: 21 additions & 1 deletion src/utils/draft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,15 @@ export function getPath(
if (target.parent) {
return getPath(target.parent, path);
}
return path.reverse();
// `target` is root draft.
path.reverse();
try {
// check if the path is valid
resolvePath(target.copy, path);
} catch (e) {
return null;
}
return path;
}

export function getType(target: any) {
Expand Down Expand Up @@ -124,3 +132,15 @@ export function unescapePath(path: string | (string | number)[]) {
.map((_item) => _item.replace(/~1/g, '/').replace(/~0/g, '~'))
.slice(1);
}

export function resolvePath(base: any, path: (string | number)[]) {
for (let index = 0; index < path.length - 1; index += 1) {
const key = path[index];
// use `index` in Set draft
base = get(getType(base) === DraftType.Set ? Array.from(base) : base, key);
if (typeof base !== 'object') {
throw new Error(`Cannot resolve patch at '${path.join('/')}'.`);
}
}
return base;
}
91 changes: 91 additions & 0 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3992,3 +3992,94 @@ test('#57 - curried create returns object of wrong type', () => {
const nextState = finalize();
expect(nextState).toEqual({ a: { x: 'test' } });
});

test('#59 - Failure to apply inverse patchset', () => {
const parentId = 'parent';
const childId = 'child';
const myObj = {
[parentId]: { name: 'parent', children: [childId] },
[childId]: { name: 'child' },
};
const [newState, patchset, inverse] = create(
myObj,
(draft: any) => {
// delete children
while (draft[parentId].children.length) {
const id = draft[parentId].children[0];
draft[parentId].children.splice(0, 1);
delete draft[id]; // delete child object
}

// delete parent
// @ts-ignore
delete draft[parentId];
},
{ enablePatches: true }
);
const reverted = apply(newState, inverse);
expect(reverted).toEqual(myObj);
const reverted2 = apply(myObj, patchset);
expect(reverted2).toEqual(newState);
});

test('#59 - Failure to apply inverse patchset(Set)', () => {
const parentId = 'parent';
const childId = 'child';

const myObj = {
[parentId]: new Set([{ name: 'parent', children: [childId] }]),
[childId]: { name: 'child' },
};

const [newState, patchset, inverse] = create(
myObj,
(draft: any) => {
// delete children
const parent: any = Array.from(draft[parentId])[0];
while (parent.children.length) {
const id = parent.children[0];
parent.children.splice(0, 1);
delete draft[id]; // delete child object
}

// delete parent
draft[parentId].clear();
},
{ enablePatches: true }
);
const reverted = apply(newState, inverse);
expect(reverted).toEqual(myObj);
const reverted2 = apply(myObj, patchset);
expect(reverted2).toEqual(newState);
});

test('#59 - Failure to apply inverse patchset(Map)', () => {
const parentId = 'parent';
const childId = 'child';

const myObj = {
[parentId]: new Map([[0, { name: 'parent', children: [childId] }]]),
[childId]: { name: 'child' },
};

const [newState, patchset, inverse] = create(
myObj,
(draft: any) => {
// delete children
const parent: any = draft[parentId].get(0);
while (parent.children.length) {
const id = parent.children[0];
parent.children.splice(0, 1);
delete draft[id]; // delete child object
}

// delete parent
draft[parentId].clear();
},
{ enablePatches: true }
);
const reverted = apply(newState, inverse);
expect(reverted).toEqual(myObj);
const reverted2 = apply(myObj, patchset);
expect(reverted2).toEqual(newState);
});
22 changes: 11 additions & 11 deletions website/blog/releases/1.0/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,23 +81,23 @@ const state = create(baseState, (draft) => {

> Mutative passed all of Immer's test cases.
Measure(ops/sec) to update 50K arrays and 1K objects, bigger is better([view source](https://github.com/unadlib/mutative/blob/main/test/performance/benchmark.ts)). [Mutative v1.0.9 vs Immer v10.1.1]
Measure(ops/sec) to update 50K arrays and 1K objects, bigger is better([view source](https://github.com/unadlib/mutative/blob/main/test/performance/benchmark.ts)). [Mutative v1.0.10 vs Immer v10.1.1]

![Benchmark](img/benchmark.jpg)

```
Naive handcrafted reducer - No Freeze x 4,447 ops/sec ±0.85% (96 runs sampled)
Mutative - No Freeze x 6,246 ops/sec ±1.29% (92 runs sampled)
Immer - No Freeze x 5.26 ops/sec ±0.56% (18 runs sampled)
Naive handcrafted reducer - No Freeze x 4,439 ops/sec ±0.65% (98 runs sampled)
Mutative - No Freeze x 6,300 ops/sec ±1.19% (94 runs sampled)
Immer - No Freeze x 5.26 ops/sec ±0.59% (18 runs sampled)
Mutative - Freeze x 950 ops/sec ±0.95% (96 runs sampled)
Immer - Freeze x 377 ops/sec ±0.37% (93 runs sampled)
Mutative - Freeze x 937 ops/sec ±1.25% (95 runs sampled)
Immer - Freeze x 378 ops/sec ±0.66% (93 runs sampled)
Mutative - Patches and No Freeze x 978 ops/sec ±0.16% (97 runs sampled)
Immer - Patches and No Freeze x 5.23 ops/sec ±0.25% (18 runs sampled)
Mutative - Patches and No Freeze x 975 ops/sec ±0.17% (99 runs sampled)
Immer - Patches and No Freeze x 5.29 ops/sec ±0.30% (18 runs sampled)
Mutative - Patches and Freeze x 504 ops/sec ±1.01% (94 runs sampled)
Immer - Patches and Freeze x 272 ops/sec ±0.83% (89 runs sampled)
Mutative - Patches and Freeze x 512 ops/sec ±0.85% (98 runs sampled)
Immer - Patches and Freeze x 278 ops/sec ±0.57% (90 runs sampled)
The fastest method is Mutative - No Freeze
```
Expand All @@ -108,7 +108,7 @@ Run `yarn benchmark` to measure performance.
Immer relies on auto-freeze to be enabled, if auto-freeze is disabled, Immer will have a huge performance drop and Mutative will have a huge performance lead, especially with large data structures it will have a performance lead of more than 50x.

So if you are using Immer, you will have to enable auto-freeze for performance. Mutative is disabled auto-freeze by default. With the default configuration of both, we can see the 16x performance gap between Mutative (`6,246 ops/sec`) and Immer (`377 ops/sec`).
So if you are using Immer, you will have to enable auto-freeze for performance. Mutative is disabled auto-freeze by default. With the default configuration of both, we can see the 16x performance gap between Mutative (`6,300 ops/sec`) and Immer (`378 ops/sec`).

Overall, Mutative has a huge performance lead over Immer in [more performance testing scenarios](https://github.com/unadlib/mutative/tree/main/test/performance).

Expand Down
22 changes: 11 additions & 11 deletions website/docs/extra-topics/comparison-with-immer.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,23 @@ Mutative has fewer bugs such as accidental draft escapes than Immer, [view detai

> Mutative passed all of Immer's test cases.
Measure(ops/sec) to update 50K arrays and 1K objects, bigger is better([view source](https://github.com/unadlib/mutative/blob/main/test/performance/benchmark.ts)). [Mutative v1.0.9 vs Immer v10.1.1]
Measure(ops/sec) to update 50K arrays and 1K objects, bigger is better([view source](https://github.com/unadlib/mutative/blob/main/test/performance/benchmark.ts)). [Mutative v1.0.10 vs Immer v10.1.1]

![Benchmark](img/benchmark.jpg)

```
Naive handcrafted reducer - No Freeze x 4,447 ops/sec ±0.85% (96 runs sampled)
Mutative - No Freeze x 6,246 ops/sec ±1.29% (92 runs sampled)
Immer - No Freeze x 5.26 ops/sec ±0.56% (18 runs sampled)
Naive handcrafted reducer - No Freeze x 4,439 ops/sec ±0.65% (98 runs sampled)
Mutative - No Freeze x 6,300 ops/sec ±1.19% (94 runs sampled)
Immer - No Freeze x 5.26 ops/sec ±0.59% (18 runs sampled)
Mutative - Freeze x 950 ops/sec ±0.95% (96 runs sampled)
Immer - Freeze x 377 ops/sec ±0.37% (93 runs sampled)
Mutative - Freeze x 937 ops/sec ±1.25% (95 runs sampled)
Immer - Freeze x 378 ops/sec ±0.66% (93 runs sampled)
Mutative - Patches and No Freeze x 978 ops/sec ±0.16% (97 runs sampled)
Immer - Patches and No Freeze x 5.23 ops/sec ±0.25% (18 runs sampled)
Mutative - Patches and No Freeze x 975 ops/sec ±0.17% (99 runs sampled)
Immer - Patches and No Freeze x 5.29 ops/sec ±0.30% (18 runs sampled)
Mutative - Patches and Freeze x 504 ops/sec ±1.01% (94 runs sampled)
Immer - Patches and Freeze x 272 ops/sec ±0.83% (89 runs sampled)
Mutative - Patches and Freeze x 512 ops/sec ±0.85% (98 runs sampled)
Immer - Patches and Freeze x 278 ops/sec ±0.57% (90 runs sampled)
The fastest method is Mutative - No Freeze
```
Expand All @@ -52,7 +52,7 @@ Run `yarn benchmark` to measure performance.
Immer relies on auto-freeze to be enabled, if auto-freeze is disabled, Immer will have a huge performance drop and Mutative will have a huge performance lead, especially with large data structures it will have a performance lead of more than 50x.

So if you are using Immer, you will have to enable auto-freeze for performance. Mutative is disabled auto-freeze by default. With the default configuration of both, we can see the 16x performance gap between Mutative (`6,246 ops/sec`) and Immer (`377 ops/sec`).
So if you are using Immer, you will have to enable auto-freeze for performance. Mutative is disabled auto-freeze by default. With the default configuration of both, we can see the 16x performance gap between Mutative (`6,300 ops/sec`) and Immer (`378 ops/sec`).

Overall, Mutative has a huge performance lead over Immer in [more performance testing scenarios](https://github.com/unadlib/mutative/tree/main/test/performance). Run `yarn performance` to get all the performance results locally.

22 changes: 11 additions & 11 deletions website/docs/getting-started/performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,23 +69,23 @@ const state = create(baseState, (draft) => {

> Mutative passed all of Immer's test cases.
Measure(ops/sec) to update 50K arrays and 1K objects, bigger is better([view source](https://github.com/unadlib/mutative/blob/main/test/performance/benchmark.ts)). [Mutative v1.0.9 vs Immer v10.1.1]
Measure(ops/sec) to update 50K arrays and 1K objects, bigger is better([view source](https://github.com/unadlib/mutative/blob/main/test/performance/benchmark.ts)). [Mutative v1.0.10 vs Immer v10.1.1]

![Benchmark](img/benchmark.jpg)

```
Naive handcrafted reducer - No Freeze x 4,447 ops/sec ±0.85% (96 runs sampled)
Mutative - No Freeze x 6,246 ops/sec ±1.29% (92 runs sampled)
Immer - No Freeze x 5.26 ops/sec ±0.56% (18 runs sampled)
Naive handcrafted reducer - No Freeze x 4,439 ops/sec ±0.65% (98 runs sampled)
Mutative - No Freeze x 6,300 ops/sec ±1.19% (94 runs sampled)
Immer - No Freeze x 5.26 ops/sec ±0.59% (18 runs sampled)
Mutative - Freeze x 950 ops/sec ±0.95% (96 runs sampled)
Immer - Freeze x 377 ops/sec ±0.37% (93 runs sampled)
Mutative - Freeze x 937 ops/sec ±1.25% (95 runs sampled)
Immer - Freeze x 378 ops/sec ±0.66% (93 runs sampled)
Mutative - Patches and No Freeze x 978 ops/sec ±0.16% (97 runs sampled)
Immer - Patches and No Freeze x 5.23 ops/sec ±0.25% (18 runs sampled)
Mutative - Patches and No Freeze x 975 ops/sec ±0.17% (99 runs sampled)
Immer - Patches and No Freeze x 5.29 ops/sec ±0.30% (18 runs sampled)
Mutative - Patches and Freeze x 504 ops/sec ±1.01% (94 runs sampled)
Immer - Patches and Freeze x 272 ops/sec ±0.83% (89 runs sampled)
Mutative - Patches and Freeze x 512 ops/sec ±0.85% (98 runs sampled)
Immer - Patches and Freeze x 278 ops/sec ±0.57% (90 runs sampled)
The fastest method is Mutative - No Freeze
```
Expand All @@ -96,7 +96,7 @@ Run `yarn benchmark` to measure performance.
Immer relies on auto-freeze to be enabled, if auto-freeze is disabled, Immer will have a huge performance drop and Mutative will have a huge performance lead, especially with large data structures it will have a performance lead of more than 50x.

So if you are using Immer, you will have to enable auto-freeze for performance. Mutative is disabled auto-freeze by default. With the default configuration of both, we can see the 16x performance gap between Mutative (`6,246 ops/sec`) and Immer (`377 ops/sec`).
So if you are using Immer, you will have to enable auto-freeze for performance. Mutative is disabled auto-freeze by default. With the default configuration of both, we can see the 16x performance gap between Mutative (`6,300 ops/sec`) and Immer (`378 ops/sec`).

Overall, Mutative has a huge performance lead over Immer in [more performance testing scenarios](https://github.com/unadlib/mutative/tree/main/test/performance). Run `yarn performance` to get all the performance results locally.

Expand Down

0 comments on commit 5396f53

Please sign in to comment.