You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
3.8 KiB
136 lines
3.8 KiB
1 year ago
|
import type Theme from './Theme';
|
||
|
import type { DerivativeFunc } from './interface';
|
||
|
|
||
|
// ================================== Cache ==================================
|
||
|
type ThemeCacheMap = Map<
|
||
|
DerivativeFunc<any, any>,
|
||
|
{
|
||
|
map?: ThemeCacheMap;
|
||
|
value?: [Theme<any, any>, number];
|
||
|
}
|
||
|
>;
|
||
|
|
||
|
type DerivativeOptions = DerivativeFunc<any, any>[];
|
||
|
|
||
|
export function sameDerivativeOption(left: DerivativeOptions, right: DerivativeOptions) {
|
||
|
if (left.length !== right.length) {
|
||
|
return false;
|
||
|
}
|
||
|
for (let i = 0; i < left.length; i++) {
|
||
|
if (left[i] !== right[i]) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
export default class ThemeCache {
|
||
|
public static MAX_CACHE_SIZE = 20;
|
||
|
public static MAX_CACHE_OFFSET = 5;
|
||
|
|
||
|
private readonly cache: ThemeCacheMap;
|
||
|
private keys: DerivativeOptions[];
|
||
|
private cacheCallTimes: number;
|
||
|
|
||
|
constructor() {
|
||
|
this.cache = new Map();
|
||
|
this.keys = [];
|
||
|
this.cacheCallTimes = 0;
|
||
|
}
|
||
|
|
||
|
public size(): number {
|
||
|
return this.keys.length;
|
||
|
}
|
||
|
|
||
|
private internalGet(
|
||
|
derivativeOption: DerivativeOptions,
|
||
|
updateCallTimes = false,
|
||
|
): [Theme<any, any>, number] | undefined {
|
||
|
let cache: ReturnType<ThemeCacheMap['get']> = { map: this.cache };
|
||
|
derivativeOption.forEach(derivative => {
|
||
|
if (!cache) {
|
||
|
cache = undefined;
|
||
|
} else {
|
||
|
cache = cache?.map?.get(derivative);
|
||
|
}
|
||
|
});
|
||
|
if (cache?.value && updateCallTimes) {
|
||
|
cache.value[1] = this.cacheCallTimes++;
|
||
|
}
|
||
|
return cache?.value;
|
||
|
}
|
||
|
|
||
|
public get(derivativeOption: DerivativeOptions): Theme<any, any> | undefined {
|
||
|
return this.internalGet(derivativeOption, true)?.[0];
|
||
|
}
|
||
|
|
||
|
public has(derivativeOption: DerivativeOptions): boolean {
|
||
|
return !!this.internalGet(derivativeOption);
|
||
|
}
|
||
|
|
||
|
public set(derivativeOption: DerivativeOptions, value: Theme<any, any>): void {
|
||
|
// New cache
|
||
|
if (!this.has(derivativeOption)) {
|
||
|
if (this.size() + 1 > ThemeCache.MAX_CACHE_SIZE + ThemeCache.MAX_CACHE_OFFSET) {
|
||
|
const [targetKey] = this.keys.reduce<[DerivativeOptions, number]>(
|
||
|
(result, key) => {
|
||
|
const [, callTimes] = result;
|
||
|
if (this.internalGet(key)![1] < callTimes) {
|
||
|
return [key, this.internalGet(key)![1]];
|
||
|
}
|
||
|
return result;
|
||
|
},
|
||
|
[this.keys[0], this.cacheCallTimes],
|
||
|
);
|
||
|
this.delete(targetKey);
|
||
|
}
|
||
|
|
||
|
this.keys.push(derivativeOption);
|
||
|
}
|
||
|
|
||
|
let cache = this.cache;
|
||
|
derivativeOption.forEach((derivative, index) => {
|
||
|
if (index === derivativeOption.length - 1) {
|
||
|
cache.set(derivative, { value: [value, this.cacheCallTimes++] });
|
||
|
} else {
|
||
|
const cacheValue = cache.get(derivative);
|
||
|
if (!cacheValue) {
|
||
|
cache.set(derivative, { map: new Map() });
|
||
|
} else if (!cacheValue.map) {
|
||
|
cacheValue.map = new Map();
|
||
|
}
|
||
|
cache = cache.get(derivative)!.map!;
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
private deleteByPath(
|
||
|
currentCache: ThemeCacheMap,
|
||
|
derivatives: DerivativeFunc<any, any>[],
|
||
|
): Theme<any, any> | undefined {
|
||
|
const cache = currentCache.get(derivatives[0])!;
|
||
|
if (derivatives.length === 1) {
|
||
|
if (!cache.map) {
|
||
|
currentCache.delete(derivatives[0]);
|
||
|
} else {
|
||
|
currentCache.set(derivatives[0], { map: cache.map });
|
||
|
}
|
||
|
return cache.value?.[0];
|
||
|
}
|
||
|
const result = this.deleteByPath(cache.map!, derivatives.slice(1));
|
||
|
if ((!cache.map || cache.map.size === 0) && !cache.value) {
|
||
|
currentCache.delete(derivatives[0]);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public delete(derivativeOption: DerivativeOptions): Theme<any, any> | undefined {
|
||
|
// If cache exists
|
||
|
if (this.has(derivativeOption)) {
|
||
|
this.keys = this.keys.filter(item => !sameDerivativeOption(item, derivativeOption));
|
||
|
return this.deleteByPath(this.cache, derivativeOption);
|
||
|
}
|
||
|
return undefined;
|
||
|
}
|
||
|
}
|