<commit_before>// tslint:disable-next-line:no-any
export type Constructor<T = {}> = new (...args: any[]) => T;

// tslint:disable-next-line:no-any
export type CacheEntry<T = any> = {
	value: T;
	expires: number;
};

export function Cacheable<T extends Constructor>(cls: T) {
	return class extends cls {
		public cache: Map<string, CacheEntry> = new Map;

		public getFromCache(cacheKey: string): {} | undefined {
			this._cleanCache();
			if (this.cache.has(cacheKey)) {
				const entry = this.cache.get(cacheKey);

				if (entry) {
					return entry.value;
				}
			}

			return undefined;
		}

		public setCache(cacheKey: string, value: {}, timeInSeconds: number): void {
			this.cache.set(cacheKey, {
				value, expires: Date.now() + (timeInSeconds * 1000)
			});
		}

		public removeFromCache(cacheKey: string|string[], prefix?: boolean) {
			let internalCacheKey: string;
			if (typeof cacheKey === 'string') {
				internalCacheKey = cacheKey;
				if (!internalCacheKey.endsWith('/')) {
					internalCacheKey += '/';
				}
			} else {
				const propName = cacheKey.shift() as string;
				internalCacheKey = createCacheKey(propName, cacheKey, prefix);
			}
			if (prefix) {
				this.cache.forEach((val, key) => {
					if (key.startsWith(internalCacheKey)) {
						this.cache.delete(key);
					}
				});
			} else {
				this.cache.delete(internalCacheKey);
			}
		}

		public _cleanCache() {
			const now = Date.now();
			this.cache.forEach((val, key) => {
				if (val.expires < now) {
					this.cache.delete(key);
				}
			});
		}
	};
}

// tslint:disable-next-line:no-any
export function createCacheKey(propName: string, params: any[], prefix?: boolean): string {
// tslint:disable-next-line:no-any
	function createSingleCacheKey(param: any) {
		// noinspection FallThroughInSwitchStatementJS
		switch (typeof param) {
			case 'undefined': {
				return '';
			}
			case 'object': {
				if (param === null) {
					return '';
				}
				if ('cacheKey' in param) {
					return param.cacheKey;
				}
			}
			// tslint:disable-next-line:no-switch-case-fall-through
			default: {
				return param.toString();
			}
		}
	}
	// tslint:disable-next-line:no-any
	return [propName, ...params.map(createSingleCacheKey)].join('/') + (prefix ? '/' : '');
}

export function Cached(timeInSeconds: number = Infinity, cacheFailures: boolean = false) {
	// tslint:disable-next-line:no-any
	return function (target: any, propName: string, descriptor: PropertyDescriptor) {
		const origFn = descriptor.value;

		// tslint:disable-next-line:no-any
		descriptor.value = async function (this: any, ...params: any[]) {
			const cacheKey = createCacheKey(propName, params);
			const cachedValue = this.getFromCache(cacheKey);

			if (cachedValue) {
				return cachedValue;
			}

			const result = await origFn.apply(this, params);
			if (result !== undefined || cacheFailures) {
				this.setCache(cacheKey, result, timeInSeconds);
			}
			return result;
		};

		return descriptor;
	};
}

export function CachedGetter(timeInSeconds: number = Infinity) {
	// tslint:disable-next-line:no-any
	return function (target: any, propName: string, descriptor: PropertyDescriptor) {
		if (descriptor.get) {
			const origFn = descriptor.get;

			// tslint:disable-next-line:no-any
			descriptor.get = function (this: any, ...params: any[]) {
				const cacheKey = createCacheKey(propName, params);
				const cachedValue = this.getFromCache(cacheKey);

				if (cachedValue) {
					return cachedValue;
				}

				const result = origFn.apply(this, params);
				this.setCache(cacheKey, result, timeInSeconds);
				return result;
			};
		}

		return descriptor;
	};
}

export function ClearsCache<T>(cacheName: keyof T, numberOfArguments?: number) {
	// tslint:disable-next-line:no-any
	return function (target: any, propName: string, descriptor: PropertyDescriptor) {
		const origFn = descriptor.value;

		// tslint:disable-next-line:no-any
		descriptor.value = async function (this: any, ...params: any[]) {
			const result = await origFn.apply(this, params);
			const args = numberOfArguments === undefined ? params.slice() : params.slice(0, numberOfArguments);
			this.removeFromCache([cacheName, ...args], true);
			return result;
		};

		return descriptor;
	};
}

// tslint:disable-next-line:no-any
export function NonEnumerable(target: any, key: string) {
	// first property defined in prototype, that's why we use getters/setters
	// (otherwise assignment in object will override property in prototype)
	Object.defineProperty(target, key, {
		get: function () {
			return undefined;
		},
		// tslint:disable-next-line:no-any
		set: function (this: any, val: any) {
			// here we have reference to instance and can set property directly to it
			Object.defineProperty(this, key, {
				value: val,
				writable: true,
				enumerable: false,
			});
		},

		enumerable: false,
	});
}<commit_msg>regard null and undefined as the same<commit_after>// tslint:disable-next-line:no-any
export type Constructor<T = {}> = new (...args: any[]) => T;

