Skip to content

Commit

Permalink
feat(infra): rework project struct, configure TSC, emit *.d.ts
Browse files Browse the repository at this point in the history
  • Loading branch information
zhibirc committed Nov 29, 2023
1 parent f105c42 commit 39547fc
Show file tree
Hide file tree
Showing 25 changed files with 519 additions and 117 deletions.
29 changes: 12 additions & 17 deletions fifo-cache/src/types.ts → fifo-cache/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ interface IFifoCache {
store: (key: any, value: any) => void;
has: (key: any) => boolean;
clear: () => void;
};

}
type TConfigOptions = {
/**
* Capacity means how many items can be stored at the same time in cache.
Expand All @@ -15,18 +14,14 @@ type TConfigOptions = {
*/
capacity: number;
};

type TNode = {
data: {
key: any,
value: any
};
next?: TNode;
};


export {
IFifoCache,
TConfigOptions,
TNode
};
declare class FifoCache implements IFifoCache {
#private;
constructor(options: TConfigOptions);
get size(): number;
set capacity(value: number);
read(key: any): any;
store(key: any, value: any): void;
has(key: any): boolean;
clear(): void;
}
export default FifoCache;
66 changes: 66 additions & 0 deletions fifo-cache/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
;
const Node = (data, next) => ({ data, next });
class FifoCache {
#capacity;
#store;
#map;
constructor(options) {
const { capacity } = options;
if (!Number.isInteger(capacity) || capacity <= 0)
throw new Error('invalid "capacity": positive integer expected');
this.#capacity = capacity;
// Store is a Singly linked list to support fast add (to tail) and delete (from head) records.
this.#store = { head: null, tail: null };
// Struct for fast access to cache records, use as <key:reference> lookup table.
this.#map = new Map();
}
get size() {
return this.#map.size;
}
set capacity(value) {
if (!Number.isInteger(value) || value <= 0)
throw new Error('invalid "capacity": positive integer expected');
if (value < this.#capacity) {
// @todo: implement
}
this.#capacity = value;
}
read(key) {
if (this.#map.has(key)) {
return this.#map.get(key)?.data.value;
}
return null;
}
store(key, value) {
// check if cache capacity limit is reached
if (this.#map.size === this.#capacity) {
// evict head since we are out of capacity
const node = this.#store.head;
this.#store.head = node.next;
this.#map.delete(node.data.key);
node.next = null;
}
const node = Node({ key, value });
if (this.#map.size === 0) {
this.#store.head = node;
}
else if (this.#map.size === 1) {
this.#store.tail = node;
this.#store.head.next = this.#store.tail;
}
else {
this.#store.tail.next = node;
this.#store.tail = node;
}
this.#map.set(key, node);
}
has(key) {
return this.#map.has(key);
}
clear() {
this.#store.head = null;
this.#store.tail = null;
this.#map.clear();
}
}
export default FifoCache;
28 changes: 26 additions & 2 deletions fifo-cache/src/index.ts → fifo-cache/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,28 @@
import { IFifoCache, TConfigOptions, TNode } from './types.js';
interface IFifoCache {
get size(): number;
set capacity(value: number);
read: (key: any) => any;
store: (key: any, value: any) => void;
has: (key: any) => boolean;
clear: () => void;
};

type TConfigOptions = {
/**
* Capacity means how many items can be stored at the same time in cache.
* For FIFO cache, by definition, capacity is a required restriction,
* without it becomes almost meaningless, so this option is mandatory.
*/
capacity: number;
};

type TNode = {
data: {
key: any,
value: any
};
next?: TNode;
};

const Node = (data: {key: any, value: any}, next?: TNode) => ({data, next});

Expand All @@ -20,7 +44,7 @@ class FifoCache implements IFifoCache {
}

get size() {
return this.#store.size;
return this.#map.size;
}

set capacity (value: number) {
Expand Down
1 change: 1 addition & 0 deletions lfu-cache/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
1 change: 1 addition & 0 deletions lfu-cache/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
File renamed without changes.
Empty file removed lfu-cache/src/types.ts
Empty file.
21 changes: 13 additions & 8 deletions lru-cache/src/types.ts → lru-cache/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ interface ILruCache {
has: (key: any) => boolean;
remove: (key: any) => void;
clear: () => void;
};

}
type TConfigOptions = {
/**
* Capacity means how many items can be stored at the same time in cache.
Expand All @@ -23,9 +22,15 @@ type TConfigOptions = {
*/
checkLowMemory?: boolean;
};


