PHP WebShell

Текущая директория: /usr/lib/node_modules/bitgo/node_modules/@vechain/sdk-core/src/vcdm

Просмотр файла: Hex.ts

import * as nc_utils from '@noble/curves/abstract/utils';
import * as nh_utils from '@noble/hashes/utils';
import { InvalidDataType, InvalidOperation } from '@vechain/sdk-errors';
import { type VeChainDataModel } from './VeChainDataModel';

/**
 * Represents a hexadecimal value expressed as
 * * `-` sign if the value is negative,
 * * `0x` hexadecimal notation tag,
 * * a not empty string of hexadecimal digits from `0` to `9` and from `a` to `f`.
 *
 * @description This hexadecimal notation is coherent with the decimal notation:
 * * the sign is only expressed for negative values, and it is always the first symbol,
 * * the `0x` tags the string as a hexadecimal expression,
 * * hexadecimal digits follow.
 * * An empty content results is no digits.
 *
 * @implements {VeChainDataModel<Hex>}
 */
class Hex implements VeChainDataModel<Hex> {
    /**
     * Negative multiplier of the {@link digits} absolute value.
     *
     * @type {number}
     */
    protected static readonly NEGATIVE: number = -1;

    /**
     * Positive multiplier of the {@link digits} absolute value.
     *
     * @type {number}
     */
    protected static readonly POSITIVE: number = 1;

    /**
     * A constant string prefix used in hexadecimal notation.
     */
    public static readonly PREFIX = '0x';

    /**
     * The radix used for representing numbers base 16 in a positional numeral notation system.
     *
     * @typedef {number} RADIX
     */
    public static readonly RADIX: number = 16;

    /**
     * Regular expression for matching hexadecimal strings.
     * An empty input is represented as a empty digits.
     *
     * @type {RegExp}
     */
    private static readonly REGEX_HEX: RegExp = /^-?(0x)?[0-9a-f]*$/i;

    /**
     * Regular expression pattern to match a prefix indicating hexadecimal number.
     *
     * @type {RegExp}
     */
    protected static readonly REGEX_HEX_PREFIX: RegExp = /^-?0x/i;

    /**
     * Returns the hexadecimal digits expressing this absolute value, sign and `0x` prefix omitted.

     * @remarks An empty content results in an empty string returned.
     */
    public readonly digits: string;

    /**
     * Represents the sign multiplier of a given number:
     * * {@link NEGATIVE} `-1` if negative,
     * * {@link POSITIVE} `1` if positive.
     */
    public readonly sign: number;

    /**
     * Creates a new instance of this class to represent the value
     * built multiplying `sign` for the absolute value expressed by the hexadecimal `digits`.
     *
     * @param {number} sign - The sign of the value.
     * @param {string} digits - The digits of the absolute value in hexadecimal base.
     * @param {function} [normalize] - The function used to normalize the digits. Defaults to converting digits to lowercase.
     */
    protected constructor(
        sign: number,
        digits: string,
        normalize: (digits: string) => string = (digits) => digits.toLowerCase()
    ) {
        this.digits = normalize(digits);
        this.sign = sign;
    }

    /**
     * Returns the absolute value of this Hex object.
     *
     * @return {Hex} A new Hex object representing the absolute value of this Hex.
     */
    public get abs(): Hex {
        return new Hex(Hex.POSITIVE, this.digits);
    }

    /**
     * Returns the value of `bi` as a `BigInt` type.
     *
     * @returns {bigint} The value of `bi` as a `BigInt`.
     */
    get bi(): bigint {
        return BigInt(this.sign) * nc_utils.hexToNumber(this.digits);
    }

    /**
     * Returns the Uint8Array representation of the aligned bytes.
     *
     * @return {Uint8Array} The Uint8Array representation of the aligned bytes.
     */
    get bytes(): Uint8Array {
        return nc_utils.hexToBytes(this.alignToBytes().digits);
    }

