BaseValue.js

  1. const bitmask = bitsNumber => -1 >>> (32 - bitsNumber);
  2. const check = (byteSize, value, isSigned) => {
  3. let lowerBound;
  4. let upperBound;
  5. if (8 === byteSize) {
  6. if ('bigint' !== typeof value)
  7. throw new TypeError(`"value" (${value}) has invalid type, expected BigInt`);
  8. lowerBound = isSigned ? -0x80000000_00000000n : 0n;
  9. upperBound = isSigned ? 0x7FFFFFFF_FFFFFFFFn : 0xFFFFFFFF_FFFFFFFFn;
  10. } else {
  11. if (!Number.isInteger(value))
  12. throw new RangeError(`"value" (${value}) is not an integer`);
  13. // note: all expressions are tricky, they're used due to JS dumbness re 1<<32 == 0 and 1<<31 = min signed int
  14. lowerBound = isSigned ? -bitmask((8 * byteSize) - 1) - 1 : 0;
  15. upperBound = isSigned ? bitmask((8 * byteSize) - 1) : bitmask(8 * byteSize);
  16. }
  17. if (value < lowerBound || value > upperBound)
  18. throw RangeError(`"value" (${value}) is outside of valid ${8 * byteSize}-bit range`);
  19. return value;
  20. };
  21. const isNonNegative = value => {
  22. if ('bigint' === typeof value)
  23. return 0n <= value;
  24. return 0 <= value;
  25. };
  26. /**
  27. * Represents a base integer.
  28. */
  29. export default class BaseValue {
  30. /**
  31. * Creates a base value.
  32. * @param {number} size Size of the integer.
  33. * @param {number|bigint} value Value.
  34. * @param {boolean} isSigned \c true if the value should be treated as signed.
  35. */
  36. constructor(size, value, isSigned = false) {
  37. /**
  38. * Size of the integer.
  39. * @type number
  40. */
  41. this.size = size;
  42. /**
  43. * \c true if the value should be treated as signed.
  44. * @type boolean
  45. */
  46. this.isSigned = isSigned;
  47. /**
  48. * Value.
  49. * @type number|bigint
  50. */
  51. this.value = check(size, value, isSigned);
  52. }
  53. /**
  54. * Converts base value to string.
  55. * @returns {string} String representation.
  56. */
  57. toString() {
  58. let unsignedValue;
  59. if (!this.isSigned || isNonNegative(this.value)) {
  60. unsignedValue = this.value;
  61. } else {
  62. const upperBoundPlusOne = (8 === this.size ? 0x1_00000000_00000000n : BigInt(bitmask(this.size * 8) + 1));
  63. unsignedValue = BigInt(this.value) + upperBoundPlusOne;
  64. }
  65. return `0x${unsignedValue.toString(16).toUpperCase().padStart(this.size * 2, '0')}`;
  66. }
  67. }