mirror of
https://github.com/monero-project/monero.git
synced 2024-12-28 03:59:24 -05:00
150 lines
4.8 KiB
C++
150 lines
4.8 KiB
C++
// Copyright (c) 2017-2019, The Monero Project
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification, are
|
|
// permitted provided that the following conditions are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
// conditions and the following disclaimer.
|
|
//
|
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
// of conditions and the following disclaimer in the documentation and/or other
|
|
// materials provided with the distribution.
|
|
//
|
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
// used to endorse or promote products derived from this software without specific
|
|
// prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
#include "hex.h"
|
|
|
|
#include <iterator>
|
|
#include <limits>
|
|
#include <ostream>
|
|
#include <stdexcept>
|
|
|
|
namespace epee
|
|
{
|
|
namespace
|
|
{
|
|
template<typename T>
|
|
void write_hex(T&& out, const span<const std::uint8_t> src)
|
|
{
|
|
static constexpr const char hex[] = u8"0123456789abcdef";
|
|
static_assert(sizeof(hex) == 17, "bad string size");
|
|
for (const std::uint8_t byte : src)
|
|
{
|
|
*out = hex[byte >> 4];
|
|
++out;
|
|
*out = hex[byte & 0x0F];
|
|
++out;
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
T to_hex::convert(const span<const std::uint8_t> src)
|
|
{
|
|
if (std::numeric_limits<std::size_t>::max() / 2 < src.size())
|
|
throw std::range_error("hex_view::to_string exceeded maximum size");
|
|
|
|
T out{};
|
|
out.resize(src.size() * 2);
|
|
to_hex::buffer_unchecked((char*)out.data(), src); // can't see the non const version in wipeable_string??
|
|
return out;
|
|
}
|
|
|
|
std::string to_hex::string(const span<const std::uint8_t> src) { return convert<std::string>(src); }
|
|
epee::wipeable_string to_hex::wipeable_string(const span<const std::uint8_t> src) { return convert<epee::wipeable_string>(src); }
|
|
|
|
void to_hex::buffer(std::ostream& out, const span<const std::uint8_t> src)
|
|
{
|
|
write_hex(std::ostreambuf_iterator<char>{out}, src);
|
|
}
|
|
|
|
void to_hex::formatted(std::ostream& out, const span<const std::uint8_t> src)
|
|
{
|
|
out.put('<');
|
|
buffer(out, src);
|
|
out.put('>');
|
|
}
|
|
|
|
void to_hex::buffer_unchecked(char* out, const span<const std::uint8_t> src) noexcept
|
|
{
|
|
return write_hex(out, src);
|
|
}
|
|
|
|
std::vector<uint8_t> from_hex::vector(boost::string_ref src)
|
|
{
|
|
// should we include a specific character
|
|
auto include = [](char input) {
|
|
// we ignore spaces and colons
|
|
return !std::isspace(input) && input != ':';
|
|
};
|
|
|
|
// the number of relevant characters to decode
|
|
auto count = std::count_if(src.begin(), src.end(), include);
|
|
|
|
// this must be a multiple of two, otherwise we have a truncated input
|
|
if (count % 2) {
|
|
throw std::length_error{ "Invalid hexadecimal input length" };
|
|
}
|
|
|
|
std::vector<uint8_t> result;
|
|
result.reserve(count / 2);
|
|
|
|
// the data to work with (std::string is always null-terminated)
|
|
auto data = src.data();
|
|
|
|
// convert a single hex character to an unsigned integer
|
|
auto char_to_int = [](const char *input) {
|
|
switch (std::tolower(*input)) {
|
|
case '0': return 0;
|
|
case '1': return 1;
|
|
case '2': return 2;
|
|
case '3': return 3;
|
|
case '4': return 4;
|
|
case '5': return 5;
|
|
case '6': return 6;
|
|
case '7': return 7;
|
|
case '8': return 8;
|
|
case '9': return 9;
|
|
case 'a': return 10;
|
|
case 'b': return 11;
|
|
case 'c': return 12;
|
|
case 'd': return 13;
|
|
case 'e': return 14;
|
|
case 'f': return 15;
|
|
default: throw std::range_error{ "Invalid hexadecimal input" };
|
|
}
|
|
};
|
|
|
|
// keep going until we reach the end
|
|
while (data[0] != '\0') {
|
|
// skip unwanted characters
|
|
if (!include(data[0])) {
|
|
++data;
|
|
continue;
|
|
}
|
|
|
|
// convert two matching characters to int
|
|
auto high = char_to_int(data++);
|
|
auto low = char_to_int(data++);
|
|
|
|
result.push_back(high << 4 | low);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|