buddy

node MVC discord bot
Log | Files | Refs | README

BitField.js (4449B)


      1 'use strict';
      2 
      3 const { RangeError } = require('../errors');
      4 
      5 /**
      6  * Data structure that makes it easy to interact with a bitfield.
      7  */
      8 class BitField {
      9   /**
     10    * @param {BitFieldResolvable} [bits=0] Bit(s) to read from
     11    */
     12   constructor(bits) {
     13     /**
     14      * Bitfield of the packed bits
     15      * @type {number}
     16      */
     17     this.bitfield = this.constructor.resolve(bits);
     18   }
     19 
     20   /**
     21    * Checks whether the bitfield has a bit, or any of multiple bits.
     22    * @param {BitFieldResolvable} bit Bit(s) to check for
     23    * @returns {boolean}
     24    */
     25   any(bit) {
     26     return (this.bitfield & this.constructor.resolve(bit)) !== 0;
     27   }
     28 
     29   /**
     30    * Checks if this bitfield equals another
     31    * @param {BitFieldResolvable} bit Bit(s) to check for
     32    * @returns {boolean}
     33    */
     34   equals(bit) {
     35     return this.bitfield === this.constructor.resolve(bit);
     36   }
     37 
     38   /**
     39    * Checks whether the bitfield has a bit, or multiple bits.
     40    * @param {BitFieldResolvable} bit Bit(s) to check for
     41    * @returns {boolean}
     42    */
     43   has(bit) {
     44     if (Array.isArray(bit)) return bit.every(p => this.has(p));
     45     bit = this.constructor.resolve(bit);
     46     return (this.bitfield & bit) === bit;
     47   }
     48 
     49   /**
     50    * Gets all given bits that are missing from the bitfield.
     51    * @param {BitFieldResolvable} bits Bit(s) to check for
     52    * @param {...*} hasParams Additional parameters for the has method, if any
     53    * @returns {string[]}
     54    */
     55   missing(bits, ...hasParams) {
     56     if (!Array.isArray(bits)) bits = new this.constructor(bits).toArray(false);
     57     return bits.filter(p => !this.has(p, ...hasParams));
     58   }
     59 
     60   /**
     61    * Freezes these bits, making them immutable.
     62    * @returns {Readonly<BitField>} These bits
     63    */
     64   freeze() {
     65     return Object.freeze(this);
     66   }
     67 
     68   /**
     69    * Adds bits to these ones.
     70    * @param {...BitFieldResolvable} [bits] Bits to add
     71    * @returns {BitField} These bits or new BitField if the instance is frozen.
     72    */
     73   add(...bits) {
     74     let total = 0;
     75     for (const bit of bits) {
     76       total |= this.constructor.resolve(bit);
     77     }
     78     if (Object.isFrozen(this)) return new this.constructor(this.bitfield | total);
     79     this.bitfield |= total;
     80     return this;
     81   }
     82 
     83   /**
     84    * Removes bits from these.
     85    * @param {...BitFieldResolvable} [bits] Bits to remove
     86    * @returns {BitField} These bits or new BitField if the instance is frozen.
     87    */
     88   remove(...bits) {
     89     let total = 0;
     90     for (const bit of bits) {
     91       total |= this.constructor.resolve(bit);
     92     }
     93     if (Object.isFrozen(this)) return new this.constructor(this.bitfield & ~total);
     94     this.bitfield &= ~total;
     95     return this;
     96   }
     97 
     98   /**
     99    * Gets an object mapping field names to a {@link boolean} indicating whether the
    100    * bit is available.
    101    * @param {...*} hasParams Additional parameters for the has method, if any
    102    * @returns {Object}
    103    */
    104   serialize(...hasParams) {
    105     const serialized = {};
    106     for (const [flag, bit] of Object.entries(this.constructor.FLAGS)) serialized[flag] = this.has(bit, ...hasParams);
    107     return serialized;
    108   }
    109 
    110   /**
    111    * Gets an {@link Array} of bitfield names based on the bits available.
    112    * @param {...*} hasParams Additional parameters for the has method, if any
    113    * @returns {string[]}
    114    */
    115   toArray(...hasParams) {
    116     return Object.keys(this.constructor.FLAGS).filter(bit => this.has(bit, ...hasParams));
    117   }
    118 
    119   toJSON() {
    120     return this.bitfield;
    121   }
    122 
    123   valueOf() {
    124     return this.bitfield;
    125   }
    126 
    127   *[Symbol.iterator]() {
    128     yield* this.toArray();
    129   }
    130 
    131   /**
    132    * Data that can be resolved to give a bitfield. This can be:
    133    * * A string (see {@link BitField.FLAGS})
    134    * * A bit number
    135    * * An instance of BitField
    136    * * An Array of BitFieldResolvable
    137    * @typedef {string|number|BitField|BitFieldResolvable[]} BitFieldResolvable
    138    */
    139 
    140   /**
    141    * Resolves bitfields to their numeric form.
    142    * @param {BitFieldResolvable} [bit=0] - bit(s) to resolve
    143    * @returns {number}
    144    */
    145   static resolve(bit = 0) {
    146     if (typeof bit === 'number' && bit >= 0) return bit;
    147     if (bit instanceof BitField) return bit.bitfield;
    148     if (Array.isArray(bit)) return bit.map(p => this.resolve(p)).reduce((prev, p) => prev | p, 0);
    149     if (typeof bit === 'string' && typeof this.FLAGS[bit] !== 'undefined') return this.FLAGS[bit];
    150     throw new RangeError('BITFIELD_INVALID');
    151   }
    152 }
    153 
    154 /**
    155  * Numeric bitfield flags.
    156  * <info>Defined in extension classes</info>
    157  * @type {Object}
    158  * @abstract
    159  */
    160 BitField.FLAGS = {};
    161 
    162 module.exports = BitField;