"use strict"; const createArithmeticCoder = require("../arithmetic-coder"); function encodeBooleans(booleans) { let n = 1n; let bitmask = 0n; for (let boolean of booleans) { if (boolean === true) { bitmask |= n; } n *= 2n; } return bitmask; } function decodeBooleans(bitmask, count) { let n = 1n; let booleans = []; for (let i = 0; i < count; i++) { booleans.push((bitmask & n) !== 0n); n *= 2n; } return booleans; } module.exports = function createBitmaskArithmeticCoder(fields) { // NOTE: We *always* store the bitmask as the very first field, to ensure that it doesn't interfere with binary sorting order let fieldCount = BigInt(fields.length); let maximumBitmaskValue = 2n ** fieldCount; // NOTE: Exclusive let coder = createArithmeticCoder([ { name: "__bitmask", minimum: 0, maximum: maximumBitmaskValue }, ... fields ]); return { bits: coder.bits, encode: function (data) { let fieldPresence = fields.map((field) => data[field.name] != null); return coder.encode({ ... data, __bitmask: encodeBooleans(fieldPresence) }); }, decode: function (data) { let decoded = coder.decode(data); let fieldPresence = decodeBooleans(decoded.__bitmask, fields.length); fields.forEach((field, i) => { if (fieldPresence[i] === false) { decoded[field.name] = undefined; } }); delete decoded.__bitmask; return decoded; } }; };