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

Code before:
    {
        s << std::span(m_data);
    }

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

template <unsigned int BITS>
consteval base_blob<BITS>::base_blob(std::string_view hex_str)
{
    if (hex_str.length() != m_data.size() * 2) throw "Hex string must fit exactly";
    auto str_it = hex_str.rbegin();
    for (auto& elem : m_data) {
        auto lo = util::ConstevalHexDigit(*(str_it++));
        elem = (util::ConstevalHexDigit(*(str_it++)) << 4) | lo;
    }
}

namespace detail {
/**
 * Writes the hex string (in reverse byte order) 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
 */
template <class uintN_t>
std::optional<uintN_t> FromHex(std::string_view str)
{
    if (uintN_t::size() * 2 != str.size() || !IsHex(str)) return std::nullopt;
    uintN_t rv;
    rv.SetHexDeprecated(str);
    return rv;
}
/**
 * @brief Like FromHex(std::string_view str), but allows an "0x" prefix
 *        and pads the input with leading zeroes if it is shorter than
 *        the expected length of uintN_t::size()*2.
 *
 *        Designed to be used when dealing with user input.
 */
template <class uintN_t>
std::optional<uintN_t> FromUserHex(std::string_view input)
{
    input = util::RemovePrefixView(input, "0x");
    constexpr auto expected_size{uintN_t::size() * 2};
    if (input.size() < expected_size) {
        auto padded = std::string(expected_size, '0');

Review comment (refactor):
Because the `uintN_t::size() * 2 == str.size()` check already makes sure there can be no underrun here, one could shorten this to
```c++
*p1 = (unsigned char)::HexDigit(str[digits - 1]) | ((unsigned char)::HexDigit(str[digits - 2]) << 4);
digits -= 2;
```
That said, for a refactor it seems better to move the code as obviously as possible as done here.

Code after:
    if (hex_str.length() != m_data.size() * 2) throw "Hex string must fit exactly";
    auto str_it = hex_str.rbegin();
    for (auto& elem : m_data) {
        auto lo = util::ConstevalHexDigit(*(str_it++));
        elem = (util::ConstevalHexDigit(*(str_it++)) << 4) | lo;
    }
}

namespace detail {
/**
 * Writes the hex string (in reverse byte order) 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
 */
template <class uintN_t>
std::optional<uintN_t> FromHex(std::string_view str)
{
    if (uintN_t::size() * 2 != str.size() || !IsHex(str)) return std::nullopt;
    uintN_t rv;
    unsigned char* p1 = rv.begin();
    unsigned char* pend = rv.end();
    size_t digits = str.size();
    while (digits > 0 && p1 < pend) {
        *p1 = ::HexDigit(str[--digits]);
        if (digits > 0) {
            *p1 |= ((unsigned char)::HexDigit(str[--digits]) << 4);
            p1++;
        }
    }
    return rv;
}
/**
 * @brief Like FromHex(std::string_view str), but allows an "0x" prefix
 *        and pads the input with leading zeroes if it is shorter than
 *        the expected length of uintN_t::size()*2.
 *
 *        Designed to be used when dealing with user input.
 */
template <class uintN_t>
std::optional<uintN_t> FromUserHex(std::string_view input)
{
    input = util::RemovePrefixView(input, "0x");
    constexpr auto expected_size{uintN_t::size() * 2};
    if (input.size() < expected_size) {
        auto padded = std::string(expected_size, '0');
        std::copy(input.begin(), input.end(), padded.begin() + expected_size - input.size());
        return FromHex<uintN_t>(padded);
    }
    return FromHex<uintN_t>(input);
}