// tslint:disable-next-line:no-any
export type CacheEntry<T = any> = {
	value: T;
	expires: number;
};

export function Cacheable<T extends Constructor>(cls: T) {
	return class extends cls {
		public cache: Map<string, CacheEntry> = new Map;

		public getFromCache(cacheKey: string): {} | undefined {
			this._cleanCache();
			if (this.cache.has(cacheKey)) {
				const entry = this.cache.get(cacheKey);

				if (entry) {
					return entry.value;
				}
			}

			return undefined;
		}

		public setCache(cacheKey: string, value: {}, timeInSeconds: number): void {
			this.cache.set(cacheKey, {
				value, expires: Date.now() + (timeInSeconds * 1000)
			});
		}

		public removeFromCache(cacheKey: string|string[], prefix?: boolean) {
			let internalCacheKey: string;
			if (typeof cacheKey === 'string') {
				internalCacheKey = cacheKey;
				if (!internalCacheKey.endsWith('/')) {
					internalCacheKey += '/';
				}
			} else {
				const propName = cacheKey.shift() as string;
				internalCacheKey = createCacheKey(propName, cacheKey, prefix);
			}
			if (prefix) {
				this.cache.forEach((val, key) => {
					if (key.startsWith(internalCacheKey)) {
						this.cache.delete(key);
					}
				});
			} else {
				this.cache.delete(internalCacheKey);
			}
		}

		public _cleanCache() {
			const now = Date.now();
			this.cache.forEach((val, key) => {
				if (val.expires < now) {
					this.cache.delete(key);
				}
			});
		}
	};
}

// tslint:disable-next-line:no-any
export function createCacheKey(propName: string, params: any[], prefix?: boolean): string {
// tslint:disable-next-line:no-any
	function createSingleCacheKey(param: any) {
		// noinspection FallThroughInSwitchStatementJS
		switch (typeof param) {
			case 'undefined': {
				return '';
			}
			case 'object': {
				if (param === null) {
					return '';
				}
				if ('cacheKey' in param) {
					return param.cacheKey;
				}
			}
			// tslint:disable-next-line:no-switch-case-fall-through
			default: {
				return param.toString();
			}
		}
	}
	// tslint:disable-next-line:no-any
	return [propName, ...params.map(createSingleCacheKey)].join('/') + (prefix ? '/' : '');
}

export function Cached(timeInSeconds: number = Infinity, cacheFailures: boolean = false) {
	// tslint:disable-next-line:no-any
	return function (target: any, propName: string, descriptor: PropertyDescriptor) {
		const origFn = descriptor.value;

		// tslint:disable-next-line:no-any
		descriptor.value = async function (this: any, ...params: any[]) {
			const cacheKey = createCacheKey(propName, params);
			const cachedValue = this.getFromCache(cacheKey);

			if (cachedValue) {
				return cachedValue;
			}

			const result = await origFn.apply(this, params);
			if (result != null || cacheFailures) {
				this.setCache(cacheKey, result, timeInSeconds);
			}
			return result;
		};

		return descriptor;
	};
}

export function CachedGetter(timeInSeconds: number = Infinity) {
	// tslint:disable-next-line:no-any
	return function (target: any, propName: string, descriptor: PropertyDescriptor) {
		if (descriptor.get) {
			const origFn = descriptor.get;

			// tslint:disable-next-line:no-any
			descriptor.get = function (this: any, ...params: any[]) {
				const cacheKey = createCacheKey(propName, params);
				const cachedValue = this.getFromCache(cacheKey);

				if (cachedValue) {
					return cachedValue;
				}

				const result = origFn.apply(this, params);
				this.setCache(cacheKey, result, timeInSeconds);
				return result;
			};
		}

		return descriptor;
	};
}

export function ClearsCache<T>(cacheName: keyof T, numberOfArguments?: number) {
	// tslint:disable-next-line:no-any
	return function (target: any, propName: string, descriptor: PropertyDescriptor) {
		const origFn = descriptor.value;

		// tslint:disable-next-line:no-any
		descriptor.value = async function (this: any, ...params: any[]) {
			const result = await origFn.apply(this, params);
			const args = numberOfArguments == null ? params.slice() : params.slice(0, numberOfArguments);
			this.removeFromCache([cacheName, ...args], true);
			return result;
		};

		return descriptor;
	};
}

// tslint:disable-next-line:no-any
export function NonEnumerable(target: any, key: string) {
	// first property defined in prototype, that's why we use getters/setters
	// (otherwise assignment in object will override property in prototype)
	Object.defineProperty(target, key, {
		get: function () {
			return undefined;
		},
		// tslint:disable-next-line:no-any
		set: function (this: any, val: any) {
			// here we have reference to instance and can set property directly to it
			Object.defineProperty(this, key, {
				value: val,
				writable: true,
				enumerable: false,
			});
		},

		enumerable: false,
	});
}
{'fraction_non_alphanumeric': 0.1420349434737924, 'fraction_numerical': 0.0010277492291880781, 'mean_word_length': 3.4699127239320164, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2819041', 'n_tokens_mistral': 3656, 'n_tokens_neox': 3108, 'n_words': 734}