Skip to content

Commit

Permalink
Miscellaneous C++20 fixups and nits + import latest BCHN HexStr() impl
Browse files Browse the repository at this point in the history
- Added header src/bitcoin/concepts.h
- Redid some of the templates to better constrain them using concepts
- Imported latest BCHN HexStr() impl. which is slightly faster than what
  we had before.
  • Loading branch information
cculianu committed Oct 24, 2024
1 parent 6aab520 commit c5f5d59
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 51 deletions.
1 change: 1 addition & 0 deletions Fulcrum.pro
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ HEADERS += \
bitcoin/block.h \
bitcoin/cashaddr.h \
bitcoin/cashaddrenc.h \
bitcoin/concepts.h \
bitcoin/compat.h \
bitcoin/crypto/byteswap.h \
bitcoin/crypto/endian.h \
Expand Down
33 changes: 33 additions & 0 deletions src/bitcoin/concepts.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// Fulcrum - A fast & nimble SPV Server for Bitcoin Cash
// Copyright (C) 2019-2024 Calin A. Culianu <[email protected]>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program (see LICENSE.txt). If not, see
// <https://www.gnu.org/licenses/>.
//
#pragma once

#include <cstddef> // for std::byte
#include <cstdint>
#include <type_traits>

namespace bitcoin {

// Added by Calin to make some of the bitcoin code more generic
template <typename T>
concept ByteLike = std::is_same_v<T, char> || std::is_same_v<T, uint8_t>
|| std::is_same_v<T, std::byte> || std::is_same_v<T, int8_t>
|| std::is_same_v<T, signed char> || std::is_same_v<T, unsigned char>;

} // namespace bitcoin
49 changes: 22 additions & 27 deletions src/bitcoin/hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@

#pragma once

#include "Span.h"

#include "concepts.h"
#include "crypto/ripemd160.h"
#include "crypto/sha256.h"
#include "uint256.h"
#include "version.h"
#include "serialize.h"

