@ganache/utils
Version:
Utility functions for @ganache packages
179 lines • 6.45 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.uintToBuffer = void 0;
const MAX_UINT32 = 0xffffffff;
/**
* This is just Node's `Buffer.allocUnsafe`. I'm documenting it extra here to
* draw attention to it. It is much faster the `Buffer.alloc(size)` because it
* doesn't initialize its memory first. It's safe for us to use below because we
* guarantee that we will fill every octet ourselves.
*
* Allocates a new buffer of {size} octets, leaving memory not initialized, so
* the contents of the newly created Buffer are unknown and may contain
* sensitive data.
*
* @param {number} - size count of octets to allocate
*/
const allocUnsafe = Buffer.allocUnsafe;
/**
* Converts positive whole numbers that are 32 bits of fewer to a Buffer. Any
* more bits and who knows what will happen!?!1?!
*
* @param num - A positive whole number less than 33 bits wide, i.e. a uint32.
* @returns an optimally sized buffer holding `num` in big-endian order (LSB is
* the _last_ value in the Buffer)
*/
function uint32ToBuf(num) {
let buf;
/** `lsb` holds the Least Significant *byte* of `num`. It *technically* holds
* all of `num`'s bytes but because of how UInt8Arrays (and thus Buffers)
* work, only the least significant byte of each value gets used. */
const lsb = num;
// shift the first 8 least significant bits off current num, if it's non-zero
// our value contains at least 2 bytes!
if ((num >>>= 8)) {
/** `second` now holds the second most least significant byte in its
* "first" (right most) 8 bits */
const second = num;
// shift the next 8 least significant bits off current num, if it's non-zero
// our value contains at least 3 bytes!
if ((num >>>= 8)) {
/** `third` now holds the third most least significant byte in its
* "first" (right most) 8 bits */
const third = num;
if ((num >>>= 8)) {
// since we have all 4 bytes, create a 4 byte Buffer and fill it with
// our values!
buf = allocUnsafe(4);
// `num` here is just what is left after shifting off the 3 other bytes
// like we did above
buf[0] = num;
buf[1] = third;
buf[2] = second;
buf[3] = lsb;
}
else {
// since we only have 3 bytes, create a 3 byte Buffer and fill it with
// our values!
buf = allocUnsafe(3);
buf[0] = third;
buf[1] = second;
buf[2] = lsb;
}
}
else {
// since we only have 2 bytes, create a 2 byte Buffer and fill it with
// our values!
buf = allocUnsafe(2);
buf[0] = second;
buf[1] = lsb;
}
}
else {
// We only have 1 byte, create a 1 byte Buffer and fill it with our only
// value, lsb!
buf = allocUnsafe(1);
buf[0] = lsb;
}
// finally, return our optimally-sized Buffer!
return buf;
}
/**
* Converts positive whole numbers less than or equal to
* `Number.MAX_SAFE_INTEGER` to a Buffer. If your value is less than 2**32 you
* should use `uint32ToBuf` instead.
*
* @param num - A positive whole number <= `Number.MAX_SAFE_INTEGER`
* @returns an optimally sized buffer holding `num` in big-endian order (LSB is
* the _last_ value in the Buffer)
*/
function uintWideToBuf(num) {
// This function is similar to `uint32ToBuf`, but splits the number into its
// 32 lowest bits and its 32 highest bits. We have to do this because numeric
// Bitwise operations can only operate on 32 bit-wide values.
// There are some differences, but if you first grasp `uint32ToBuf`, you can
// handle this just fine.
let buf;
/** If we are in this function we are probably > 32 bits wide, so we need to
* first convert this value to BigInt in order to shift off those high bits.
* Now that I'm documenting this, we could probably just subtract `2**32` from
* `num` to avoid the conversion overhead (BigInts are slower than numbers) */
let hi = Number(BigInt(num) >> 32n);
const hiLsb = hi;
let offset = 0;
// the high bits determine the size of the Buffer, so we compute the high bits
// first
if ((hi >>>= 8)) {
const six = hi;
if ((hi >>>= 8)) {
const five = hi;
if ((hi >>>= 8)) {
buf = allocUnsafe(8);
buf[0] = hi; // msb
buf[1] = five;
buf[2] = six;
buf[3] = hiLsb;
offset = 7;
}
else {
buf = allocUnsafe(7);
buf[0] = five; // msb
buf[1] = six;
buf[2] = hiLsb;
offset = 6;
}
}
else {
buf = allocUnsafe(6);
buf[0] = six; // msb
buf[1] = hiLsb;
offset = 5;
}
}
else {
buf = allocUnsafe(5);
buf[0] = hiLsb; // msb
offset = 4;
}
// set the low bytes:
let lo = num & MAX_UINT32;
const lsb = lo;
if ((lo >>>= 8)) {
const two = lo;
if ((lo >>>= 8)) {
const one = lo;
buf[offset - 3] = lo >>>= 8;
buf[offset - 2] = one;
buf[offset - 1] = two;
buf[offset] = lsb;
}
else {
buf[offset - 3] = 0;
buf[offset - 2] = 0;
buf[offset - 1] = two;
buf[offset] = lsb;
}
}
else {
buf[offset - 3] = 0;
buf[offset - 2] = 0;
buf[offset - 1] = 0;
buf[offset] = lsb;
}
return buf;
}
/**
* Converts a JavaScript number, treated as a Whole Number (0, 1, 2, 3, 4, ...)
* less than 64 bits wide, to a Buffer.
*
* Numbers that are negative, fractional, or greater than 64 bits wide will
* return very unexpected results. Numbers that are greater than
* `Number.MAX_SAFE_INTEGER` will return unexpected results.
*
* @param num - A positive whole number <= `Number.MAX_SAFE_INTEGER`
*/
function uintToBuffer(num) {
return num > MAX_UINT32 ? uintWideToBuf(num) : uint32ToBuf(num);
}
exports.uintToBuffer = uintToBuffer;
//# sourceMappingURL=uint-to-buffer.js.map