diff --git a/src/contract/highloadWallet/HighloadQueryId.js b/src/contract/highloadWallet/HighloadQueryId.js new file mode 100644 index 0000000..4c96929 --- /dev/null +++ b/src/contract/highloadWallet/HighloadQueryId.js @@ -0,0 +1,105 @@ +const BIT_NUMBER_SIZE = 10n; // 10 bit +const SHIFT_SIZE = 13n; // 13 bit +const MAX_BIT_NUMBER = 1022n; +const MAX_SHIFT = 8191n; // 2^13 = 8192 + +class HighloadQueryId { + constructor() { + /** + * @private + * @type {bigint} [0 .. 8191] + */ + this.shift = 0n; + /** + * @private + * @type {bigint} [0 .. 1022] + */ + this.bitnumber = 0n; + } + + /** + * @param shift {bigint} + * @param bitnumber {bigint} + * @return {HighloadQueryId} + */ + static fromShiftAndBitNumber(shift, bitnumber) { + const q = new HighloadQueryId(); + q.shift = shift; + if (q.shift < 0) throw new Error('invalid shift'); + if (q.shift > MAX_SHIFT) throw new Error('invalid shift'); + q.bitnumber = bitnumber; + if (q.bitnumber < 0) throw new Error('invalid bitnumber'); + if (q.bitnumber > MAX_BIT_NUMBER) throw new Error('invalid bitnumber'); + return q; + } + + increase() { + this.bitnumber += 1n; + + if (this.shift === MAX_SHIFT && this.bitnumber > (MAX_BIT_NUMBER - 1n)) { + throw new Error('Overload'); // NOTE: we left one queryId for emergency withdraw + } + + if (this.bitnumber > MAX_BIT_NUMBER) { + this.bitnumber = 0n; + this.shift += 1n; + if (this.shift > MAX_SHIFT) { + throw new Error('Overload') + } + } + } + + isEnd() { + return this.bitnumber >= (MAX_BIT_NUMBER - 1n) && this.shift === MAX_SHIFT; // NOTE: we left one queryId for emergency withdraw + } + + /** + * @return {bigint} + */ + getShift() { + return this.shift; + } + + /** + * @return {bigint} + */ + getBitNumber() { + return this.bitnumber; + } + + /** + * @return {bigint} + */ + getQueryId() { + return (this.shift << BIT_NUMBER_SIZE) + this.bitnumber; + } + + /** + * @param queryId {bigint} + * @return {HighloadQueryId} + */ + static fromQueryId(queryId) { + const shift = queryId >> BIT_NUMBER_SIZE; + const bitnumber = queryId & 1023n; + return this.fromShiftAndBitNumber(shift, bitnumber); + } + + /** + * @param i {bigint} + * @return {HighloadQueryId} + */ + static fromSeqno(i) { + const shift = i / 1023n; + const bitnumber = i % 1023n; + return this.fromShiftAndBitNumber(shift, bitnumber); + } + + /** + * @return {bigint} [0 .. 8380415] + */ + toSeqno() { + return this.bitnumber + this.shift * 1023n; + } +} + +module.exports = {HighloadQueryId}; diff --git a/src/test-highload-query-id.js b/src/test-highload-query-id.js new file mode 100644 index 0000000..e1b54dd --- /dev/null +++ b/src/test-highload-query-id.js @@ -0,0 +1,41 @@ +const {HighloadQueryId} = require("./contract/highloadWallet/HighloadQueryId"); + +if (HighloadQueryId.fromSeqno(0n).toSeqno() !== 0n) throw new Error(); + +const i = HighloadQueryId.fromSeqno(1022n); +if (i.toSeqno() !== 1022n) throw new Error(); +const i2 = HighloadQueryId.fromSeqno(1023n); +if (i2.toSeqno() !== 1023n) throw new Error(); +const i3 = HighloadQueryId.fromSeqno(1024n); +if (i3.toSeqno() !== 1024n) throw new Error(); +const i4 = HighloadQueryId.fromSeqno(8380415n); +if (i4.toSeqno() !== 8380415n) throw new Error(); + + +const queryId = new HighloadQueryId(); +console.log(queryId.getQueryId(), queryId.isEnd()); + +const MAX = (2n ** 13n) * 1023n - 2n; +for (let i = 0; i < MAX; i++) { + queryId.increase(); + + const q = queryId.getQueryId(); + const q2 = HighloadQueryId.fromQueryId(q); + + if (queryId.getShift() !== q2.getShift()) throw new Error() + if (queryId.getBitNumber() !== q2.getBitNumber()) throw new Error() + if (q2.getQueryId() !== q) throw new Error(); + + const q3 = HighloadQueryId.fromShiftAndBitNumber(queryId.getShift(), queryId.getBitNumber()); + if (queryId.getShift() !== q3.getShift()) throw new Error() + if (queryId.getBitNumber() !== q3.getBitNumber()) throw new Error() + if (q3.getQueryId() !== q) throw new Error(); + + if (queryId.isEnd()) { + console.log('END') + } +} +console.log(queryId.shift); +console.log(queryId.bitnumber); + +console.log(queryId.getQueryId(), queryId.isEnd()); \ No newline at end of file