Skip to content

Commit

Permalink
remove ArrayLRUCache as we assume browser will support Map
Browse files Browse the repository at this point in the history
  • Loading branch information
fuzhenn committed Mar 5, 2025
1 parent 90e351a commit 72e49d5
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 228 deletions.
282 changes: 57 additions & 225 deletions packages/maptalks/src/core/util/LRUCache.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,24 @@
/* eslint-disable @typescript-eslint/ban-types */
const isMapSupported = typeof Map === 'function';

const nullOnRemove = () => { };
/**
* from mapbox-gl-js
* A [least-recently-used cache](http://en.wikipedia.org/wiki/Cache_algorithms)
* with hash lookup made possible by keeping a list of keys in parallel to
* an array of dictionary of values
*
* @public
*/
export class ArrayLRUCache {
class LRUCache {
max: number
onRemove: Function
data: any
order: any[]

/**
* @param max number of permitted values
* @param onRemove callback called with items when they expire
*/
constructor(max: number, onRemove: Function) {
constructor(max: number, onRemove?: Function) {
const nullOnRemove = () => { };
this.max = max;
this.onRemove = onRemove || nullOnRemove;
this.reset();
}

/**
* Clear the cache
*
* @returns this cache
*/
reset() {
for (const key in this.data) {
this.onRemove(this.data[key]);
if (this.data) {
const values = this.data.values();
for (const p of values) {
this.onRemove(p);
}
}

this.data = {};
this.order = [];

this.data = new Map();
return this;
}

Expand All @@ -47,235 +27,87 @@ export class ArrayLRUCache {
delete this.onRemove;
}

/**
* Add a key, value combination to the cache, trimming its size if this pushes
* it over max length.
*
* @param key lookup key for the item
* @param data any value
* @returns this cache
*/
add(key: string, data: any) {

add(key, data) {
if (!data) {
return this;
}
if (this.has(key)) {
this.order.splice(this.order.indexOf(key), 1);
this.data[key] = data;
this.order.push(key);

this.data.delete(key);
this.data.set(key, data);
// if (this.data.size > this.max) {
// this.shrink();
// }
} else {
this.data[key] = data;
this.order.push(key);

if (this.order.length > this.max) {
const removedData = this.getAndRemove(this.order[0]);
if (removedData) this.onRemove(removedData);
}
this.data.set(key, data);
// if (this.data.size > this.max) {
// this.shrink();
// }
}

return this;
}

/**
* Determine whether the value attached to `key` is present
*
* @param key the key to be looked-up
* @returns whether the cache has this value
*/
has(key: string) {
return key in this.data;
keys() {
const keys = new Array(this.data.size);
let i = 0;
const iterator = this.data.keys();
for (const k of iterator) {
keys[i++] = k;
}
return keys;
}

/**
* List all keys in the cache
*
* @returns an array of keys in this cache.
*/
keys(): string[] {
return this.order;

shrink() {
const iterator = this.data.keys();
let item = iterator.next();
while (this.data.size > this.max) {
const removedData = this.getAndRemove(item.value);
if (removedData) {
this.onRemove(removedData);
}
item = iterator.next();
}
}

/**
* Get the value attached to a specific key and remove data from cache.
* If the key is not found, returns `null`
*
* @param key the key to look up
* @returns the data, or null if it isn't found
*/
getAndRemove(key: string) {
if (!this.has(key)) { return null; }
has(key) {
return this.data.has(key);
}

const data = this.data[key];

delete this.data[key];
this.order.splice(this.order.indexOf(key), 1);
getAndRemove(key) {
if (!this.has(key)) { return null; }

const data = this.data.get(key);
this.data.delete(key);
return data;
}

/**
* Get the value attached to a specific key without removing data
* from the cache. If the key is not found, returns `null`
*
* @param key the key to look up
* @returns the data, or null if it isn't found
*/
get(key: string) {

get(key) {
if (!this.has(key)) { return null; }

const data = this.data[key];
const data = this.data.get(key);
return data;
}

/**
* Remove a key/value combination from the cache.
*
* @param key the key for the pair to delete
* @returns this cache
*/
remove(key: string) {
remove(key) {
if (!this.has(key)) { return this; }

const data = this.data[key];
delete this.data[key];
const data = this.data.get(key);
this.data.delete(key);
this.onRemove(data);
this.order.splice(this.order.indexOf(key), 1);

return this;
}

/**
* Change the max size of the cache.
*
* @param max the max size of the cache
* @returns this cache
*/
setMaxSize(max: number) {
setMaxSize(max) {
this.max = max;

while (this.order.length > this.max) {
const removedData = this.getAndRemove(this.order[0]);
if (removedData) this.onRemove(removedData);
if (this.data.size > this.max) {
this.shrink();
}

return this;
}
}

let MapLRUCache;

if (isMapSupported) {
MapLRUCache = class {
max: number
onRemove: Function
data: any
constructor(max: number, onRemove: Function) {
this.max = max;
this.onRemove = onRemove || nullOnRemove;
this.reset();
}

reset() {
if (this.data) {
const values = this.data.values();
for (const p of values) {
this.onRemove(p);
}
}

this.data = new Map();
return this;
}

clear() {
this.reset();
delete this.onRemove;
}

add(key, data) {
if (!data) {
return this;
}
if (this.has(key)) {
this.data.delete(key);
this.data.set(key, data);
// if (this.data.size > this.max) {
// this.shrink();
// }
} else {
this.data.set(key, data);
// if (this.data.size > this.max) {
// this.shrink();
// }
}

return this;
}

keys() {
const keys = new Array(this.data.size);
let i = 0;
const iterator = this.data.keys();
for (const k of iterator) {
keys[i++] = k;
}
return keys;
}


shrink() {
const iterator = this.data.keys();
let item = iterator.next();
while (this.data.size > this.max) {
const removedData = this.getAndRemove(item.value);
if (removedData) {
this.onRemove(removedData);
}
item = iterator.next();
}
}

has(key) {
return this.data.has(key);
}


getAndRemove(key) {
if (!this.has(key)) { return null; }

const data = this.data.get(key);
this.data.delete(key);
return data;
}


get(key) {
if (!this.has(key)) { return null; }

const data = this.data.get(key);
return data;
}

remove(key) {
if (!this.has(key)) { return this; }

const data = this.data.get(key);
this.data.delete(key);
this.onRemove(data);

return this;
}

setMaxSize(max) {
this.max = max;
if (this.data.size > this.max) {
this.shrink();
}
return this;
}
};
}


const LRUCache = isMapSupported ? MapLRUCache : ArrayLRUCache;

export default LRUCache;
4 changes: 2 additions & 2 deletions packages/maptalks/src/layer/tile/TileLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
Vector3,
Matrix4
} from '../../core/util';
import LRUCache, { ArrayLRUCache } from '../../core/util/LRUCache';
import LRUCache from '../../core/util/LRUCache';
import Browser from '../../core/Browser';
import Size from '../../geo/Size';
import Point from '../../geo/Point';
Expand Down Expand Up @@ -194,7 +194,7 @@ const ARR3: Vector3 = [0, 0, 0];
})
*/
class TileLayer extends Layer {
tileInfoCache: ArrayLRUCache;
tileInfoCache: LRUCache;
//@internal
_tileSize: Size;
//@internal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const TileLayerRenderable = function <T extends MixinConstructor>(Base: T) {
}[];
//@internal
_tileQueueIds: Set<LayerId>;
tileCache: typeof LRUCache;
tileCache: LRUCache;
//@internal
_compareTiles: any;
//@internal
Expand Down

0 comments on commit 72e49d5

Please sign in to comment.