BitField.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. /*************************************************************
  2. *
  3. * Copyright (c) 2018-2022 The MathJax Consortium
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /**
  18. * @fileoverview Implements bit-fields with extendable field names
  19. *
  20. * @author dpvc@mathjax.org (Davide Cervone)
  21. */
  22. export class BitField {
  23. /**
  24. * The largest bit available
  25. */
  26. protected static MAXBIT = 1 << 31;
  27. /**
  28. * The next bit to be allocated
  29. */
  30. protected static next: number = 1;
  31. /**
  32. * The map of names to bit positions
  33. */
  34. protected static names: Map<string, number> = new Map();
  35. /**
  36. * The bits that are set
  37. */
  38. protected bits: number = 0;
  39. /**
  40. * @param {string} names The names of the bit positions to reserve
  41. */
  42. public static allocate(...names: string[]) {
  43. for (const name of names) {
  44. if (this.has(name)) {
  45. throw new Error('Bit already allocated for ' + name);
  46. }
  47. if (this.next === BitField.MAXBIT) {
  48. throw new Error('Maximum number of bits already allocated');
  49. }
  50. this.names.set(name, this.next);
  51. this.next <<= 1;
  52. }
  53. }
  54. /**
  55. * @param {string} name The name of the bit to check for being defined
  56. * @return {boolean} True if the named bit is already allocated
  57. */
  58. public static has(name: string): boolean {
  59. return this.names.has(name);
  60. }
  61. /**
  62. * @param {string} name The name of the bit position to set
  63. */
  64. public set(name: string) {
  65. this.bits |= this.getBit(name);
  66. }
  67. /**
  68. * @param {string} name The name of the bit position to clear
  69. */
  70. public clear(name: string) {
  71. this.bits &= ~this.getBit(name);
  72. }
  73. /**
  74. * @param {string} name The name of the bit to check if set
  75. * @return {boolean} True if the named bit is set
  76. */
  77. public isSet(name: string): boolean {
  78. return !!(this.bits & this.getBit(name));
  79. }
  80. /**
  81. * Clear all bits
  82. */
  83. public reset() {
  84. this.bits = 0;
  85. }
  86. /**
  87. * @param {string} name The name whose bit position is needed (error if not defined)
  88. * @return {number} The position of the named bit
  89. */
  90. protected getBit(name: string): number {
  91. const bit = (this.constructor as typeof BitField).names.get(name);
  92. if (!bit) {
  93. throw new Error('Unknown bit-field name: ' + name);
  94. }
  95. return bit;
  96. }
  97. }
  98. /**
  99. * @param {string[]} names The name of the positions to allocate initially
  100. * @return {typeof AbstractBitField} The bit-field class with names allocated
  101. */
  102. export function BitFieldClass(...names: string[]): typeof BitField {
  103. const Bits = class extends BitField {};
  104. Bits.allocate(...names);
  105. return Bits;
  106. }