"use strict"; const assert = require("assert"); const bigintBuffer = require("../bigint-buffer"); const createArithmeticCoder = require("./bitmask-arithmetic"); module.exports = function createDurationEncoder() { // 55 bits for value + 1 bit for sign = 56 bits = 7 bytes // NOTE: Maximums are exclusive! let coder = createArithmeticCoder([ { name: "milliseconds", minimum: 0, maximum: 1000 }, { name: "seconds", minimum: 0, maximum: 64 }, // No, this is not a typo; leap seconds are a thing { name: "minutes", minimum: 0, maximum: 60 }, { name: "hours", minimum: 0, maximum: 25 }, { name: "days", minimum: 0, maximum: 31 }, { name: "months", minimum: 0, maximum: 12 }, { name: "years", minimum: 0, maximum: 8000 }, ]); let zeroPoint = 2n ** coder.bits; // first bit is a 1 return { bits: coder.bits + 1n, encode: function (value) { let { negative, ... rest } = value; let number = coder.encode(rest); // NOTE: This approach ensures that values are correctly sorted even in their buffer representation! let signedNumber = (negative) ? zeroPoint - number : zeroPoint + number; return { value: bigintBuffer.toBuffer(signedNumber), auxiliaryBlob: undefined }; }, decode: function (bytes) { let signedNumber = bigintBuffer.toBigint(bytes); let negative = (signedNumber < zeroPoint); let number = (negative) ? zeroPoint - signedNumber : signedNumber - zeroPoint; // FIXME: Update to new decode API let decoded = coder.decode(number); return { ... decoded, negative: negative }; } }; }; // let coder = module.exports(); // let data1 = { // negative: true, // seconds: 1n // }; // let data2 = { // negative: false, // seconds: 1n // }; // let encoded1 = coder.encode(data1); // let encoded2 = coder.encode(data2); // console.log({ encoded1, encoded2 }); // let decoded1 = coder.decode(encoded1); // let decoded2 = coder.decode(encoded2); // console.log({ decoded1, decoded2 }); // assert(Buffer.compare(encoded1, encoded2) === -1); // assert.deepStrictEqual(data1, stripUndefined(decoded1)); // assert.deepStrictEqual(data2, stripUndefined(decoded2)); // function stripUndefined(object) { // let newObject = {}; // for (let [ key, value ] of Object.entries(object)) { // if (value !== undefined) { // newObject[key] = value; // } // } // return newObject; // }