    /**
     * Returns the value of n.
     *
     * @return {number} The value of n.
     *
     * @throws {InvalidOperation<Hex>} Throws an error if this instance doesn't represent
     * an [IEEE 754 double precision 64 bits floating point format](https://en.wikipedia.org/wiki/Double-precision_floating-point_format).
     */
    get n(): number {
        if (this.isNumber()) {
            // The sign is part of the IEEE 754 representation hence no need to consider `this.sign` property.
            return new DataView(this.bytes.buffer).getFloat64(0);
        }
        throw new InvalidOperation('Hex.n', 'not an IEEE 754 float 64 number', {
            hex: this.toString()
        });
    }

    /**
     * Aligns the hexadecimal string to bytes by adding a leading '0' if the string length is odd.
     *
     * @returns {Hex} - The aligned hexadecimal string.
     */
    public alignToBytes(): Hex {
        return this.digits.length % 2 === 0
            ? this
            : new Hex(this.sign, '0' + this.digits);
    }

    /**
     * Compares the current Hex object with another Hex object.
     *
     * @param {Hex} that - The Hex object to compare with.
     *
     * @return {number} - Returns a negative number if the current Hex object is less than the given Hex object,
     *                    zero if they are equal, or a positive number if the current Hex object is greater than the given Hex object.
     */
    compareTo(that: Hex): number {
        if (this.sign === that.sign) {
            const digits = Math.max(this.digits.length, that.digits.length);
            const thisBytes = this.fit(digits).bytes;
            const thatBytes = that.fit(digits).bytes;
            let i = 0;
            let compareByte = 0;
            while (compareByte === 0 && i < thisBytes.length) {
                compareByte = thisBytes[i] - thatBytes[i];
                i++;
            }
            return compareByte;
        }
        return this.sign - that.sign;
    }

    /**
     * Returns a new instance of the Hex class, its value fits to the specified number of digits.
     *
     * @param {number} digits - The number of digits to fit the Hex value into.
     *
     * @returns {Hex} - A new Hex instance that represents the fitted Hex value.
     *
     * @throws {InvalidDataType} - If the Hex value cannot be fit into the specified number of digits.
     */
    public fit(digits: number): Hex {
        if (digits < this.digits.length) {
            // Cut.
            let cue = 0;
            while (
                this.digits.length - cue > digits &&
                this.digits.at(cue) === '0'
            ) {
                cue++;
            }
            if (this.digits.length - cue === digits) {
                return new Hex(this.sign, this.digits.slice(cue));
            }
            throw new InvalidDataType(
                'Hex.fit',
                `can't fit in ${digits} digits`,
                { digits, hex: this }
            );
        }
        if (digits > this.digits.length) {
            // Pad.
            return new Hex(
                this.sign,
                '0'.repeat(digits - this.digits.length) + this.digits
            );
        }
        return this;
    }

    /**
     * Determines whether this Hex instance is equal to the given Hex instance.
     *
     * @param {Hex} that - The Hex instance to compare with.
     * @return {boolean} - True if the Hex instances are equal, otherwise false.
     */
    isEqual(that: Hex): boolean {
        return this.compareTo(that) === 0;
    }

    /**
     * Checks if this instance expresses a valid {@link Number} value
     * according the
     * [IEEE 754 double precision 64 bits floating point format](https://en.wikipedia.org/wiki/Double-precision_floating-point_format).
     *
     * @returns {boolean} Returns true if this instance expresses 32 hex digits (16 bytes, 128 bits) needed to represent
     * a {@link Number} value, else it returns false.
     */
    isNumber(): boolean {
        return this.digits.length === 32;
    }

    /**
     * Checks if the given string expression is a valid hexadecimal value.
     *
     * @param {string} exp - The string representation of a hexadecimal value.
     *
     * @return {boolean} - True if the expression is a valid hexadecimal value, case-insensitive,
     * optionally prefixed with `0x`; false otherwise.
     */
    public static isValid(exp: string): boolean {
        return Hex.REGEX_HEX.test(exp);
    }

