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;