BIP155 (addrv2)
Tor v3 + i2p
This commit is contained in:
47
src/util/readwritefile.cpp
Normal file
47
src/util/readwritefile.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2015-2020 The Bitcoin Core developers
|
||||
// Copyright (c) 2017 The Zcash developers
|
||||
// Copyright (c) 2016-2022 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
#include <fs.h>
|
||||
#include <limits>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits<size_t>::max())
|
||||
{
|
||||
FILE *f = fsbridge::fopen(filename, "rb");
|
||||
if (f == nullptr)
|
||||
return std::make_pair(false,"");
|
||||
std::string retval;
|
||||
char buffer[128];
|
||||
do {
|
||||
const size_t n = fread(buffer, 1, sizeof(buffer), f);
|
||||
// Check for reading errors so we don't return any data if we couldn't
|
||||
// read the entire file (or up to maxsize)
|
||||
if (ferror(f)) {
|
||||
fclose(f);
|
||||
return std::make_pair(false,"");
|
||||
}
|
||||
retval.append(buffer, buffer+n);
|
||||
} while (!feof(f) && retval.size() <= maxsize);
|
||||
fclose(f);
|
||||
return std::make_pair(true,retval);
|
||||
}
|
||||
|
||||
bool WriteBinaryFile(const fs::path &filename, const std::string &data)
|
||||
{
|
||||
FILE *f = fsbridge::fopen(filename, "wb");
|
||||
if (f == nullptr)
|
||||
return false;
|
||||
if (fwrite(data.data(), 1, data.size(), f) != data.size()) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
if (fclose(f) != 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
29
src/util/readwritefile.h
Normal file
29
src/util/readwritefile.h
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2015-2020 The Bitcoin Core developers
|
||||
// Copyright (c) 2016-2022 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
#ifndef BITCOIN_UTIL_READWRITEFILE_H
|
||||
#define BITCOIN_UTIL_READWRITEFILE_H
|
||||
|
||||
#include <fs.h>
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
/** Read full contents of a file and return them in a std::string.
|
||||
* Returns a pair <status, string>.
|
||||
* If an error occurred, status will be false, otherwise status will be true and the data will be returned in string.
|
||||
*
|
||||
* @param maxsize Puts a maximum size limit on the file that is read. If the file is larger than this, truncated data
|
||||
* (with len > maxsize) will be returned.
|
||||
*/
|
||||
std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits<size_t>::max());
|
||||
|
||||
/** Write contents of std::string to a file.
|
||||
* @return true on success.
|
||||
*/
|
||||
bool WriteBinaryFile(const fs::path &filename, const std::string &data);
|
||||
|
||||
#endif /* BITCOIN_UTIL_READWRITEFILE_H */
|
||||
328
src/util/sock.cpp
Normal file
328
src/util/sock.cpp
Normal file
@@ -0,0 +1,328 @@
|
||||
// Copyright (c) 2020-2021 The Bitcoin Core developers
|
||||
// Copyright (c) 2016-2022 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
#include <compat.h>
|
||||
#include <tinyformat.h>
|
||||
#include <util/sock.h>
|
||||
#include <utiltime.h>
|
||||
#include <util.h>
|
||||
#include <codecvt>
|
||||
#include <cwchar>
|
||||
#include <locale>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#ifdef USE_POLL
|
||||
#include <poll.h>
|
||||
#endif
|
||||
|
||||
static inline bool IOErrorIsPermanent(int err)
|
||||
{
|
||||
return err != WSAEAGAIN && err != WSAEINTR && err != WSAEWOULDBLOCK && err != WSAEINPROGRESS;
|
||||
}
|
||||
|
||||
Sock::Sock() : m_socket(INVALID_SOCKET) {}
|
||||
|
||||
Sock::Sock(SOCKET s) : m_socket(s) {}
|
||||
|
||||
Sock::Sock(Sock&& other)
|
||||
{
|
||||
m_socket = other.m_socket;
|
||||
other.m_socket = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
Sock::~Sock() { Reset(); }
|
||||
|
||||
Sock& Sock::operator=(Sock&& other)
|
||||
{
|
||||
Reset();
|
||||
m_socket = other.m_socket;
|
||||
other.m_socket = INVALID_SOCKET;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SOCKET Sock::Get() const { return m_socket; }
|
||||
|
||||
SOCKET Sock::Release()
|
||||
{
|
||||
const SOCKET s = m_socket;
|
||||
m_socket = INVALID_SOCKET;
|
||||
return s;
|
||||
}
|
||||
|
||||
void Sock::Reset() { CloseSocket(m_socket); }
|
||||
|
||||
ssize_t Sock::Send(const void* data, size_t len, int flags) const
|
||||
{
|
||||
return send(m_socket, static_cast<const char*>(data), len, flags);
|
||||
}
|
||||
|
||||
ssize_t Sock::Recv(void* buf, size_t len, int flags) const
|
||||
{
|
||||
return recv(m_socket, static_cast<char*>(buf), len, flags);
|
||||
}
|
||||
|
||||
int Sock::Connect(const sockaddr* addr, socklen_t addr_len) const
|
||||
{
|
||||
return connect(m_socket, addr, addr_len);
|
||||
}
|
||||
|
||||
int Sock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const
|
||||
{
|
||||
return getsockopt(m_socket, level, opt_name, static_cast<char*>(opt_val), opt_len);
|
||||
}
|
||||
|
||||
bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
|
||||
{
|
||||
#ifdef USE_POLL
|
||||
pollfd fd;
|
||||
fd.fd = m_socket;
|
||||
fd.events = 0;
|
||||
if (requested & RECV) {
|
||||
fd.events |= POLLIN;
|
||||
}
|
||||
if (requested & SEND) {
|
||||
fd.events |= POLLOUT;
|
||||
}
|
||||
|
||||
if (poll(&fd, 1, count_milliseconds(timeout)) == SOCKET_ERROR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (occurred != nullptr) {
|
||||
*occurred = 0;
|
||||
if (fd.revents & POLLIN) {
|
||||
*occurred |= RECV;
|
||||
}
|
||||
if (fd.revents & POLLOUT) {
|
||||
*occurred |= SEND;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
if (!IsSelectableSocket(m_socket)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fd_set fdset_recv;
|
||||
fd_set fdset_send;
|
||||
FD_ZERO(&fdset_recv);
|
||||
FD_ZERO(&fdset_send);
|
||||
|
||||
if (requested & RECV) {
|
||||
FD_SET(m_socket, &fdset_recv);
|
||||
}
|
||||
|
||||
if (requested & SEND) {
|
||||
FD_SET(m_socket, &fdset_send);
|
||||
}
|
||||
|
||||
timeval timeout_struct = MillisToTimeval(timeout.count());
|
||||
|
||||
if (select(m_socket + 1, &fdset_recv, &fdset_send, nullptr, &timeout_struct) == SOCKET_ERROR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (occurred != nullptr) {
|
||||
*occurred = 0;
|
||||
if (FD_ISSET(m_socket, &fdset_recv)) {
|
||||
*occurred |= RECV;
|
||||
}
|
||||
if (FD_ISSET(m_socket, &fdset_send)) {
|
||||
*occurred |= SEND;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif /* USE_POLL */
|
||||
}
|
||||
|
||||
void Sock::SendComplete(const std::string& data,
|
||||
std::chrono::milliseconds timeout) const
|
||||
{
|
||||
const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
|
||||
size_t sent{0};
|
||||
|
||||
for (;;) {
|
||||
const ssize_t ret{Send(data.data() + sent, data.size() - sent, MSG_NOSIGNAL)};
|
||||
|
||||
if (ret > 0) {
|
||||
sent += static_cast<size_t>(ret);
|
||||
if (sent == data.size()) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
const int err{WSAGetLastError()};
|
||||
if (IOErrorIsPermanent(err)) {
|
||||
throw std::runtime_error(strprintf("send(): %s", NetworkErrorString(err)));
|
||||
}
|
||||
}
|
||||
|
||||
const auto now = GetTime<std::chrono::milliseconds>();
|
||||
|
||||
if (now >= deadline) {
|
||||
throw std::runtime_error(strprintf(
|
||||
"Send timeout (sent only %u of %u bytes before that)", sent, data.size()));
|
||||
}
|
||||
|
||||
// Wait for a short while (or the socket to become ready for sending) before retrying
|
||||
// if nothing was sent.
|
||||
const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
|
||||
(void)Wait(wait_time, SEND);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Sock::RecvUntilTerminator(uint8_t terminator,
|
||||
std::chrono::milliseconds timeout,
|
||||
size_t max_data) const
|
||||
{
|
||||
const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
|
||||
std::string data;
|
||||
bool terminator_found{false};
|
||||
|
||||
// We must not consume any bytes past the terminator from the socket.
|
||||
// One option is to read one byte at a time and check if we have read a terminator.
|
||||
// However that is very slow. Instead, we peek at what is in the socket and only read
|
||||
// as many bytes as possible without crossing the terminator.
|
||||
// Reading 64 MiB of random data with 262526 terminator chars takes 37 seconds to read
|
||||
// one byte at a time VS 0.71 seconds with the "peek" solution below. Reading one byte
|
||||
// at a time is about 50 times slower.
|
||||
|
||||
for (;;) {
|
||||
if (data.size() >= max_data) {
|
||||
throw std::runtime_error(
|
||||
strprintf("Received too many bytes without a terminator (%u)", data.size()));
|
||||
}
|
||||
|
||||
char buf[512];
|
||||
|
||||
const ssize_t peek_ret{Recv(buf, std::min(sizeof(buf), max_data - data.size()), MSG_PEEK)};
|
||||
|
||||
switch (peek_ret) {
|
||||
case -1: {
|
||||
const int err{WSAGetLastError()};
|
||||
if (IOErrorIsPermanent(err)) {
|
||||
throw std::runtime_error(strprintf("recv(): %s", NetworkErrorString(err)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0:
|
||||
throw std::runtime_error("Connection unexpectedly closed by peer");
|
||||
default:
|
||||
auto end = buf + peek_ret;
|
||||
auto terminator_pos = std::find(buf, end, terminator);
|
||||
terminator_found = terminator_pos != end;
|
||||
|
||||
const size_t try_len{terminator_found ? terminator_pos - buf + 1 :
|
||||
static_cast<size_t>(peek_ret)};
|
||||
|
||||
const ssize_t read_ret{Recv(buf, try_len, 0)};
|
||||
|
||||
if (read_ret < 0 || static_cast<size_t>(read_ret) != try_len) {
|
||||
throw std::runtime_error(
|
||||
strprintf("recv() returned %u bytes on attempt to read %u bytes but previous "
|
||||
"peek claimed %u bytes are available",
|
||||
read_ret, try_len, peek_ret));
|
||||
}
|
||||
|
||||
// Don't include the terminator in the output.
|
||||
const size_t append_len{terminator_found ? try_len - 1 : try_len};
|
||||
|
||||
data.append(buf, buf + append_len);
|
||||
|
||||
if (terminator_found) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
const auto now = GetTime<std::chrono::milliseconds>();
|
||||
|
||||
if (now >= deadline) {
|
||||
throw std::runtime_error(strprintf(
|
||||
"Receive timeout (received %u bytes without terminator before that)", data.size()));
|
||||
}
|
||||
|
||||
// Wait for a short while (or the socket to become ready for reading) before retrying.
|
||||
const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
|
||||
(void)Wait(wait_time, RECV);
|
||||
}
|
||||
}
|
||||
|
||||
bool Sock::IsConnected(std::string& errmsg) const
|
||||
{
|
||||
if (m_socket == INVALID_SOCKET) {
|
||||
errmsg = "not connected";
|
||||
return false;
|
||||
}
|
||||
|
||||
char c;
|
||||
switch (Recv(&c, sizeof(c), MSG_PEEK)) {
|
||||
case -1: {
|
||||
const int err = WSAGetLastError();
|
||||
if (IOErrorIsPermanent(err)) {
|
||||
errmsg = NetworkErrorString(err);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case 0:
|
||||
errmsg = "closed";
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
std::string NetworkErrorString(int err)
|
||||
{
|
||||
wchar_t buf[256];
|
||||
buf[0] = 0;
|
||||
if(FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
|
||||
nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
buf, ARRAYSIZE(buf), nullptr))
|
||||
{
|
||||
return strprintf("%s (%d)", std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().to_bytes(buf), err);
|
||||
}
|
||||
else
|
||||
{
|
||||
return strprintf("Unknown error (%d)", err);
|
||||
}
|
||||
}
|
||||
#else
|
||||
std::string NetworkErrorString(int err)
|
||||
{
|
||||
char buf[256];
|
||||
buf[0] = 0;
|
||||
/* Too bad there are two incompatible implementations of the
|
||||
* thread-safe strerror. */
|
||||
const char *s;
|
||||
#ifdef STRERROR_R_CHAR_P /* GNU variant can return a pointer outside the passed buffer */
|
||||
s = strerror_r(err, buf, sizeof(buf));
|
||||
#else /* POSIX variant always returns message in buffer */
|
||||
s = buf;
|
||||
if (strerror_r(err, buf, sizeof(buf)))
|
||||
buf[0] = 0;
|
||||
#endif
|
||||
return strprintf("%s (%d)", s, err);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool CloseSocket(SOCKET& hSocket)
|
||||
{
|
||||
if (hSocket == INVALID_SOCKET)
|
||||
return false;
|
||||
#ifdef WIN32
|
||||
int ret = closesocket(hSocket);
|
||||
#else
|
||||
int ret = close(hSocket);
|
||||
#endif
|
||||
if (ret) {
|
||||
LogPrintf("Socket close failed: %d. Error: %s\n", hSocket, NetworkErrorString(WSAGetLastError()));
|
||||
}
|
||||
hSocket = INVALID_SOCKET;
|
||||
return ret != SOCKET_ERROR;
|
||||
}
|
||||
184
src/util/sock.h
Normal file
184
src/util/sock.h
Normal file
@@ -0,0 +1,184 @@
|
||||
// Copyright (c) 2020-2021 The Bitcoin Core developers
|
||||
// Copyright (c) 2016-2022 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
#ifndef BITCOIN_UTIL_SOCK_H
|
||||
#define BITCOIN_UTIL_SOCK_H
|
||||
|
||||
#include <compat.h>
|
||||
// #include <threadinterrupt.h>
|
||||
#include <utiltime.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* Maximum time to wait for I/O readiness.
|
||||
* It will take up until this time to break off in case of an interruption.
|
||||
*/
|
||||
static constexpr auto MAX_WAIT_FOR_IO = 1000;
|
||||
|
||||
/**
|
||||
* RAII helper class that manages a socket. Mimics `std::unique_ptr`, but instead of a pointer it
|
||||
* contains a socket and closes it automatically when it goes out of scope.
|
||||
*/
|
||||
class Sock
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Default constructor, creates an empty object that does nothing when destroyed.
|
||||
*/
|
||||
Sock();
|
||||
|
||||
/**
|
||||
* Take ownership of an existent socket.
|
||||
*/
|
||||
explicit Sock(SOCKET s);
|
||||
|
||||
/**
|
||||
* Copy constructor, disabled because closing the same socket twice is undesirable.
|
||||
*/
|
||||
Sock(const Sock&) = delete;
|
||||
|
||||
/**
|
||||
* Move constructor, grab the socket from another object and close ours (if set).
|
||||
*/
|
||||
Sock(Sock&& other);
|
||||
|
||||
/**
|
||||
* Destructor, close the socket or do nothing if empty.
|
||||
*/
|
||||
virtual ~Sock();
|
||||
|
||||
/**
|
||||
* Copy assignment operator, disabled because closing the same socket twice is undesirable.
|
||||
*/
|
||||
Sock& operator=(const Sock&) = delete;
|
||||
|
||||
/**
|
||||
* Move assignment operator, grab the socket from another object and close ours (if set).
|
||||
*/
|
||||
virtual Sock& operator=(Sock&& other);
|
||||
|
||||
/**
|
||||
* Get the value of the contained socket.
|
||||
* @return socket or INVALID_SOCKET if empty
|
||||
*/
|
||||
virtual SOCKET Get() const;
|
||||
|
||||
/**
|
||||
* Get the value of the contained socket and drop ownership. It will not be closed by the
|
||||
* destructor after this call.
|
||||
* @return socket or INVALID_SOCKET if empty
|
||||
*/
|
||||
virtual SOCKET Release();
|
||||
|
||||
/**
|
||||
* Close if non-empty.
|
||||
*/
|
||||
virtual void Reset();
|
||||
|
||||
/**
|
||||
* send(2) wrapper. Equivalent to `send(this->Get(), data, len, flags);`. Code that uses this
|
||||
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
|
||||
*/
|
||||
virtual ssize_t Send(const void* data, size_t len, int flags) const;
|
||||
|
||||
/**
|
||||
* recv(2) wrapper. Equivalent to `recv(this->Get(), buf, len, flags);`. Code that uses this
|
||||
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
|
||||
*/
|
||||
virtual ssize_t Recv(void* buf, size_t len, int flags) const;
|
||||
|
||||
/**
|
||||
* connect(2) wrapper. Equivalent to `connect(this->Get(), addr, addrlen)`. Code that uses this
|
||||
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
|
||||
*/
|
||||
virtual int Connect(const sockaddr* addr, socklen_t addr_len) const;
|
||||
|
||||
/**
|
||||
* getsockopt(2) wrapper. Equivalent to
|
||||
* `getsockopt(this->Get(), level, opt_name, opt_val, opt_len)`. Code that uses this
|
||||
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
|
||||
*/
|
||||
virtual int GetSockOpt(int level,
|
||||
int opt_name,
|
||||
void* opt_val,
|
||||
socklen_t* opt_len) const;
|
||||
|
||||
using Event = uint8_t;
|
||||
|
||||
/**
|
||||
* If passed to `Wait()`, then it will wait for readiness to read from the socket.
|
||||
*/
|
||||
static constexpr Event RECV = 0b01;
|
||||
|
||||
/**
|
||||
* If passed to `Wait()`, then it will wait for readiness to send to the socket.
|
||||
*/
|
||||
static constexpr Event SEND = 0b10;
|
||||
|
||||
/**
|
||||
* Wait for readiness for input (recv) or output (send).
|
||||
* @param[in] timeout Wait this much for at least one of the requested events to occur.
|
||||
* @param[in] requested Wait for those events, bitwise-or of `RECV` and `SEND`.
|
||||
* @param[out] occurred If not nullptr and `true` is returned, then upon return this
|
||||
* indicates which of the requested events occurred. A timeout is indicated by return
|
||||
* value of `true` and `occurred` being set to 0.
|
||||
* @return true on success and false otherwise
|
||||
*/
|
||||
virtual bool Wait(std::chrono::milliseconds timeout,
|
||||
Event requested,
|
||||
Event* occurred = nullptr) const;
|
||||
|
||||
/* Higher level, convenience, methods. These may throw. */
|
||||
|
||||
/**
|
||||
* Send the given data, retrying on transient errors.
|
||||
* @param[in] data Data to send.
|
||||
* @param[in] timeout Timeout for the entire operation.
|
||||
* @param[in] interrupt If this is signaled then the operation is canceled.
|
||||
* @throws std::runtime_error if the operation cannot be completed. In this case only some of
|
||||
* the data will be written to the socket.
|
||||
*/
|
||||
virtual void SendComplete(const std::string& data,
|
||||
std::chrono::milliseconds timeout) const;
|
||||
|
||||
/**
|
||||
* Read from socket until a terminator character is encountered. Will never consume bytes past
|
||||
* the terminator from the socket.
|
||||
* @param[in] terminator Character up to which to read from the socket.
|
||||
* @param[in] timeout Timeout for the entire operation.
|
||||
* @param[in] interrupt If this is signaled then the operation is canceled.
|
||||
* @param[in] max_data The maximum amount of data (in bytes) to receive. If this many bytes
|
||||
* are received and there is still no terminator, then this method will throw an exception.
|
||||
* @return The data that has been read, without the terminating character.
|
||||
* @throws std::runtime_error if the operation cannot be completed. In this case some bytes may
|
||||
* have been consumed from the socket.
|
||||
*/
|
||||
virtual std::string RecvUntilTerminator(uint8_t terminator,
|
||||
std::chrono::milliseconds timeout,
|
||||
size_t max_data) const;
|
||||
|
||||
/**
|
||||
* Check if still connected.
|
||||
* @param[out] errmsg The error string, if the socket has been disconnected.
|
||||
* @return true if connected
|
||||
*/
|
||||
virtual bool IsConnected(std::string& errmsg) const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Contained socket. `INVALID_SOCKET` designates the object is empty.
|
||||
*/
|
||||
SOCKET m_socket;
|
||||
};
|
||||
|
||||
/** Return readable error string for a network error code */
|
||||
std::string NetworkErrorString(int err);
|
||||
|
||||
/** Close socket and set hSocket to INVALID_SOCKET */
|
||||
bool CloseSocket(SOCKET& hSocket);
|
||||
|
||||
#endif // BITCOIN_UTIL_SOCK_H
|
||||
68
src/util/spanparsing.cpp
Normal file
68
src/util/spanparsing.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2018-2019 The Bitcoin Core developers
|
||||
// Copyright (c) 2016-2022 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
#include <util/spanparsing.h>
|
||||
|
||||
#include <span.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace spanparsing {
|
||||
|
||||
bool Const(const std::string& str, Span<const char>& sp)
|
||||
{
|
||||
if ((size_t)sp.size() >= str.size() && std::equal(str.begin(), str.end(), sp.begin())) {
|
||||
sp = sp.subspan(str.size());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Func(const std::string& str, Span<const char>& sp)
|
||||
{
|
||||
if ((size_t)sp.size() >= str.size() + 2 && sp[str.size()] == '(' && sp[sp.size() - 1] == ')' && std::equal(str.begin(), str.end(), sp.begin())) {
|
||||
sp = sp.subspan(str.size() + 1, sp.size() - str.size() - 2);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Span<const char> Expr(Span<const char>& sp)
|
||||
{
|
||||
int level = 0;
|
||||
auto it = sp.begin();
|
||||
while (it != sp.end()) {
|
||||
if (*it == '(' || *it == '{') {
|
||||
++level;
|
||||
} else if (level && (*it == ')' || *it == '}')) {
|
||||
--level;
|
||||
} else if (level == 0 && (*it == ')' || *it == '}' || *it == ',')) {
|
||||
break;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
Span<const char> ret = sp.first(it - sp.begin());
|
||||
sp = sp.subspan(it - sp.begin());
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<Span<const char>> Split(const Span<const char>& sp, char sep)
|
||||
{
|
||||
std::vector<Span<const char>> ret;
|
||||
auto it = sp.begin();
|
||||
auto start = it;
|
||||
while (it != sp.end()) {
|
||||
if (*it == sep) {
|
||||
ret.emplace_back(start, it);
|
||||
start = it + 1;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
ret.emplace_back(start, it);
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace spanparsing
|
||||
50
src/util/spanparsing.h
Normal file
50
src/util/spanparsing.h
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2018-2019 The Bitcoin Core developers
|
||||
// Copyright (c) 2016-2022 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
#ifndef BITCOIN_UTIL_SPANPARSING_H
|
||||
#define BITCOIN_UTIL_SPANPARSING_H
|
||||
|
||||
#include <span.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace spanparsing {
|
||||
|
||||
/** Parse a constant.
|
||||
*
|
||||
* If sp's initial part matches str, sp is updated to skip that part, and true is returned.
|
||||
* Otherwise sp is unmodified and false is returned.
|
||||
*/
|
||||
bool Const(const std::string& str, Span<const char>& sp);
|
||||
|
||||
/** Parse a function call.
|
||||
*
|
||||
* If sp's initial part matches str + "(", and sp ends with ")", sp is updated to be the
|
||||
* section between the braces, and true is returned. Otherwise sp is unmodified and false
|
||||
* is returned.
|
||||
*/
|
||||
bool Func(const std::string& str, Span<const char>& sp);
|
||||
|
||||
/** Extract the expression that sp begins with.
|
||||
*
|
||||
* This function will return the initial part of sp, up to (but not including) the first
|
||||
* comma or closing brace, skipping ones that are surrounded by braces. So for example,
|
||||
* for "foo(bar(1),2),3" the initial part "foo(bar(1),2)" will be returned. sp will be
|
||||
* updated to skip the initial part that is returned.
|
||||
*/
|
||||
Span<const char> Expr(Span<const char>& sp);
|
||||
|
||||
/** Split a string on every instance of sep, returning a vector.
|
||||
*
|
||||
* If sep does not occur in sp, a singleton with the entirety of sp is returned.
|
||||
*
|
||||
* Note that this function does not care about braces, so splitting
|
||||
* "foo(bar(1),2),3) on ',' will return {"foo(bar(1)", "2)", "3)"}.
|
||||
*/
|
||||
std::vector<Span<const char>> Split(const Span<const char>& sp, char sep);
|
||||
|
||||
} // namespace spanparsing
|
||||
|
||||
#endif // BITCOIN_UTIL_SPANPARSING_H
|
||||
655
src/util/strencodings.cpp
Normal file
655
src/util/strencodings.cpp
Normal file
@@ -0,0 +1,655 @@
|
||||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2020 The Bitcoin Core developers
|
||||
// Copyright (c) 2016-2022 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
#include "util/strencodings.h"
|
||||
#include "util/string.h"
|
||||
|
||||
#include <tinyformat.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <errno.h>
|
||||
#include <limits>
|
||||
|
||||
using namespace std;
|
||||
|
||||
static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
|
||||
static const std::string SAFE_CHARS[] =
|
||||
{
|
||||
CHARS_ALPHA_NUM + " .,;-_/:?@()", // SAFE_CHARS_DEFAULT
|
||||
CHARS_ALPHA_NUM + " .,;-_?@", // SAFE_CHARS_UA_COMMENT
|
||||
CHARS_ALPHA_NUM + ".-_", // SAFE_CHARS_FILENAME
|
||||
CHARS_ALPHA_NUM + "!*'();:@&=+$,/?#[]-_.~%", // SAFE_CHARS_URI
|
||||
};
|
||||
|
||||
std::string SanitizeString(const std::string& str, int rule)
|
||||
{
|
||||
std::string strResult;
|
||||
for (std::string::size_type i = 0; i < str.size(); i++)
|
||||
{
|
||||
if (SAFE_CHARS[rule].find(str[i]) != std::string::npos)
|
||||
strResult.push_back(str[i]);
|
||||
}
|
||||
return strResult;
|
||||
}
|
||||
|
||||
string SanitizeFilename(const string& str)
|
||||
{
|
||||
/**
|
||||
* safeChars chosen to restrict filename, keeping it simple to avoid cross-platform issues.
|
||||
* http://stackoverflow.com/a/2306003
|
||||
*/
|
||||
static string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890");
|
||||
string strResult;
|
||||
for (std::string::size_type i = 0; i < str.size(); i++)
|
||||
{
|
||||
if (safeChars.find(str[i]) != std::string::npos)
|
||||
strResult.push_back(str[i]);
|
||||
}
|
||||
return strResult;
|
||||
}
|
||||
|
||||
std::string HexInt(uint32_t val)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::setfill('0') << std::setw(sizeof(uint32_t) * 2) << std::hex << val;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
uint32_t ParseHexToUInt32(const std::string& str) {
|
||||
std::istringstream converter(str);
|
||||
uint32_t value;
|
||||
converter >> std::hex >> value;
|
||||
return value;
|
||||
}
|
||||
|
||||
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,
|
||||
0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1,
|
||||
-1,0xa,0xb,0xc,0xd,0xe,0xf,-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,0xa,0xb,0xc,0xd,0xe,0xf,-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,-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,-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,-1,-1,-1,-1,-1,-1,-1,-1,-1, };
|
||||
|
||||
signed char HexDigit(char c)
|
||||
{
|
||||
return p_util_hexdigit[(unsigned char)c];
|
||||
}
|
||||
|
||||
bool IsHex(const std::string& str)
|
||||
{
|
||||
for(std::string::const_iterator it(str.begin()); it != str.end(); ++it)
|
||||
{
|
||||
if (HexDigit(*it) < 0)
|
||||
return false;
|
||||
}
|
||||
return (str.size() > 0) && (str.size()%2 == 0);
|
||||
}
|
||||
|
||||
bool IsHexNumber(const std::string& str)
|
||||
{
|
||||
size_t starting_location = 0;
|
||||
if (str.size() > 2 && *str.begin() == '0' && *(str.begin()+1) == 'x') {
|
||||
starting_location = 2;
|
||||
}
|
||||
for (const char c : str.substr(starting_location)) {
|
||||
if (HexDigit(c) < 0) return false;
|
||||
}
|
||||
// Return false for empty string or "0x".
|
||||
return (str.size() > starting_location);
|
||||
}
|
||||
|
||||
std::vector<unsigned char> ParseHex(const char* psz)
|
||||
{
|
||||
// convert hex dump to vector
|
||||
std::vector<unsigned char> vch;
|
||||
while (true)
|
||||
{
|
||||
while (IsSpace(*psz))
|
||||
psz++;
|
||||
signed char c = HexDigit(*psz++);
|
||||
if (c == (signed char)-1)
|
||||
break;
|
||||
unsigned char n = (c << 4);
|
||||
c = HexDigit(*psz++);
|
||||
if (c == (signed char)-1)
|
||||
break;
|
||||
n |= c;
|
||||
vch.push_back(n);
|
||||
}
|
||||
return vch;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> ParseHex(const std::string& str)
|
||||
{
|
||||
return ParseHex(str.c_str());
|
||||
}
|
||||
|
||||
void SplitHostPort(std::string in, int &portOut, std::string &hostOut) {
|
||||
size_t colon = in.find_last_of(':');
|
||||
// if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator
|
||||
bool fHaveColon = colon != in.npos;
|
||||
bool fBracketed = fHaveColon && (in[0]=='[' && in[colon-1]==']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe
|
||||
bool fMultiColon = fHaveColon && (in.find_last_of(':',colon-1) != in.npos);
|
||||
if (fHaveColon && (colon==0 || fBracketed || !fMultiColon)) {
|
||||
int32_t n;
|
||||
if (ParseInt32(in.substr(colon + 1), &n) && n > 0 && n < 0x10000) {
|
||||
in = in.substr(0, colon);
|
||||
portOut = n;
|
||||
}
|
||||
}
|
||||
if (in.size()>0 && in[0] == '[' && in[in.size()-1] == ']')
|
||||
hostOut = in.substr(1, in.size()-2);
|
||||
else
|
||||
hostOut = in;
|
||||
}
|
||||
|
||||
std::string EncodeBase64(Span<const unsigned char> input)
|
||||
{
|
||||
static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
std::string str;
|
||||
str.reserve(((input.size() + 2) / 3) * 4);
|
||||
ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, input.begin(), input.end());
|
||||
while (str.size() % 4) str += '=';
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string EncodeBase64(const unsigned char* pch, size_t len)
|
||||
{
|
||||
static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
std::string str;
|
||||
str.reserve(((len + 2) / 3) * 4);
|
||||
ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, pch, pch + len);
|
||||
while (str.size() % 4) str += '=';
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string EncodeBase64(const std::string& str)
|
||||
{
|
||||
return EncodeBase64((const unsigned char*)str.data(), str.size());
|
||||
}
|
||||
|
||||
std::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid)
|
||||
{
|
||||
static const int decode64_table[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, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
|
||||
-1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
|
||||
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, -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, -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, -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
|
||||
};
|
||||
|
||||
const char* e = p;
|
||||
std::vector<uint8_t> val;
|
||||
val.reserve(strlen(p));
|
||||
while (*p != 0) {
|
||||
int x = decode64_table[(unsigned char)*p];
|
||||
if (x == -1) break;
|
||||
val.push_back(x);
|
||||
++p;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> ret;
|
||||
ret.reserve((val.size() * 3) / 4);
|
||||
bool valid = ConvertBits<6, 8, false>([&](unsigned char c) { ret.push_back(c); }, val.begin(), val.end());
|
||||
|
||||
const char* q = p;
|
||||
while (valid && *p != 0) {
|
||||
if (*p != '=') {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
valid = valid && (p - e) % 4 == 0 && p - q < 4;
|
||||
if (pf_invalid) *pf_invalid = !valid;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string DecodeBase64(const std::string& str, bool* pf_invalid)
|
||||
{
|
||||
if (!ValidAsCString(str)) {
|
||||
if (pf_invalid) {
|
||||
*pf_invalid = true;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
std::vector<unsigned char> vchRet = DecodeBase64(str.c_str(), pf_invalid);
|
||||
return std::string((const char*)vchRet.data(), vchRet.size());
|
||||
}
|
||||
|
||||
std::string EncodeBase32(Span<const unsigned char> input, bool pad)
|
||||
{
|
||||
static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567";
|
||||
|
||||
std::string str;
|
||||
str.reserve(((input.size() + 4) / 5) * 8);
|
||||
ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, input.begin(), input.end());
|
||||
if (pad) {
|
||||
while (str.size() % 8) {
|
||||
str += '=';
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string EncodeBase32(const std::string& str, bool pad)
|
||||
{
|
||||
return EncodeBase32(MakeUCharSpan(str), pad);
|
||||
}
|
||||
|
||||
std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid)
|
||||
{
|
||||
static const int decode32_table[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, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 0, 1, 2,
|
||||
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 24, 25, -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, -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, -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
|
||||
};
|
||||
|
||||
const char* e = p;
|
||||
std::vector<uint8_t> val;
|
||||
val.reserve(strlen(p));
|
||||
while (*p != 0) {
|
||||
int x = decode32_table[(unsigned char)*p];
|
||||
if (x == -1) break;
|
||||
val.push_back(x);
|
||||
++p;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> ret;
|
||||
ret.reserve((val.size() * 5) / 8);
|
||||
bool valid = ConvertBits<5, 8, false>([&](unsigned char c) { ret.push_back(c); }, val.begin(), val.end());
|
||||
|
||||
const char* q = p;
|
||||
while (valid && *p != 0) {
|
||||
if (*p != '=') {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
valid = valid && (p - e) % 8 == 0 && p - q < 8;
|
||||
if (pf_invalid) *pf_invalid = !valid;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string DecodeBase32(const std::string& str, bool* pf_invalid)
|
||||
{
|
||||
if (!ValidAsCString(str)) {
|
||||
if (pf_invalid) {
|
||||
*pf_invalid = true;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
std::vector<unsigned char> vchRet = DecodeBase32(str.c_str(), pf_invalid);
|
||||
return std::string((const char*)vchRet.data(), vchRet.size());
|
||||
}
|
||||
|
||||
static bool ParsePrechecks(const std::string& str)
|
||||
{
|
||||
if (str.empty()) // No empty string allowed
|
||||
return false;
|
||||
if (str.size() >= 1 && (IsSpace(str[0]) || IsSpace(str[str.size()-1]))) // No padding allowed
|
||||
return false;
|
||||
if (!ValidAsCString(str)) // No embedded NUL characters allowed
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseInt32(const std::string& str, int32_t *out)
|
||||
{
|
||||
if (!ParsePrechecks(str))
|
||||
return false;
|
||||
char *endp = nullptr;
|
||||
errno = 0; // strtol will not set errno if valid
|
||||
long int n = strtol(str.c_str(), &endp, 10);
|
||||
if(out) *out = (int32_t)n;
|
||||
// Note that strtol returns a *long int*, so even if strtol doesn't report an over/underflow
|
||||
// we still have to check that the returned value is within the range of an *int32_t*. On 64-bit
|
||||
// platforms the size of these types may be different.
|
||||
return endp && *endp == 0 && !errno &&
|
||||
n >= std::numeric_limits<int32_t>::min() &&
|
||||
n <= std::numeric_limits<int32_t>::max();
|
||||
}
|
||||
|
||||
bool ParseInt64(const std::string& str, int64_t *out)
|
||||
{
|
||||
if (!ParsePrechecks(str))
|
||||
return false;
|
||||
char *endp = nullptr;
|
||||
errno = 0; // strtoll will not set errno if valid
|
||||
long long int n = strtoll(str.c_str(), &endp, 10);
|
||||
if(out) *out = (int64_t)n;
|
||||
// Note that strtoll returns a *long long int*, so even if strtol doesn't report an over/underflow
|
||||
// we still have to check that the returned value is within the range of an *int64_t*.
|
||||
return endp && *endp == 0 && !errno &&
|
||||
n >= std::numeric_limits<int64_t>::min() &&
|
||||
n <= std::numeric_limits<int64_t>::max();
|
||||
}
|
||||
|
||||
bool ParseUInt8(const std::string& str, uint8_t *out)
|
||||
{
|
||||
uint32_t u32;
|
||||
if (!ParseUInt32(str, &u32) || u32 > std::numeric_limits<uint8_t>::max()) {
|
||||
return false;
|
||||
}
|
||||
if (out != nullptr) {
|
||||
*out = static_cast<uint8_t>(u32);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseUInt32(const std::string& str, uint32_t *out)
|
||||
{
|
||||
if (!ParsePrechecks(str))
|
||||
return false;
|
||||
if (str.size() >= 1 && str[0] == '-') // Reject negative values, unfortunately strtoul accepts these by default if they fit in the range
|
||||
return false;
|
||||
char *endp = nullptr;
|
||||
errno = 0; // strtoul will not set errno if valid
|
||||
unsigned long int n = strtoul(str.c_str(), &endp, 10);
|
||||
if(out) *out = (uint32_t)n;
|
||||
// Note that strtoul returns a *unsigned long int*, so even if it doesn't report an over/underflow
|
||||
// we still have to check that the returned value is within the range of an *uint32_t*. On 64-bit
|
||||
// platforms the size of these types may be different.
|
||||
return endp && *endp == 0 && !errno &&
|
||||
n <= std::numeric_limits<uint32_t>::max();
|
||||
}
|
||||
|
||||
bool ParseUInt64(const std::string& str, uint64_t *out)
|
||||
{
|
||||
if (!ParsePrechecks(str))
|
||||
return false;
|
||||
if (str.size() >= 1 && str[0] == '-') // Reject negative values, unfortunately strtoull accepts these by default if they fit in the range
|
||||
return false;
|
||||
char *endp = nullptr;
|
||||
errno = 0; // strtoull will not set errno if valid
|
||||
unsigned long long int n = strtoull(str.c_str(), &endp, 10);
|
||||
if(out) *out = (uint64_t)n;
|
||||
// Note that strtoull returns a *unsigned long long int*, so even if it doesn't report an over/underflow
|
||||
// we still have to check that the returned value is within the range of an *uint64_t*.
|
||||
return endp && *endp == 0 && !errno &&
|
||||
n <= std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
|
||||
|
||||
bool ParseDouble(const std::string& str, double *out)
|
||||
{
|
||||
if (!ParsePrechecks(str))
|
||||
return false;
|
||||
if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed
|
||||
return false;
|
||||
std::istringstream text(str);
|
||||
text.imbue(std::locale::classic());
|
||||
double result;
|
||||
text >> result;
|
||||
if(out) *out = result;
|
||||
return text.eof() && !text.fail();
|
||||
}
|
||||
|
||||
std::string FormatParagraph(const std::string& in, size_t width, size_t indent)
|
||||
{
|
||||
std::stringstream out;
|
||||
size_t ptr = 0;
|
||||
size_t indented = 0;
|
||||
while (ptr < in.size())
|
||||
{
|
||||
size_t lineend = in.find_first_of('\n', ptr);
|
||||
if (lineend == std::string::npos) {
|
||||
lineend = in.size();
|
||||
}
|
||||
const size_t linelen = lineend - ptr;
|
||||
const size_t rem_width = width - indented;
|
||||
if (linelen <= rem_width) {
|
||||
out << in.substr(ptr, linelen + 1);
|
||||
ptr = lineend + 1;
|
||||
indented = 0;
|
||||
} else {
|
||||
size_t finalspace = in.find_last_of(" \n", ptr + rem_width);
|
||||
if (finalspace == std::string::npos || finalspace < ptr) {
|
||||
// No place to break; just include the entire word and move on
|
||||
finalspace = in.find_first_of("\n ", ptr);
|
||||
if (finalspace == std::string::npos) {
|
||||
// End of the string, just add it and break
|
||||
out << in.substr(ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
out << in.substr(ptr, finalspace - ptr) << "\n";
|
||||
if (in[finalspace] == '\n') {
|
||||
indented = 0;
|
||||
} else if (indent) {
|
||||
out << std::string(indent, ' ');
|
||||
indented = indent;
|
||||
}
|
||||
ptr = finalspace + 1;
|
||||
}
|
||||
}
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string i64tostr(int64_t n)
|
||||
{
|
||||
return strprintf("%d", n);
|
||||
}
|
||||
|
||||
std::string itostr(int n)
|
||||
{
|
||||
return strprintf("%d", n);
|
||||
}
|
||||
|
||||
int64_t atoi64(const char* psz)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
return _atoi64(psz);
|
||||
#else
|
||||
return strtoll(psz, nullptr, 10);
|
||||
#endif
|
||||
}
|
||||
|
||||
int64_t atoi64(const std::string& str)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
return _atoi64(str.c_str());
|
||||
#else
|
||||
return strtoll(str.c_str(), nullptr, 10);
|
||||
#endif
|
||||
}
|
||||
|
||||
int atoi(const std::string& str)
|
||||
{
|
||||
return atoi(str.c_str());
|
||||
}
|
||||
|
||||
/** Upper bound for mantissa.
|
||||
* 10^18-1 is the largest arbitrary decimal that will fit in a signed 64-bit integer.
|
||||
* Larger integers cannot consist of arbitrary combinations of 0-9:
|
||||
*
|
||||
* 999999999999999999 1^18-1
|
||||
* 9223372036854775807 (1<<63)-1 (max int64_t)
|
||||
* 9999999999999999999 1^19-1 (would overflow)
|
||||
*/
|
||||
static const int64_t UPPER_BOUND = 1000000000000000000LL - 1LL;
|
||||
|
||||
/** Helper function for ParseFixedPoint */
|
||||
static inline bool ProcessMantissaDigit(char ch, int64_t &mantissa, int &mantissa_tzeros)
|
||||
{
|
||||
if(ch == '0')
|
||||
++mantissa_tzeros;
|
||||
else {
|
||||
for (int i=0; i<=mantissa_tzeros; ++i) {
|
||||
if (mantissa > (UPPER_BOUND / 10LL))
|
||||
return false; /* overflow */
|
||||
mantissa *= 10;
|
||||
}
|
||||
mantissa += ch - '0';
|
||||
mantissa_tzeros = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out)
|
||||
{
|
||||
int64_t mantissa = 0;
|
||||
int64_t exponent = 0;
|
||||
int mantissa_tzeros = 0;
|
||||
bool mantissa_sign = false;
|
||||
bool exponent_sign = false;
|
||||
int ptr = 0;
|
||||
int end = val.size();
|
||||
int point_ofs = 0;
|
||||
|
||||
if (ptr < end && val[ptr] == '-') {
|
||||
mantissa_sign = true;
|
||||
++ptr;
|
||||
}
|
||||
if (ptr < end)
|
||||
{
|
||||
if (val[ptr] == '0') {
|
||||
/* pass single 0 */
|
||||
++ptr;
|
||||
} else if (val[ptr] >= '1' && val[ptr] <= '9') {
|
||||
while (ptr < end && IsDigit(val[ptr])) {
|
||||
if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
|
||||
return false; /* overflow */
|
||||
++ptr;
|
||||
}
|
||||
} else return false; /* missing expected digit */
|
||||
} else return false; /* empty string or loose '-' */
|
||||
if (ptr < end && val[ptr] == '.')
|
||||
{
|
||||
++ptr;
|
||||
if (ptr < end && IsDigit(val[ptr]))
|
||||
{
|
||||
while (ptr < end && IsDigit(val[ptr])) {
|
||||
if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
|
||||
return false; /* overflow */
|
||||
++ptr;
|
||||
++point_ofs;
|
||||
}
|
||||
} else return false; /* missing expected digit */
|
||||
}
|
||||
if (ptr < end && (val[ptr] == 'e' || val[ptr] == 'E'))
|
||||
{
|
||||
++ptr;
|
||||
if (ptr < end && val[ptr] == '+')
|
||||
++ptr;
|
||||
else if (ptr < end && val[ptr] == '-') {
|
||||
exponent_sign = true;
|
||||
++ptr;
|
||||
}
|
||||
if (ptr < end && IsDigit(val[ptr])) {
|
||||
while (ptr < end && IsDigit(val[ptr])) {
|
||||
if (exponent > (UPPER_BOUND / 10LL))
|
||||
return false; /* overflow */
|
||||
exponent = exponent * 10 + val[ptr] - '0';
|
||||
++ptr;
|
||||
}
|
||||
} else return false; /* missing expected digit */
|
||||
}
|
||||
if (ptr != end)
|
||||
return false; /* trailing garbage */
|
||||
|
||||
/* finalize exponent */
|
||||
if (exponent_sign)
|
||||
exponent = -exponent;
|
||||
exponent = exponent - point_ofs + mantissa_tzeros;
|
||||
|
||||
/* finalize mantissa */
|
||||
if (mantissa_sign)
|
||||
mantissa = -mantissa;
|
||||
|
||||
/* convert to one 64-bit fixed-point value */
|
||||
exponent += decimals;
|
||||
if (exponent < 0)
|
||||
return false; /* cannot represent values smaller than 10^-decimals */
|
||||
if (exponent >= 18)
|
||||
return false; /* cannot represent values larger than or equal to 10^(18-decimals) */
|
||||
|
||||
for (int i=0; i < exponent; ++i) {
|
||||
if (mantissa > (UPPER_BOUND / 10LL) || mantissa < -(UPPER_BOUND / 10LL))
|
||||
return false; /* overflow */
|
||||
mantissa *= 10;
|
||||
}
|
||||
if (mantissa > UPPER_BOUND || mantissa < -UPPER_BOUND)
|
||||
return false; /* overflow */
|
||||
|
||||
if (amount_out)
|
||||
*amount_out = mantissa;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string ToLower(const std::string& str)
|
||||
{
|
||||
std::string r;
|
||||
for (auto ch : str) r += ToLower((unsigned char)ch);
|
||||
return r;
|
||||
}
|
||||
|
||||
std::string ToUpper(const std::string& str)
|
||||
{
|
||||
std::string r;
|
||||
for (auto ch : str) r += ToUpper((unsigned char)ch);
|
||||
return r;
|
||||
}
|
||||
|
||||
std::string Capitalize(std::string str)
|
||||
{
|
||||
if (str.empty()) return str;
|
||||
str[0] = ToUpper(str.front());
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string HexStr(const Span<const uint8_t> s)
|
||||
{
|
||||
std::string rv;
|
||||
static constexpr char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
||||
rv.reserve(s.size() * 2);
|
||||
for (uint8_t v: s) {
|
||||
rv.push_back(hexmap[v >> 4]);
|
||||
rv.push_back(hexmap[v & 15]);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
302
src/util/strencodings.h
Normal file
302
src/util/strencodings.h
Normal file
@@ -0,0 +1,302 @@
|
||||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2020 The Bitcoin Core developers
|
||||
// Copyright (c) 2016-2022 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
/**
|
||||
* Utilities for converting data from/to strings.
|
||||
*/
|
||||
#ifndef BITCOIN_UTIL_STRENCODINGS_H
|
||||
#define BITCOIN_UTIL_STRENCODINGS_H
|
||||
|
||||
#include "attributes.h"
|
||||
#include "span.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define BEGIN(a) ((char*)&(a))
|
||||
#define END(a) ((char*)&((&(a))[1]))
|
||||
#define UBEGIN(a) ((unsigned char*)&(a))
|
||||
#define UEND(a) ((unsigned char*)&((&(a))[1]))
|
||||
#define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0]))
|
||||
|
||||
/** This is needed because the foreach macro can't get over the comma in pair<t1, t2> */
|
||||
#define PAIRTYPE(t1, t2) std::pair<t1, t2>
|
||||
|
||||
/** Used by SanitizeString() */
|
||||
enum SafeChars
|
||||
{
|
||||
SAFE_CHARS_DEFAULT, //!< The full set of allowed chars
|
||||
SAFE_CHARS_UA_COMMENT, //!< BIP-0014 subset
|
||||
SAFE_CHARS_FILENAME, //!< Chars allowed in filenames
|
||||
SAFE_CHARS_URI, //!< Chars allowed in URIs (RFC 3986)
|
||||
};
|
||||
|
||||
std::string SanitizeFilename(const std::string& str);
|
||||
/**
|
||||
* Remove unsafe chars. Safe chars chosen to allow simple messages/URLs/email
|
||||
* addresses, but avoid anything even possibly remotely dangerous like & or >
|
||||
* @param[in] str The string to sanitize
|
||||
* @param[in] rule The set of safe chars to choose (default: least restrictive)
|
||||
* @return A new string without unsafe chars
|
||||
*/
|
||||
std::string SanitizeString(const std::string& str, int rule = SAFE_CHARS_DEFAULT);
|
||||
std::string HexInt(uint32_t val);
|
||||
uint32_t ParseHexToUInt32(const std::string& str);
|
||||
std::vector<unsigned char> ParseHex(const char* psz);
|
||||
std::vector<unsigned char> ParseHex(const std::string& str);
|
||||
signed char HexDigit(char c);
|
||||
/* Returns true if each character in str is a hex character, and has an even
|
||||
* number of hex digits.*/
|
||||
bool IsHex(const std::string& str);
|
||||
/**
|
||||
* Return true if the string is a hex number, optionally prefixed with "0x"
|
||||
*/
|
||||
bool IsHexNumber(const std::string& str);
|
||||
std::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid = nullptr);
|
||||
std::string DecodeBase64(const std::string& str, bool* pf_invalid = nullptr);
|
||||
std::string EncodeBase64(Span<const unsigned char> input);
|
||||
std::string EncodeBase64(const unsigned char* pch, size_t len);
|
||||
std::string EncodeBase64(const std::string& str);
|
||||
std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid = nullptr);
|
||||
std::string DecodeBase32(const std::string& str, bool* pf_invalid = nullptr);
|
||||
|
||||
/**
|
||||
* Base32 encode.
|
||||
* If `pad` is true, then the output will be padded with '=' so that its length
|
||||
* is a multiple of 8.
|
||||
*/
|
||||
std::string EncodeBase32(Span<const unsigned char> input, bool pad = true);
|
||||
|
||||
/**
|
||||
* Base32 encode.
|
||||
* If `pad` is true, then the output will be padded with '=' so that its length
|
||||
* is a multiple of 8.
|
||||
*/
|
||||
std::string EncodeBase32(const std::string& str, bool pad = true);
|
||||
|
||||
void SplitHostPort(std::string in, int& portOut, std::string& hostOut);
|
||||
std::string i64tostr(int64_t n);
|
||||
std::string itostr(int n);
|
||||
int64_t atoi64(const char* psz);
|
||||
int64_t atoi64(const std::string& str);
|
||||
int atoi(const std::string& str);
|
||||
|
||||
/**
|
||||
* Tests if the given character is a decimal digit.
|
||||
* @param[in] c character to test
|
||||
* @return true if the argument is a decimal digit; otherwise false.
|
||||
*/
|
||||
constexpr bool IsDigit(char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the given character is a whitespace character. The whitespace characters
|
||||
* are: space, form-feed ('\f'), newline ('\n'), carriage return ('\r'), horizontal
|
||||
* tab ('\t'), and vertical tab ('\v').
|
||||
*
|
||||
* This function is locale independent. Under the C locale this function gives the
|
||||
* same result as std::isspace.
|
||||
*
|
||||
* @param[in] c character to test
|
||||
* @return true if the argument is a whitespace character; otherwise false
|
||||
*/
|
||||
constexpr inline bool IsSpace(char c) noexcept {
|
||||
return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v';
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string to signed 32-bit integer with strict parse error feedback.
|
||||
* @returns true if the entire string could be parsed as valid integer,
|
||||
* false if not the entire string could be parsed or when overflow or underflow occurred.
|
||||
*/
|
||||
bool ParseInt32(const std::string& str, int32_t *out);
|
||||
|
||||
/**
|
||||
* Convert string to signed 64-bit integer with strict parse error feedback.
|
||||
* @returns true if the entire string could be parsed as valid integer,
|
||||
* false if not the entire string could be parsed or when overflow or underflow occurred.
|
||||
*/
|
||||
bool ParseInt64(const std::string& str, int64_t *out);
|
||||
|
||||
/**
|
||||
* Convert decimal string to unsigned 8-bit integer with strict parse error feedback.
|
||||
* @returns true if the entire string could be parsed as valid integer,
|
||||
* false if not the entire string could be parsed or when overflow or underflow occurred.
|
||||
*/
|
||||
bool ParseUInt8(const std::string& str, uint8_t *out);
|
||||
|
||||
/**
|
||||
* Convert decimal string to unsigned 32-bit integer with strict parse error feedback.
|
||||
* @returns true if the entire string could be parsed as valid integer,
|
||||
* false if not the entire string could be parsed or when overflow or underflow occurred.
|
||||
*/
|
||||
bool ParseUInt32(const std::string& str, uint32_t *out);
|
||||
|
||||
/**
|
||||
* Convert decimal string to unsigned 64-bit integer with strict parse error feedback.
|
||||
* @returns true if the entire string could be parsed as valid integer,
|
||||
* false if not the entire string could be parsed or when overflow or underflow occurred.
|
||||
*/
|
||||
bool ParseUInt64(const std::string& str, uint64_t *out);
|
||||
|
||||
/**
|
||||
* Convert string to double with strict parse error feedback.
|
||||
* @returns true if the entire string could be parsed as valid double,
|
||||
* false if not the entire string could be parsed or when overflow or underflow occurred.
|
||||
*/
|
||||
bool ParseDouble(const std::string& str, double *out);
|
||||
|
||||
template<typename 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)
|
||||
{
|
||||
unsigned char val = (unsigned char)(*it);
|
||||
if(fSpaces && it != itbegin)
|
||||
rv.push_back(' ');
|
||||
rv.push_back(hexmap[val>>4]);
|
||||
rv.push_back(hexmap[val&15]);
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
std::string HexStr(const Span<const uint8_t> s);
|
||||
inline std::string HexStr(const Span<const char> s) { return HexStr(MakeUCharSpan(s)); }
|
||||
|
||||
/**
|
||||
* Format a paragraph of text to a fixed width, adding spaces for
|
||||
* indentation to any added line.
|
||||
*/
|
||||
std::string FormatParagraph(const std::string& in, size_t width = 79, size_t indent = 0);
|
||||
|
||||
/**
|
||||
* Timing-attack-resistant comparison.
|
||||
* Takes time proportional to length
|
||||
* of first argument.
|
||||
*/
|
||||
template <typename T>
|
||||
bool TimingResistantEqual(const T& a, const T& b)
|
||||
{
|
||||
if (b.size() == 0) return a.size() == 0;
|
||||
size_t accumulator = a.size() ^ b.size();
|
||||
for (size_t i = 0; i < a.size(); i++)
|
||||
accumulator |= a[i] ^ b[i%b.size()];
|
||||
return accumulator == 0;
|
||||
}
|
||||
|
||||
/** Parse number as fixed point according to JSON number syntax.
|
||||
* See http://json.org/number.gif
|
||||
* @returns true on success, false on error.
|
||||
* @note The result must be in the range (-10^18,10^18), otherwise an overflow error will trigger.
|
||||
*/
|
||||
bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out);
|
||||
|
||||
/** Convert from one power-of-2 number base to another. */
|
||||
template<int frombits, int tobits, bool pad, typename O, typename I>
|
||||
bool ConvertBits(const O& outfn, I it, I end) {
|
||||
size_t acc = 0;
|
||||
size_t bits = 0;
|
||||
constexpr size_t maxv = (1 << tobits) - 1;
|
||||
constexpr size_t max_acc = (1 << (frombits + tobits - 1)) - 1;
|
||||
while (it != end) {
|
||||
acc = ((acc << frombits) | *it) & max_acc;
|
||||
bits += frombits;
|
||||
while (bits >= tobits) {
|
||||
bits -= tobits;
|
||||
outfn((acc >> bits) & maxv);
|
||||
}
|
||||
++it;
|
||||
}
|
||||
if (pad) {
|
||||
if (bits) outfn((acc << (tobits - bits)) & maxv);
|
||||
} else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given character to its lowercase equivalent.
|
||||
* This function is locale independent. It only converts uppercase
|
||||
* characters in the standard 7-bit ASCII range.
|
||||
* This is a feature, not a limitation.
|
||||
*
|
||||
* @param[in] c the character to convert to lowercase.
|
||||
* @return the lowercase equivalent of c; or the argument
|
||||
* if no conversion is possible.
|
||||
*/
|
||||
constexpr char ToLower(char c)
|
||||
{
|
||||
return (c >= 'A' && c <= 'Z' ? (c - 'A') + 'a' : c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the lowercase equivalent of the given string.
|
||||
* This function is locale independent. It only converts uppercase
|
||||
* characters in the standard 7-bit ASCII range.
|
||||
* This is a feature, not a limitation.
|
||||
*
|
||||
* @param[in] str the string to convert to lowercase.
|
||||
* @returns lowercased equivalent of str
|
||||
*/
|
||||
std::string ToLower(const std::string& str);
|
||||
|
||||
/**
|
||||
* Converts the given character to its uppercase equivalent.
|
||||
* This function is locale independent. It only converts lowercase
|
||||
* characters in the standard 7-bit ASCII range.
|
||||
* This is a feature, not a limitation.
|
||||
*
|
||||
* @param[in] c the character to convert to uppercase.
|
||||
* @return the uppercase equivalent of c; or the argument
|
||||
* if no conversion is possible.
|
||||
*/
|
||||
constexpr char ToUpper(char c)
|
||||
{
|
||||
return (c >= 'a' && c <= 'z' ? (c - 'a') + 'A' : c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the uppercase equivalent of the given string.
|
||||
* This function is locale independent. It only converts lowercase
|
||||
* characters in the standard 7-bit ASCII range.
|
||||
* This is a feature, not a limitation.
|
||||
*
|
||||
* @param[in] str the string to convert to uppercase.
|
||||
* @returns UPPERCASED EQUIVALENT OF str
|
||||
*/
|
||||
std::string ToUpper(const std::string& str);
|
||||
|
||||
/**
|
||||
* Capitalizes the first character of the given string.
|
||||
* This function is locale independent. It only converts lowercase
|
||||
* characters in the standard 7-bit ASCII range.
|
||||
* This is a feature, not a limitation.
|
||||
*
|
||||
* @param[in] str the string to capitalize.
|
||||
* @returns string with the first letter capitalized.
|
||||
*/
|
||||
std::string Capitalize(std::string str);
|
||||
|
||||
#endif // BITCOIN_UTIL_STRENCODINGS_H
|
||||
6
src/util/string.cpp
Normal file
6
src/util/string.cpp
Normal file
@@ -0,0 +1,6 @@
|
||||
// Copyright (c) 2019 The Bitcoin Core developers
|
||||
// Copyright (c) 2016-2022 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
#include "util/string.h"
|
||||
99
src/util/string.h
Normal file
99
src/util/string.h
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2019-2020 The Bitcoin Core developers
|
||||
// Copyright (c) 2016-2022 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
#ifndef BITCOIN_UTIL_STRING_H
|
||||
#define BITCOIN_UTIL_STRING_H
|
||||
|
||||
#include "attributes.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
[[nodiscard]] inline std::string TrimString(const std::string& str, const std::string& pattern = " \f\n\r\t\v")
|
||||
{
|
||||
std::string::size_type front = str.find_first_not_of(pattern);
|
||||
if (front == std::string::npos) {
|
||||
return std::string();
|
||||
}
|
||||
std::string::size_type end = str.find_last_not_of(pattern);
|
||||
return str.substr(front, end - front + 1);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline std::string RemovePrefix(const std::string& str, const std::string& prefix)
|
||||
{
|
||||
if (str.substr(0, prefix.size()) == prefix) {
|
||||
return str.substr(prefix.size());
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join a list of items
|
||||
*
|
||||
* @param list The list to join
|
||||
* @param separator The separator
|
||||
* @param unary_op Apply this operator to each item in the list
|
||||
*/
|
||||
template <typename T, typename BaseType, typename UnaryOp>
|
||||
auto Join(const std::vector<T>& list, const BaseType& separator, UnaryOp unary_op)
|
||||
-> decltype(unary_op(list.at(0)))
|
||||
{
|
||||
decltype(unary_op(list.at(0))) ret;
|
||||
for (size_t i = 0; i < list.size(); ++i) {
|
||||
if (i > 0) ret += separator;
|
||||
ret += unary_op(list.at(i));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T Join(const std::vector<T>& list, const T& separator)
|
||||
{
|
||||
return Join(list, separator, [](const T& i) { return i; });
|
||||
}
|
||||
|
||||
// Explicit overload needed for c_str arguments, which would otherwise cause a substitution failure in the template above.
|
||||
inline std::string Join(const std::vector<std::string>& list, const std::string& separator)
|
||||
{
|
||||
return Join<std::string>(list, separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string does not contain any embedded NUL (\0) characters
|
||||
*/
|
||||
[[nodiscard]] inline bool ValidAsCString(const std::string& str) noexcept
|
||||
{
|
||||
return str.size() == strlen(str.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* Locale-independent version of std::to_string
|
||||
*/
|
||||
template <typename T>
|
||||
std::string ToString(const T& t)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss.imbue(std::locale::classic());
|
||||
oss << t;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a container begins with the given prefix.
|
||||
*/
|
||||
template <typename T1, size_t PREFIX_LEN>
|
||||
[[nodiscard]] inline bool HasPrefix(const T1& obj,
|
||||
const std::array<uint8_t, PREFIX_LEN>& prefix)
|
||||
{
|
||||
return obj.size() >= PREFIX_LEN &&
|
||||
std::equal(std::begin(prefix), std::end(prefix), std::begin(obj));
|
||||
}
|
||||
|
||||
#endif // BITCOIN_UTIL_STRENCODINGS_H
|
||||
Reference in New Issue
Block a user