Add from and to hex string conversions
This commit is contained in:
103
src/strutils.cpp
103
src/strutils.cpp
@@ -2,8 +2,82 @@
|
|||||||
|
|
||||||
// system includes
|
// system includes
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
// 3rdparty lib includes
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
namespace cpputils {
|
namespace cpputils {
|
||||||
|
namespace {
|
||||||
|
tl::expected<char *, std::string> sodium_bin2hex(char * const hex, const size_t hex_maxlen,
|
||||||
|
const unsigned char * const bin, const size_t bin_len)
|
||||||
|
__attribute__ ((nonnull(1)));
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::string toHexString(std::basic_string_view<unsigned char> buf)
|
||||||
|
{
|
||||||
|
std::string hex(buf.size() * 2 + 1, {});
|
||||||
|
assert(hex.size() == buf.size() * 2 + 1);
|
||||||
|
|
||||||
|
if (const auto result = sodium_bin2hex(hex.data(), hex.size(), buf.data(), buf.size()); !result)
|
||||||
|
{
|
||||||
|
// auto msg = fmt::format("sodium_bin2hex() failed with {}", result.error());
|
||||||
|
// ESP_LOGW(TAG, "%.*s", msg.size(), msg.data());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
hex.resize(hex.size() - 1);
|
||||||
|
assert(hex.size() == buf.size() * 2);
|
||||||
|
|
||||||
|
return hex;
|
||||||
|
}
|
||||||
|
|
||||||
|
tl::expected<std::basic_string<unsigned char>, std::string> fromHexString(std::string_view hex)
|
||||||
|
{
|
||||||
|
if (hex.size() % 2 != 0)
|
||||||
|
return tl::make_unexpected("hex length not even");
|
||||||
|
|
||||||
|
std::basic_string<unsigned char> result;
|
||||||
|
result.reserve(hex.size() / 2);
|
||||||
|
|
||||||
|
for (auto iter = std::cbegin(hex); iter != std::cend(hex); )
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
unsigned char c;
|
||||||
|
struct {
|
||||||
|
unsigned char nibble1:4;
|
||||||
|
unsigned char nibble0:4;
|
||||||
|
} nibbles;
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (const auto c = *iter)
|
||||||
|
{
|
||||||
|
case '0'...'9': nibbles.nibble0 = c - '0'; break;
|
||||||
|
case 'A'...'F': nibbles.nibble0 = c - 'A' + 10; break;
|
||||||
|
case 'a'...'f': nibbles.nibble0 = c - 'a' + 10; break;
|
||||||
|
default:
|
||||||
|
return tl::make_unexpected(fmt::format("invalid character {} at pos {}", c, std::distance(std::begin(hex), iter)));
|
||||||
|
}
|
||||||
|
|
||||||
|
iter++;
|
||||||
|
|
||||||
|
switch (const auto c = *iter)
|
||||||
|
{
|
||||||
|
case '0'...'9': nibbles.nibble1 = c - '0'; break;
|
||||||
|
case 'A'...'F': nibbles.nibble1 = c - 'A' + 10; break;
|
||||||
|
case 'a'...'f': nibbles.nibble1 = c - 'a' + 10; break;
|
||||||
|
default:
|
||||||
|
return tl::make_unexpected(fmt::format("invalid character {} at pos {}", c, std::distance(std::begin(hex), iter)));
|
||||||
|
}
|
||||||
|
|
||||||
|
iter++;
|
||||||
|
|
||||||
|
result.push_back(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool stringEqualsIgnoreCase(std::string_view a, std::string_view b)
|
bool stringEqualsIgnoreCase(std::string_view a, std::string_view b)
|
||||||
{
|
{
|
||||||
if (a.size() != b.size())
|
if (a.size() != b.size())
|
||||||
@@ -118,4 +192,33 @@ std::optional<std::string_view> getStringBetween(std::string_view search, std::s
|
|||||||
return restView.substr(0, endIndex);
|
return restView.substr(0, endIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
/* Derived from original code by CodesInChaos */
|
||||||
|
tl::expected<char *, std::string>
|
||||||
|
sodium_bin2hex(char *const hex, const size_t hex_maxlen,
|
||||||
|
const unsigned char *const bin, const size_t bin_len)
|
||||||
|
{
|
||||||
|
size_t i = (size_t) 0U;
|
||||||
|
unsigned int x;
|
||||||
|
int b;
|
||||||
|
int c;
|
||||||
|
|
||||||
|
if (bin_len >= SIZE_MAX / 2 || hex_maxlen <= bin_len * 2U) {
|
||||||
|
return tl::make_unexpected("misuse because bin_len >= SIZE_MAX / 2 || hex_maxlen <= bin_len * 2U");
|
||||||
|
}
|
||||||
|
while (i < bin_len) {
|
||||||
|
c = bin[i] & 0xf;
|
||||||
|
b = bin[i] >> 4;
|
||||||
|
x = (unsigned char) (87U + c + (((c - 10U) >> 8) & ~38U)) << 8 |
|
||||||
|
(unsigned char) (87U + b + (((b - 10U) >> 8) & ~38U));
|
||||||
|
hex[i * 2U] = (char) x;
|
||||||
|
x >>= 8;
|
||||||
|
hex[i * 2U + 1U] = (char) x;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
hex[i * 2U] = 0U;
|
||||||
|
|
||||||
|
return hex;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
} // namespace cpputils
|
} // namespace cpputils
|
||||||
|
@@ -7,6 +7,9 @@
|
|||||||
#include <random>
|
#include <random>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
// 3rdparty lib includes
|
||||||
|
#include <tl/expected.hpp>
|
||||||
|
|
||||||
namespace cpputils {
|
namespace cpputils {
|
||||||
inline std::string toString(bool val) { return val ? "true" : "false"; }
|
inline std::string toString(bool val) { return val ? "true" : "false"; }
|
||||||
inline std::string toString(int8_t val) { return std::to_string(val); }
|
inline std::string toString(int8_t val) { return std::to_string(val); }
|
||||||
@@ -33,6 +36,14 @@ inline std::string toString(std::optional<float> val) { if (val) return toString
|
|||||||
inline std::string toString(std::optional<double> val) { if (val) return toString(*val); else return {}; }
|
inline std::string toString(std::optional<double> val) { if (val) return toString(*val); else return {}; }
|
||||||
inline std::string toString(const std::optional<std::string> &val) { if (val) return toString(*val); else return {}; }
|
inline std::string toString(const std::optional<std::string> &val) { if (val) return toString(*val); else return {}; }
|
||||||
|
|
||||||
|
std::string toHexString(std::basic_string_view<unsigned char> buf);
|
||||||
|
inline std::string toHexString(std::string_view str)
|
||||||
|
{
|
||||||
|
return toHexString(std::basic_string_view<unsigned char>{reinterpret_cast<const unsigned char *>(str.data()), str.size()});
|
||||||
|
}
|
||||||
|
|
||||||
|
tl::expected<std::basic_string<unsigned char>, std::string> fromHexString(std::string_view hex);
|
||||||
|
|
||||||
bool stringEqualsIgnoreCase(std::string_view a, std::string_view b);
|
bool stringEqualsIgnoreCase(std::string_view a, std::string_view b);
|
||||||
|
|
||||||
//void stringReplaceAll(char search, char replace, std::string &subject);
|
//void stringReplaceAll(char search, char replace, std::string &subject);
|
||||||
|
Reference in New Issue
Block a user