#include <cstddef> // for std::byte
#include <type_traits>
#include <vector>

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast"
Expand All @@ -25,10 +24,6 @@ namespace bitcoin {

using ChainCode = uint256;

// Added by Calin to make some of the bitcoin code below more generic
template <typename T>
concept ByteLike = std::is_same_v<T, char> || std::is_same_v<T, uint8_t> || std::is_same_v<T, std::byte>;

/** A hasher class for Bitcoin's 256-bit hash (double SHA-256). */
class CHash256 {
private:
Expand Down Expand Up @@ -90,7 +85,8 @@ class CHash160 {
};

/** Compute the 256-bit hash of an object. */
template <typename T1> inline uint256 Hash(const T1 pbegin, const T1 pend, bool once = false) {
template <std::random_access_iterator T1>
inline uint256 Hash(const T1 pbegin, const T1 pend, bool once = false) {
const uint8_t pblank[1] = {};
uint256 result{uint256::Uninitialized};
CHash256(once)
Expand All @@ -101,17 +97,17 @@ template <typename T1> inline uint256 Hash(const T1 pbegin, const T1 pend, bool
}

/** Compute the 256-bit SINGLE hash of an object. This was added by Calin to work with ElectrumX */
template <typename T1> inline uint256 HashOnce(const T1 pbegin, const T1 pend) {
template <std::random_access_iterator T1>
inline uint256 HashOnce(const T1 pbegin, const T1 pend) {
return Hash(pbegin, pend, true);
}

inline uint256 Hash(Span<const uint8_t> sp) { return Hash(sp.begin(), sp.end()); }
inline uint256 HashOnce(Span<const uint8_t> sp) { return Hash(sp.begin(), sp.end(), true); }

/** Compute the 256-bit hash of the concatenation of two objects. */
template <typename T1, typename T2>
inline uint256 Hash(const T1 p1begin, const T1 p1end, const T2 p2begin,
const T2 p2end) {
template <std::random_access_iterator T1, std::random_access_iterator T2>
inline uint256 Hash(const T1 p1begin, const T1 p1end, const T2 p2begin, const T2 p2end) {
const uint8_t pblank[1] = {};
uint256 result{uint256::Uninitialized};
CHash256()
Expand All @@ -124,9 +120,8 @@ inline uint256 Hash(const T1 p1begin, const T1 p1end, const T2 p2begin,
}

/** Compute the 256-bit hash of the concatenation of three objects. */
template <typename T1, typename T2, typename T3>
inline uint256 Hash(const T1 p1begin, const T1 p1end, const T2 p2begin,
const T2 p2end, const T3 p3begin, const T3 p3end) {
template <std::random_access_iterator T1, std::random_access_iterator T2, std::random_access_iterator T3>
inline uint256 Hash(const T1 p1begin, const T1 p1end, const T2 p2begin, const T2 p2end, const T3 p3begin, const T3 p3end) {
const uint8_t pblank[1] = {};
uint256 result{uint256::Uninitialized};
CHash256()
Expand All @@ -141,7 +136,8 @@ inline uint256 Hash(const T1 p1begin, const T1 p1end, const T2 p2begin,
}

/** Compute the 160-bit hash an object. */
template <typename T1> inline uint160 Hash160(const T1 pbegin, const T1 pend) {
template <std::random_access_iterator T1>
inline uint160 Hash160(const T1 pbegin, const T1 pend) {
const uint8_t pblank[1] = {};
uint160 result{uint160::Uninitialized};
CHash160()
Expand Down Expand Up @@ -187,7 +183,8 @@ class CHashWriter {
template <ByteLike ByteT>
void GetHashInPlace(ByteT buf[CHash256::OUTPUT_SIZE]) { ctx.Finalize(buf); }

template <typename T> CHashWriter &operator<<(const T &obj) {
template <typename T>
CHashWriter &operator<<(const T &obj) {
// Serialize to this stream
bitcoin::Serialize(*this, obj);
return *this;
Expand All @@ -198,14 +195,14 @@ class CHashWriter {
* Reads data from an underlying stream, while hashing the read data.
*/

template <typename Source> class CHashVerifier : public CHashWriter {
template <typename Source>
class CHashVerifier : public CHashWriter {
private:
Source *source;

public:
explicit CHashVerifier(Source *source_)
: CHashWriter(source_->GetType(), source_->GetVersion()),
source(source_) {}
: CHashWriter(source_->GetType(), source_->GetVersion()), source(source_) {}

template <ByteLike ByteT>
void read(ByteT *pch, size_t nSize) {
Expand All @@ -232,8 +229,7 @@ template <typename Source> class CHashVerifier : public CHashWriter {
/** Compute the 256-bit hash of an object's serialization. */

template <typename T>
uint256 SerializeHash(const T &obj, int nType = SER_GETHASH,
int nVersion = PROTOCOL_VERSION, bool once = false) {
uint256 SerializeHash(const T &obj, int nType = SER_GETHASH, int nVersion = PROTOCOL_VERSION, bool once = false) {
CHashWriter ss(nType, nVersion, once);
ss << obj;
return ss.GetHash();
Expand All @@ -249,10 +245,9 @@ void SerializeHashInPlace(ByteT hash[CHash256::OUTPUT_SIZE], const T &obj,
}

// MurmurHash3: ultra-fast hash suitable for hash tables but not cryptographically secure
uint32_t MurmurHash3(uint32_t nHashSeed,
const uint8_t *pDataToHash, size_t nDataLen /* bytes */);
inline uint32_t MurmurHash3(uint32_t nHashSeed,
const std::vector<uint8_t> &vDataToHash) {
uint32_t MurmurHash3(uint32_t nHashSeed, const uint8_t *pDataToHash, size_t nDataLen /* bytes */);

inline uint32_t MurmurHash3(uint32_t nHashSeed, Span<const uint8_t> vDataToHash) {
return MurmurHash3(nHashSeed, vDataToHash.data(), vDataToHash.size());
}

Expand Down
26 changes: 15 additions & 11 deletions src/bitcoin/prevector.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,16 @@ class prevector {
return iterator(ptr--);
}
difference_type friend operator-(iterator a, iterator b) {
return &*a - &*b;
return a.ptr - b.ptr;
}
iterator operator+(size_type n) { return iterator(ptr + n); }
iterator &operator+=(size_type n) {
iterator operator+(difference_type n) const { return iterator(ptr + n); }
friend iterator operator+(difference_type n, iterator b) { return iterator(b.ptr + n); }
iterator &operator+=(difference_type n) {
ptr += n;
return *this;
}
iterator operator-(size_type n) { return iterator(ptr - n); }
iterator &operator-=(size_type n) {
iterator operator-(difference_type n) const { return iterator(ptr - n); }
iterator &operator-=(difference_type n) {
ptr -= n;
return *this;
}
Expand Down Expand Up @@ -144,7 +145,7 @@ class prevector {
const_iterator(iterator x) : ptr(&(*x)) {}
const T &operator*() const { return *ptr; }
const T *operator->() const { return ptr; }
const T &operator[](size_type pos) const { return ptr[pos]; }
const T &operator[](difference_type pos) const { return ptr[pos]; }
const_iterator &operator++() {
++ptr;
return *this;
Expand All @@ -160,19 +161,22 @@ class prevector {
return const_iterator(ptr--);
}
difference_type friend operator-(const_iterator a, const_iterator b) {
return &*a - &*b;
return a.ptr - b.ptr;
}
const_iterator operator+(size_type n) {
const_iterator operator+(difference_type n) const {
return const_iterator(ptr + n);
}
const_iterator &operator+=(size_type n) {
friend const_iterator operator+(difference_type n, const_iterator b) {
return const_iterator(b.ptr + n);
}
const_iterator &operator+=(difference_type n) {
ptr += n;
return *this;
}
const_iterator operator-(size_type n) {
const_iterator operator-(difference_type n) const {
return const_iterator(ptr - n);
}
const_iterator &operator-=(size_type n) {
const_iterator &operator-=(difference_type n) {
ptr -= n;
return *this;
}
Expand Down
13 changes: 12 additions & 1 deletion src/bitcoin/utilstrencodings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@

namespace bitcoin {

namespace strencodings {
// used by the HexStr template function as a lookup table to convert bytes -> hex
const char hexmap[513] =
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f"
"303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f"
"606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f"
"909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
"c0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeef"
"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
} // namespace strencodings

static const std::string CHARS_ALPHA_NUM =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

Expand All @@ -45,7 +56,7 @@ std::string SanitizeString(const std::string &str, int rule) {
return strResult;
}

const signed char p_util_hexdigit[256] = {
static const signed char p_util_hexdigit[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
Expand Down
71 changes: 59 additions & 12 deletions src/bitcoin/utilstrencodings.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@
*/
#pragma once

#include "concepts.h"
#include "Span.h"

#include <cassert>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <string>
#include <vector>

Expand Down Expand Up @@ -116,25 +122,66 @@ bool ParseUInt64(const std::string &str, uint64_t *out);
*/
bool ParseDouble(const std::string &str, double *out);

template <typename T>
namespace strencodings {
// We use a hex lookup table with a series of hex pairs all in 1 string in order to ensure locality of reference.
// This is indexed as hexmap[ubyte_val * 2].
extern const char hexmap[513];
} // namespace strencodings

template <std::random_access_iterator T>
requires (ByteLike<std::iter_value_t<T>>)
std::string HexStr(const T itbegin, const T itend, bool fSpaces = false) {
std::string rv;
static const char hexmap[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
rv.reserve((itend - itbegin) * 3);
for (T it = itbegin; it < itend; ++it) {
uint8_t val = uint8_t(*it);
if (fSpaces && it != itbegin) rv.push_back(' ');
rv.push_back(hexmap[val >> 4]);
rv.push_back(hexmap[val & 0xfu]);
using strencodings::hexmap;
using diff_t = std::iter_difference_t<T>;
const diff_t iSpaces = diff_t(fSpaces);
diff_t size = (itend - itbegin) * (2 + iSpaces);
if (size <= 0) // short-circuit return and/or guard against invalid usage
return rv;
size -= iSpaces; // fSpaces only: deduct 1 space for first item
rv.resize(static_cast<size_t>(size)); // pre-allocate the entire array to avoid using the slower push_back
size_t pos = 0;
if (!fSpaces) {
// this branch is the most likely branch in this codebase
for (T it = itbegin; it < itend; ++it) {
const char *hex = &hexmap[static_cast<uint8_t>(*it) * 2u];
rv[pos++] = *hex++;
rv[pos++] = *hex;
}
} else {
// we unroll the first iteration (which prints no space) to avoid any branching while looping
T it = itbegin;
if (it < itend) {
// first iteration, no space
const char *hex = &hexmap[static_cast<uint8_t>(*it) * 2u];
rv[pos++] = *hex++;
rv[pos++] = *hex;
++it;
for (; it < itend; ++it) {
// subsequent iterations (if any), unconditionally prepend space
rv[pos++] = ' ';
hex = &hexmap[static_cast<uint8_t>(*it) * 2u];
rv[pos++] = *hex++;
rv[pos++] = *hex;
}
}
}
assert(pos == rv.size());

return rv;
}

template <typename T>
inline std::string HexStr(const T &vch, bool fSpaces = false) {
return HexStr(vch.begin(), vch.end(), fSpaces);
/**
* Convert a span of bytes to a lower-case hexadecimal string.
*/
inline std::string HexStr(const Span<const uint8_t> input, bool fSpaces = false) {
return HexStr(input.begin(), input.end(), fSpaces);
}
inline std::string HexStr(const Span<const char> input, bool fSpaces = false) {
return HexStr(MakeUInt8Span(input), fSpaces);
}
inline std::string HexStr(const Span<const std::byte> input, bool fSpaces = false) {
return HexStr(MakeUInt8Span(input), fSpaces);
}

/**
Expand Down

0 comments on commit c5f5d59

Please sign in to comment.