asclasit
Version:
ASync CLasses + ASync ITerators
193 lines (163 loc) • 5.28 kB
JavaScript
const $ = require('../func');
const Iter = require('../iter');
const AsIt = require('../as-it');
const {func_} = $;
function dropCache() {
this.cache.map = null;
}
function getFromCache(func, value) {
const at = func.cache.map;
if (at.has(value)) {
const res = at.get(value);
at.delete(value);
at.set(value, res);
return {res};
}
}
function checkOver(func) {
const {cache} = func;
const over = cache.map.size - cache.size;
if (over > 0) Iter.keys(cache.map).take(over).unset(cache.map).exec();
}
function getCacheFunc(mapper, desc) {
let func;
if (mapper.constructor === $.AsyncFunction) {
func = async function _cache(value) {
const {cache} = func;
if (cache.map) {
const cached = getFromCache(func, value);
if (cached) return cached.res;
} else cache.map = new Map();
const mapped = await mapper.call(this, value, value, desc);
cache.map.set(value, mapped);
checkOver(func);
return mapped;
};
} else {
func = function _cache(value) {
const {cache} = func;
if (cache.map) {
const cached = getFromCache(func, value);
if (cached) return cached.res;
} else cache.map = new Map();
const mapped = mapper.call(this, value, value, desc);
cache.map.set(value, mapped);
checkOver(func);
return mapped;
};
}
return func;
}
$.defaultCacheSize = 200;
func_(function cache_(size, mapper) {
if (typeof mapper !== 'function') throw new TypeError('mapper is not function');
if (!Number.isFinite(size) || size <= 0) size = $.defaultCacheSize;
const desc = {ctx: this};
const func = getCacheFunc(mapper, desc);
func.cache = {size, map: null};
func.cacheMapper = mapper;
func.dropCache = dropCache;
func.toChunkCache = toChunkCache;
return func;
});
function getChunkFromCache(func, chunk) {
const {cache, type} = func;
const isArray = type === Array;
let res, toOut;
if (!cache.map) cache.map = new Map();
const need = new Map();
const iter = Iter.entries(chunk instanceof Array ? Iter.getIter(chunk) : chunk);
if (isArray) {
iter.mapKey($.counter_(0));
res = chunk instanceof Array ? Array(chunk.length) : [];
toOut = Iter.toObject;
} else {
res = ($.accInit.get(type) || $)();
toOut = Iter.toResult;
}
for (const [key, value] of iter) {
const cached = getFromCache(func, value);
if (cached) toOut([[key, cached.res]], res);
else $.defMap(need, value, Set).add(key);
}
return {res, need};
}
function getChunkCacheFunc(mapper, desc) {
let func;
if (mapper.constructor === $.AsyncFunction) {
func = async function _cacheChunk(chunk) {
const {res, need} = getChunkFromCache(func, chunk);
if (!need.size) return res;
const {cache} = func;
const mapped = await mapper.call(this, need.keys(), chunk, desc);
const iter = Iter.from(need).mapKey(mapped).maps(([k, v]) => Iter.entries(v).mapValue($._(k)));
if (func.type === Array) iter.toObject(res); else iter.toResult(res);
Iter.from(mapped).toMap(cache.map);
checkOver(func);
return res;
};
} else {
func = function _cacheChunk(chunk) {
const {res, need} = getChunkFromCache(func, chunk);
if (!need.size) return res;
const {cache} = func;
const mapped = mapper.call(this, need.keys(), chunk, desc);
const iter = Iter.from(need).mapKey(mapped).maps(([k, v]) => Iter.entries(v).mapValue($._(k)));
if (func.type === Array) iter.toObject(res); else iter.toResult(res);
Iter.from(mapped).toMap(cache.map);
checkOver(func);
return res;
};
}
return func;
}
function emulMapper(mapper) {
if (mapper.constructor === $.AsyncFunction) {
return async (value) => {
const map = await mapper.call(this, new Set([value]));
if (map instanceof Map) return map.get(value);
return map[value];
}
} else {
return (value) => {
const map = mapper.call(this, new Set([value]));
if (map instanceof Map) return map.get(value);
return map[value];
}
}
}
function toCache(mapper, ctx) {
if (!mapper) mapper = emulMapper(this.cacheMapper);
const func = getCacheFunc(mapper, {ctx});
func.cache = this.cache;
func.dropCache = dropCache;
return func;
}
function emulChunkMapper(mapper) {
if (mapper.constructor === $.AsyncFunction) {
return async chunk => await AsIt.from(chunk).map(2).mapValue(mapper).toMap();
} else {
return chunk => Iter.from(chunk).map(2).mapValue(mapper).toMap();
}
}
function toChunkCache(type, mapper, ctx) {
if (!mapper) mapper = emulChunkMapper(this.cacheMapper);
const func = getChunkCacheFunc(mapper, {ctx});
func.type = type;
func.cache = this.cache;
func.dropCache = dropCache;
return func;
}
func_(function cacheChunk_(size, mapper, type) {
if (typeof mapper !== 'function') throw new TypeError('mapper is not function');
if (!Number.isFinite(size) || size <= 0) size = $.defaultCacheSize;
const desc = {ctx: this};
const func = getChunkCacheFunc(mapper, desc);
func.type = type;
func.cache = {size, map: null};
func.cacheMapper = mapper;
func.dropCache = dropCache;
func.toCache = toCache;
return func;
});
module.exports = $;