UNPKG

node-hitomi

Version:
114 lines (111 loc) 2.91 kB
import { HitomiError, ErrorCode } from '../structures/error.mjs'; import { Base } from './base.mjs'; import { RESOURCE_DOMAIN } from './constants.mjs'; class Provider extends Base { constructor(hitomi, fetch, maximumAge) { super(hitomi); this.fetch = fetch; this.maximumAge = maximumAge; this.expiresAt = 0; } async retrieve() { if(Date.now() > this.expiresAt) { if(this.promise) { return this.promise; } try { this.value = await (this.promise = this.fetch()); if(this.maximumAge) { this.expiresAt = Date.now() + this.maximumAge; } } finally { this.promise = undefined; } } return this.value; } } class IndexProvider extends Provider { static compareBuffers(a, b) { const length = a.byteLength < b.byteLength ? a.byteLength : b.byteLength; for(let i = 0; i < length; i++) { if(a[i] < b[i]) { return -1; } if(a[i] > b[i]) { return 1; } } return 0; } constructor(hitomi, field) { const path = '/' + field + 'index/version'; super(hitomi, function () { return this.hitomi.request(RESOURCE_DOMAIN, path, 2); }, hitomi.indexMaximumAge); this.field = field; } async getNodeAtAddress(address, version) { const view = await this.hitomi.request(RESOURCE_DOMAIN, '/' + this.field + 'index/' + this.field + '.' + version + '.index', 1, address + '-' + (address + 463n)); if(!view.byteLength) { return; } const node = [[], [], []]; const keyCount = view.getInt32(0); let offset = 4; let i; for(i = 0; i < keyCount; i++) { const keySize = view.getInt32(offset); if(keySize < 1 || keySize > 31) { throw new HitomiError(ErrorCode.UnexpectedResponseBody, 'KeySize', 'between 1 and 31'); } node[0].push(new Uint8Array(view.buffer, view.byteOffset + (offset += 4), keySize)); offset += keySize; } const dataCount = view.getInt32(offset); offset += 4; for(i = 0; i < dataCount; i++) { node[1].push([view.getBigUint64(offset), view.getInt32(offset + 8)]); offset += 12; } for(i = 0; i < 17; i++) { node[2].push(view.getBigUint64(offset)); offset += 8; } return node; } async binarySearch(key, node, version) { if(!node[0].length) { return; } let compareResult = -1; let index = 0; for(; index < node[0].length && (compareResult = IndexProvider.compareBuffers(key, node[0][index])) === 1; index++) ; if(!compareResult) { return node[1][index]; } if(!node[2][index]) { return; } let j = 0; for(; j < node[2].length; j++) { if(node[2][j]) { break; } } if(j === node[2].length) { return; } if(!node[2][index]) { throw new HitomiError(ErrorCode.UnexpectedResponseBody, 'SubnodeAddress', '0', false); } const subnode = await this.getNodeAtAddress(node[2][index], version); if(!subnode) { return; } return this.binarySearch(key, subnode, version); } } export { IndexProvider, Provider };