Repository: bitcoin/bitcoin
File: src/uint256.h (C/C++)

Code before:

    constexpr void SetNull()
    {
        std::fill(m_data.begin(), m_data.end(), 0);
    }

    constexpr int Compare(const base_blob& other) const { return std::memcmp(m_data.data(), other.m_data.data(), WIDTH); }

    friend constexpr bool operator==(const base_blob& a, const base_blob& b) { return a.Compare(b) == 0; }
    friend constexpr bool operator!=(const base_blob& a, const base_blob& b) { return a.Compare(b) != 0; }
    friend constexpr bool operator<(const base_blob& a, const base_blob& b) { return a.Compare(b) < 0; }

    // Hex string representations are little-endian.
    std::string GetHex() const;
    /** Unlike FromHex this accepts any invalid input, thus it is fragile and deprecated */
    void SetHexDeprecated(std::string_view str);
    std::string ToString() const;

    constexpr const unsigned char* data() const { return m_data.data(); }
    constexpr unsigned char* data() { return m_data.data(); }

    constexpr unsigned char* begin() { return m_data.data(); }
    constexpr unsigned char* end() { return m_data.data() + WIDTH; }

    constexpr const unsigned char* begin() const { return m_data.data(); }
    constexpr const unsigned char* end() const { return m_data.data() + WIDTH; }

    static constexpr unsigned int size() { return WIDTH; }

    constexpr uint64_t GetUint64(int pos) const { return ReadLE64(m_data.data() + pos * 8); }

    template<typename Stream>
    void Serialize(Stream& s) const
    {
        s << Span(m_data);
    }

    template<typename Stream>
    void Unserialize(Stream& s)
    {
        s.read(MakeWritableByteSpan(m_data));
    }
};

namespace detail {
/**
 * Writes the hex string (treated as little-endian) into a new uintN_t object
 * and only returns a value iff all of the checks pass:
 *   - Input length is uintN_t::size()*2
 *   - All characters are hex
 */

Review comment (suggestion):
In commit "doc + test: Correct uint256 hex string endianness" (07de45a882d43fd5567b2a6b7c8a2da3d6dbda3d)

I think this description is somewhat helpful but missing important information, like the fact that it is reversing the hex string.

Would suggest:

- This function strips optional whitespace and "0x" prefixes and then copies pairs of hex digits from the end of the hex string, moving backwards, into the beginning bytes of the uint, moving forwards. If too many hex digits are provided, the digits at the beginning of the hex string are ignored. If too few hex digits are provided, the bytes at the end of the uint will be filled with 0's. If there are too few hex digits, and the number of hex digits is odd, the high order bits of the last byte copied will be 0. For example if the hex string `0x123` is provided, the resulting blob will contain bytes `{0x23, 0x1, 0x0, ..., 0x0}`

Code after:

    constexpr void SetNull()
    {
        std::fill(m_data.begin(), m_data.end(), 0);
    }

    /** Lexicographic ordering
     * @note Does NOT match the ordering on the corresponding \ref
     *       base_uint::CompareTo, which starts comparing from the end.
     */
    constexpr int Compare(const base_blob& other) const { return std::memcmp(m_data.data(), other.m_data.data(), WIDTH); }

    friend constexpr bool operator==(const base_blob& a, const base_blob& b) { return a.Compare(b) == 0; }
    friend constexpr bool operator!=(const base_blob& a, const base_blob& b) { return a.Compare(b) != 0; }
    friend constexpr bool operator<(const base_blob& a, const base_blob& b) { return a.Compare(b) < 0; }

    /** @name Hex representation
     *
     * The reverse-byte hex representation is a convenient way to view the blob
     * as a number, because it is consistent with the way the base_uint class
     * converts blobs to numbers.
     *
     * @note base_uint treats the blob as an array of bytes with the numerically
     * least significant byte first and the most significant byte last. Because
     * numbers are typically written with the most significant digit first and
     * the least significant digit last, the reverse hex display of the blob
     * corresponds to the same numeric value that base_uint interprets from the
     * blob.
     * @{*/
    std::string GetHex() const;
    /** Unlike FromHex this accepts any invalid input, thus it is fragile and deprecated!
     *
     * - Hex numbers that don't specify enough bytes to fill the internal array
     *   will be treated as setting the beginning of it, which corresponds to
     *   the least significant bytes when converted to base_uint.
     *
     * - Hex numbers specifying too many bytes will have the numerically most
     *   significant bytes (the beginning of the string) narrowed away.
     *
     * - An odd count of hex digits will result in the high bits of the leftmost
     *   byte being zero.
     *   "0x123" => {0x23, 0x1, 0x0, ..., 0x0}
     */
    void SetHexDeprecated(std::string_view str);
    std::string ToString() const;
    /**@}*/

    constexpr const unsigned char* data() const { return m_data.data(); }
    constexpr unsigned char* data() { return m_data.data(); }

    constexpr unsigned char* begin() { return m_data.data(); }