export {
ILruCache,
TConfigOptions
};
declare class LruCache implements ILruCache {
#private;
constructor(options: TConfigOptions);
get size(): number;
set capacity(value: number);
read(key: any): any;
store(key: any, value: any): void;
has(key: any): boolean;
remove(key: any): void;
clear(): void;
}
export default LruCache;
73 changes: 73 additions & 0 deletions lru-cache/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
;
const getTimestamp = () => Date.now();
class LruCache {
#capacity;
#store;
#accessMap;
constructor(options) {
const { capacity, checkLowMemory } = options;
let checkLowMemoryTimer;
if (!Number.isInteger(capacity) || capacity <= 0)
throw new Error('invalid "capacity": positive integer expected');
if (typeof checkLowMemory !== 'boolean')
throw new Error('option "checkLowMemory" must be boolean if provided');
this.#capacity = capacity;
if (checkLowMemory) {
checkLowMemoryTimer = setInterval(() => {
// @todo
}, 5000);
// checkLowMemoryTimer.unref?.();
}
this.#store = new Map();
// struct for tracking of access to cache items
this.#accessMap = new Map();
}
get size() {
return this.#store.size;
}
set capacity(value) {
if (!Number.isInteger(value) || value <= 0)
throw new Error('invalid "capacity": positive integer expected');
this.#capacity = value;
}
read(key) {
if (this.#store.has(key)) {
// update access time
this.#accessMap.set(key, getTimestamp());
return this.#store.get(key);
}
return null;
}
store(key, value) {
const timestamp = getTimestamp();
// check if cache size limit is reached
if (this.#store.size === this.#capacity) {
// first, get item with the lowest priority (oldest based on timestamp)
const min = {
key: null,
value: +Infinity
};
for (const [itemKey, itemValue] of this.#accessMap) {
if (itemValue < min.value) {
min.key = itemKey;
min.value = itemValue;
}
}
this.#accessMap.delete(min.key);
this.#store.delete(min.key);
}
this.#accessMap.set(key, timestamp);
this.#store.set(key, value);
}
has(key) {
return this.#store.has(key);
}
remove(key) {
this.#store.delete(key);
}
clear() {
this.#store.clear();
this.#accessMap.clear();
}
}
export default LruCache;
26 changes: 25 additions & 1 deletion lru-cache/src/index.ts → lru-cache/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,28 @@
import { ILruCache, TConfigOptions } from './types.js';
interface ILruCache {
get size(): number;
set capacity(value: number);
read: (key: any) => any;
store: (key: any, value: any) => void;
has: (key: any) => boolean;
remove: (key: any) => void;
clear: () => void;
};

type TConfigOptions = {
/**
* Capacity means how many items can be stored at the same time in cache.
* For LRU cache, by definition, capacity is a required restriction,
* without it becomes almost meaningless, so this option is mandatory.
*/
capacity: number;
/**
* Detect low memory situation and act accordingly: clear cache to free up memory
* and prevent storing new records temporarily.
* If such situation is just hypothetical, it's better to not enable this feature
* to not waste system resources.
*/
checkLowMemory?: boolean;
};

const getTimestamp = () => Date.now();

Expand Down
57 changes: 57 additions & 0 deletions rr-cache/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
type TStats = {
size: number;
capacity: number;
locked: boolean;
};
interface IRRCache {
get stats(): TStats;
set locked(state: boolean);
read: (key: any) => any;
add: (key: any, value: any) => void;
has: (key: any) => boolean;
remove: (key: any) => void;
clear: () => void;
}
type TConfigOptions = {
/**
* Capacity means how many items can be stored at the same time in cache.
* For RR cache, by definition, capacity is a required restriction,
* without it becomes almost meaningless, so this option is mandatory.
*/
capacity: number;
};
declare class RRCache implements IRRCache {
#private;
constructor(options: TConfigOptions);
get stats(): {
size: number;
capacity: number;
locked: boolean;
};
set locked(state: boolean);
/**
* Read value stored in cache by assosiated key.
* @param {*} key - cache record's key
* @return {*|null} record's value retrieved by key or null if record is absent
*/
read(key: any): any;
add(key: any, value: any): void;
/**
* Check if record by given key exists in cache.
* @param {*} key - cache record's key
* @return {boolean} return true if record is in the cache
*/
has(key: any): boolean;
/**
* Remove an item from the cache.
* @param {*} key - cache record's key
* @return {void}
*/
remove(key: any): void;
/**
* Remove all items from the cache.
* @return {void}
*/
clear(): void;
}
export default RRCache;
Loading

0 comments on commit 39547fc

Please sign in to comment.