    /**
     * Determines whether the given string is a valid hexadecimal number prefixed with '0x'.
     *
     * @param {string} exp - The string to be evaluated.
     * @return {boolean} - True if the string is a valid hexadecimal number prefixed with '0x', otherwise false.
     */
    public static isValid0x(exp: string): boolean {
        return Hex.REGEX_HEX_PREFIX.test(exp) && Hex.isValid(exp);
    }

    /**
     * Create a Hex instance from a bigint, number, string, or Uint8Array.
     *
     * @param {bigint | number | string | Uint8Array} exp - The value to represent in a Hex instance:
     * * bigint is always representable in hexadecimal base notation;
     * * number, encoded as [IEEE 754 double precision 64 bits floating point format](https://en.wikipedia.org/wiki/Double-precision_floating-point_format);
     * * string is parsed as the hexadecimal expression of a bigint value, optionally tagged with `0x`;
     * * Uint8Array is interpreted as the sequence of bytes.
     *
     * @returns {Hex} - A Hex instance representing the input value.
     *
     * @throws {InvalidDataType} if the given `exp` can't be represented as a hexadecimal expression.
     */
    public static of(exp: bigint | number | string | Uint8Array): Hex {
        try {
            if (typeof exp === 'bigint') {
                if (exp < 0n) {
                    return new Hex(
                        this.NEGATIVE,
                        nc_utils.numberToHexUnpadded(-1n * exp)
                    );
                }
                return new Hex(
                    this.POSITIVE,
                    nc_utils.numberToHexUnpadded(exp)
                );
            } else if (typeof exp === 'number') {
                const dataView = new DataView(new ArrayBuffer(16));
                dataView.setFloat64(0, exp);
                return new Hex(
                    exp < 0 ? this.NEGATIVE : this.POSITIVE,
                    nc_utils.bytesToHex(new Uint8Array(dataView.buffer))
                );
            } else if (typeof exp === 'string') {
                if (!this.isValid(exp)) {
                    throw new InvalidDataType(
                        'Hex.of',
                        'not an hexadecimal string',
                        { exp }
                    );
                }
                if (exp.startsWith('-')) {
                    return new Hex(
                        this.NEGATIVE,
                        this.REGEX_HEX_PREFIX.test(exp)
                            ? exp.slice(3)
                            : exp.slice(1)
                    );
                }
                return new Hex(
                    this.POSITIVE,
                    this.REGEX_HEX_PREFIX.test(exp) ? exp.slice(2) : exp
                );
            }
            return new Hex(this.POSITIVE, nc_utils.bytesToHex(exp));
        } catch (e) {
            throw new InvalidDataType(
                'Hex.of',
                'not an hexadecimal expression',
                { exp: `${exp}` }, // Needed to serialize bigint values.
                e
            );
        }
    }

    /**
     * Generates a random Hex value of the given number of bytes length.
     *
     * @param {number} bytes - The number of bytes to generate.
     * @throws {InvalidDataType} - If the bytes argument is not greater than 0.
     * @returns {Hex} - A randomly generated Hex value.
     *
     * @remarks Security auditable method, depends on
     * * [`nh_utils.randomBytes`](https://github.com/paulmillr/noble-hashes?tab=readme-ov-file#utils).
     */
    public static random(bytes: number): Hex {
        if (bytes > 0) {
            return Hex.of(nh_utils.randomBytes(bytes));
        }
        throw new InvalidDataType('Hex.random', 'bytes argument not > 0', {
            bytes
        });
    }

    /**
     * Returns a string representation of the object.
     *
     * @param {boolean} compact - Whether to compact the string representation.
     * @return {string} The string representation of the object.
     */
    public toString(compact: boolean = false): string {
        if (compact) {
            const stripped = this.digits.replace(/^0+/, '');
            const compactDigits = stripped === '' ? '0' : stripped;
            return (this.sign < 0 ? '-0x' : '0x') + compactDigits;
        }

        return (this.sign < 0 ? '-0x' : '0x') + this.digits;
    }
}

export { Hex };

Выполнить команду


Для локальной разработки. Не используйте в интернете!