node-hitomi
Version:
Hitomi.la API for Node.js
114 lines (111 loc) • 2.91 kB
JavaScript
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 };