diff --git a/package.json b/package.json index 4cdfd6c..4e21fdc 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,7 @@ "@csstools/css-calc": "^3.1.1", "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0", - "lru-cache": "^11.2.7" + "@csstools/css-tokenizer": "^4.0.0" }, "devDependencies": { "@tanstack/vite-config": "^0.5.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 99124df..6e90ca3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,9 +33,6 @@ importers: '@csstools/css-tokenizer': specifier: ^4.0.0 version: 4.0.0 - lru-cache: - specifier: ^11.2.7 - version: 11.2.7 devDependencies: '@tanstack/vite-config': specifier: ^0.5.2 diff --git a/src/js/cache.ts b/src/js/cache.ts index 1bbf1ed..8b159f5 100644 --- a/src/js/cache.ts +++ b/src/js/cache.ts @@ -2,7 +2,6 @@ * cache */ -import { LRUCache } from 'lru-cache'; import { Options } from './typedef'; /* numeric constants */ @@ -45,12 +44,63 @@ export class NullObject extends CacheItem { } } +/** + * Generational Cache implementation + */ +export class GenerationalCache { + private max: number; + private current: Map; + private old: Map; + + constructor(max: number) { + this.max = Math.ceil(max / 2); + this.current = new Map(); + this.old = new Map(); + } + + get(key: K): V | undefined { + let value = this.current.get(key); + if (value !== undefined) { + return value; + } + + value = this.old.get(key); + if (value !== undefined) { + this.set(key, value); + return value; + } + + return undefined; + } + + set(key: K, value: V): void { + this.current.set(key, value); + + if (this.current.size >= this.max) { + this.old = this.current; + this.current = new Map(); + } + } + + has(key: K): boolean { + return this.current.has(key) || this.old.has(key); + } + + delete(key: K): void { + this.current.delete(key); + this.old.delete(key); + } + + clear(): void { + this.current.clear(); + this.old.clear(); + } +} + /* - * lru cache + * generational cache instance */ -export const lruCache = new LRUCache({ - max: MAX_CACHE -}); +export const genCache = new GenerationalCache(MAX_CACHE); /** * set cache @@ -61,11 +111,11 @@ export const lruCache = new LRUCache({ export const setCache = (key: string, value: unknown): void => { if (key) { if (value === null) { - lruCache.set(key, new NullObject()); + genCache.set(key, new NullObject()); } else if (value instanceof CacheItem) { - lruCache.set(key, value); + genCache.set(key, value); } else { - lruCache.set(key, new CacheItem(value)); + genCache.set(key, new CacheItem(value)); } } }; @@ -76,13 +126,13 @@ export const setCache = (key: string, value: unknown): void => { * @returns cached item or false otherwise */ export const getCache = (key: string): CacheItem | boolean => { - if (key && lruCache.has(key)) { - const item = lruCache.get(key); + if (key && genCache.has(key)) { + const item = genCache.get(key); if (item instanceof CacheItem) { return item; } // delete unexpected cached item - lruCache.delete(key); + genCache.delete(key); return false; } return false; diff --git a/test/cache.test.ts b/test/cache.test.ts index e1e7ac3..bcf21bf 100644 --- a/test/cache.test.ts +++ b/test/cache.test.ts @@ -6,18 +6,17 @@ import { afterEach, assert, beforeEach, describe, it } from 'vitest'; /* test */ -import { LRUCache } from 'lru-cache'; import * as cache from '../src/js/cache'; -describe('lru cache', () => { +describe('generational cache', () => { it('should be instance', () => { - const { lruCache } = cache; - assert.strictEqual(lruCache instanceof LRUCache, true, 'instance'); - assert.strictEqual(typeof lruCache.clear, 'function', 'clear'); - assert.strictEqual(typeof lruCache.delete, 'function', 'delete'); - assert.strictEqual(typeof lruCache.get, 'function', 'get'); - assert.strictEqual(typeof lruCache.has, 'function', 'has'); - assert.strictEqual(typeof lruCache.set, 'function', 'set'); + const { GenerationalCache, genCache } = cache; + assert.strictEqual(genCache instanceof GenerationalCache, true, 'instance'); + assert.strictEqual(typeof genCache.clear, 'function', 'clear'); + assert.strictEqual(typeof genCache.delete, 'function', 'delete'); + assert.strictEqual(typeof genCache.get, 'function', 'get'); + assert.strictEqual(typeof genCache.has, 'function', 'has'); + assert.strictEqual(typeof genCache.set, 'function', 'set'); }); }); @@ -58,55 +57,55 @@ describe('NullObject', () => { }); describe('set cache', () => { - const { CacheItem, lruCache } = cache; + const { CacheItem, genCache } = cache; const func = cache.setCache; beforeEach(() => { - lruCache.clear(); + genCache.clear(); }); afterEach(() => { - lruCache.clear(); + genCache.clear(); }); it('should not set cache', () => { func(''); - assert.strictEqual(lruCache.has(''), false, 'has'); + assert.strictEqual(genCache.has(''), false, 'has'); }); it('should set cache', () => { func('foo'); - assert.strictEqual(lruCache.has('foo'), true, 'has'); - assert.strictEqual(lruCache.get('foo') instanceof CacheItem, true, 'cache'); + assert.strictEqual(genCache.has('foo'), true, 'has'); + assert.strictEqual(genCache.get('foo') instanceof CacheItem, true, 'cache'); }); it('should set cache', () => { func('bar', 'bar'); - assert.strictEqual(lruCache.has('bar'), true, 'has'); - assert.strictEqual(lruCache.get('bar') instanceof CacheItem, true, 'cache'); + assert.strictEqual(genCache.has('bar'), true, 'has'); + assert.strictEqual(genCache.get('bar') instanceof CacheItem, true, 'cache'); }); it('should set cache', () => { func('baz', null); - assert.strictEqual(lruCache.has('baz'), true, 'has'); - assert.strictEqual(lruCache.get('baz') instanceof CacheItem, true, 'cache'); + assert.strictEqual(genCache.has('baz'), true, 'has'); + assert.strictEqual(genCache.get('baz') instanceof CacheItem, true, 'cache'); }); it('should set cache', () => { func('qux', new CacheItem('qux')); - assert.strictEqual(lruCache.has('qux'), true, 'has'); - assert.strictEqual(lruCache.get('qux') instanceof CacheItem, true, 'cache'); + assert.strictEqual(genCache.has('qux'), true, 'has'); + assert.strictEqual(genCache.get('qux') instanceof CacheItem, true, 'cache'); }); }); describe('get cache', () => { - const { CacheItem, NullObject, lruCache, setCache } = cache; + const { CacheItem, NullObject, genCache, setCache } = cache; const func = cache.getCache; beforeEach(() => { - lruCache.clear(); + genCache.clear(); }); afterEach(() => { - lruCache.clear(); + genCache.clear(); }); it('should get cache', () => { @@ -138,9 +137,9 @@ describe('get cache', () => { }); it('should get false and delete key', () => { - lruCache.set('quux', 'quux'); + genCache.set('quux', 'quux'); const res = func('quux'); - assert.strictEqual(lruCache.has('quux'), false, 'key'); + assert.strictEqual(genCache.has('quux'), false, 'key'); assert.strictEqual(res, false, 'result'); }); }); diff --git a/test/color.test.ts b/test/color.test.ts index dc9e96d..fe61894 100644 --- a/test/color.test.ts +++ b/test/color.test.ts @@ -6,14 +6,14 @@ import { afterEach, assert, beforeEach, describe, it } from 'vitest'; /* test */ -import { NullObject, lruCache } from '../src/js/cache'; +import { NullObject, genCache } from '../src/js/cache'; import * as color from '../src/js/color'; beforeEach(() => { - lruCache.clear(); + genCache.clear(); }); afterEach(() => { - lruCache.clear(); + genCache.clear(); }); describe('cache invalid color value', () => { diff --git a/test/convert.test.ts b/test/convert.test.ts index be8bd3d..b0d3a90 100644 --- a/test/convert.test.ts +++ b/test/convert.test.ts @@ -7,14 +7,14 @@ import { afterEach, assert, beforeEach, describe, it } from 'vitest'; /* test */ import * as convert from '../src/js/convert.js'; -import { lruCache } from '../src/js/cache.js'; +import { genCache } from '../src/js/cache.js'; beforeEach(() => { - lruCache.clear(); + genCache.clear(); }); afterEach(() => { - lruCache.clear(); + genCache.clear(); }); describe('pre process', () => { diff --git a/test/css-calc.test.ts b/test/css-calc.test.ts index 7800c2e..cd37670 100644 --- a/test/css-calc.test.ts +++ b/test/css-calc.test.ts @@ -6,15 +6,15 @@ import { afterEach, assert, beforeEach, describe, it } from 'vitest'; /* test */ -import { lruCache } from '../src/js/cache'; +import { genCache } from '../src/js/cache'; import * as csscalc from '../src/js/css-calc'; beforeEach(() => { - lruCache.clear(); + genCache.clear(); }); afterEach(() => { - lruCache.clear(); + genCache.clear(); }); describe('Calculator', () => { diff --git a/test/css-gradient.test.ts b/test/css-gradient.test.ts index 23f2bb9..d1452a4 100644 --- a/test/css-gradient.test.ts +++ b/test/css-gradient.test.ts @@ -6,15 +6,15 @@ import { afterEach, assert, beforeEach, describe, it } from 'vitest'; /* test */ -import { lruCache } from '../src/js/cache'; +import { genCache } from '../src/js/cache'; import * as grad from '../src/js/css-gradient'; beforeEach(() => { - lruCache.clear(); + genCache.clear(); }); afterEach(() => { - lruCache.clear(); + genCache.clear(); }); describe('get gradient type', () => { diff --git a/test/css-var.test.ts b/test/css-var.test.ts index f3cd9b4..7efd04c 100644 --- a/test/css-var.test.ts +++ b/test/css-var.test.ts @@ -6,15 +6,15 @@ import { afterEach, assert, beforeEach, describe, it } from 'vitest'; /* test */ -import { lruCache } from '../src/js/cache'; +import { genCache } from '../src/js/cache'; import * as custom from '../src/js/css-var'; beforeEach(() => { - lruCache.clear(); + genCache.clear(); }); afterEach(() => { - lruCache.clear(); + genCache.clear(); }); describe('resolve CSS variable', () => { diff --git a/test/relative-color.test.ts b/test/relative-color.test.ts index d1b37d2..dd5a7d5 100644 --- a/test/relative-color.test.ts +++ b/test/relative-color.test.ts @@ -7,15 +7,15 @@ import { tokenize } from '@csstools/css-tokenizer'; import { afterEach, assert, beforeEach, describe, it } from 'vitest'; /* test */ -import { lruCache } from '../src/js/cache'; +import { genCache } from '../src/js/cache'; import * as relColor from '../src/js/relative-color'; beforeEach(() => { - lruCache.clear(); + genCache.clear(); }); afterEach(() => { - lruCache.clear(); + genCache.clear(); }); describe('resolve relative color channels', () => { diff --git a/test/resolve.test.ts b/test/resolve.test.ts index 9cfc8c1..cd3ba1e 100644 --- a/test/resolve.test.ts +++ b/test/resolve.test.ts @@ -7,15 +7,15 @@ import { afterEach, assert, beforeEach, describe, it } from 'vitest'; /* test */ import * as api from '../src/js/resolve'; -import { lruCache } from '../src/js/cache'; +import { genCache } from '../src/js/cache'; import { parseColorValue } from '../src/js/color'; beforeEach(() => { - lruCache.clear(); + genCache.clear(); }); afterEach(() => { - lruCache.clear(); + genCache.clear(); }); describe('resolve CSS color', () => { diff --git a/test/util.test.ts b/test/util.test.ts index f1cfb6c..3f8eb89 100644 --- a/test/util.test.ts +++ b/test/util.test.ts @@ -6,15 +6,15 @@ import { afterEach, assert, beforeEach, describe, it } from 'vitest'; /* test */ -import { lruCache } from '../src/js/cache'; +import { genCache } from '../src/js/cache'; import * as util from '../src/js/util'; beforeEach(() => { - lruCache.clear(); + genCache.clear(); }); afterEach(() => { - lruCache.clear(); + genCache.clear(); }); describe('split value', () => {