Skip to content

Commit

Permalink
Change CacheSnapshot structure and reduce its size
Browse files Browse the repository at this point in the history
  • Loading branch information
inokawa committed Feb 25, 2024
1 parent 4200aa8 commit 5f733e8
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 9 deletions.
70 changes: 70 additions & 0 deletions src/core/cache.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
initCache,
computeRange,
estimateDefaultItemSize,
takeCacheSnapshot,
} from "./cache";

const range = <T>(length: number, cb: (i: number) => T): T[] => {
Expand Down Expand Up @@ -780,6 +781,75 @@ describe(initCache.name, () => {
}
`);
});

it("should create cache from snapshot", () => {
expect(initCache(10, 23, [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 123]))
.toMatchInlineSnapshot(`
{
"_computedOffsetIndex": -1,
"_defaultItemSize": 123,
"_length": 10,
"_offsets": [
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
],
"_sizes": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
],
}
`);
});
});

describe(takeCacheSnapshot.name, () => {
it("smoke", () => {
const cache = initCacheWithComputedOffsets(
range(10, (i) => (i + 1) * 10),
40
);
const snapshot = takeCacheSnapshot(cache);
expect(snapshot).toMatchInlineSnapshot(`
[
[
10,
20,
30,
40,
50,
60,
70,
80,
90,
100,
],
40,
]
`);

// Check if modifying snapshot doesn't affect cache
const clonedSnapshot = structuredClone(snapshot);
snapshot[0][0] = 999;
snapshot[1] = 123;
expect(snapshot).not.toEqual(clonedSnapshot);
expect(takeCacheSnapshot(cache)).toEqual(clonedSnapshot);
});
});

describe(updateCacheLength.name, () => {
Expand Down
19 changes: 15 additions & 4 deletions src/core/cache.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ItemsRange } from "./types";
import { InternalCacheSnapshot, ItemsRange } from "./types";
import { clamp, max, median, min } from "./utils";

type Writeable<T> = {
Expand Down Expand Up @@ -160,16 +160,27 @@ export const estimateDefaultItemSize = (
/**
* @internal
*/
export const initCache = (length: number, itemSize: number): Cache => {
export const initCache = (
length: number,
itemSize: number,
snapshot?: InternalCacheSnapshot
): Cache => {
return {
_defaultItemSize: itemSize,
_defaultItemSize: snapshot ? snapshot[1] : itemSize,
_sizes: snapshot ? snapshot[0] : fill([], length),
_length: length,
_computedOffsetIndex: -1,
_sizes: fill([], length),
_offsets: fill([], length),
};
};

/**
* @internal
*/
export const takeCacheSnapshot = (cache: Cache): InternalCacheSnapshot => {
return [[...cache._sizes], cache._defaultItemSize];
};

/**
* @internal
*/
Expand Down
18 changes: 13 additions & 5 deletions src/core/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@ import {
getItemSize as _getItemSize,
computeTotalSize,
computeOffset as computeStartOffset,
Cache,
UNCACHED,
setItemSize,
estimateDefaultItemSize,
updateCacheLength,
computeRange,
takeCacheSnapshot,
} from "./cache";
import { isIOSWebKit } from "./environment";
import type { CacheSnapshot, ItemResize, ItemsRange } from "./types";
import type {
CacheSnapshot,
InternalCacheSnapshot,
ItemResize,
ItemsRange,
} from "./types";
import { abs, clamp, max, min } from "./utils";

// Scroll offset and sizes can have sub-pixel value if window.devicePixelRatio has decimal value
Expand Down Expand Up @@ -155,8 +160,11 @@ export const createVirtualStore = (
let _prevRange: ItemsRange = [0, 0];
let _totalMeasuredSize = 0;

const cache =
(cacheSnapshot as Cache | undefined) || initCache(elementsCount, itemSize);
const cache = initCache(
elementsCount,
itemSize,
cacheSnapshot as unknown as InternalCacheSnapshot | undefined
);
const subscribers = new Set<[number, Subscriber]>();
const getRange = (offset: number) => {
return computeRange(cache, offset, _prevRange[0], viewportSize);
Expand Down Expand Up @@ -192,7 +200,7 @@ export const createVirtualStore = (
return stateVersion;
},
_getCacheSnapshot() {
return JSON.parse(JSON.stringify(cache)) as unknown as CacheSnapshot;
return takeCacheSnapshot(cache) as unknown as CacheSnapshot;
},
_getRange() {
// Return previous range for consistent render until next scroll event comes in.
Expand Down
3 changes: 3 additions & 0 deletions src/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ export type ItemResize = Readonly<[index: number, size: number]>;
/** @internal */
export type ItemsRange = Readonly<[startIndex: number, endIndex: number]>;

/** @internal */
export type InternalCacheSnapshot = [sizes: number[], defaultSize: number];

declare const cacheSymbol: unique symbol;
/**
* Serializable cache snapshot.
Expand Down

0 comments on commit 5f733e8

Please sign in to comment.