From 4a8f96f95da51e6b570a00ddcfefe100844d8f8b Mon Sep 17 00:00:00 2001 From: Lee Clagett Date: Mon, 27 Feb 2017 13:33:16 -0500 Subject: Improvements for epee binary to hex functions: - Performance improvements - Added `span` for zero-copy pointer+length arguments - Added `std::ostream` overload for direct writing to output buffers - Removal of unused `string_tools::buff_to_hex` --- tests/unit_tests/CMakeLists.txt | 1 + tests/unit_tests/crypto.cpp | 75 +++++++++ tests/unit_tests/epee_utils.cpp | 332 ++++++++++++++++++++++++++++++++++++- tests/unit_tests/ringct.cpp | 11 ++ tests/unit_tests/serialization.cpp | 4 - 5 files changed, 415 insertions(+), 8 deletions(-) create mode 100644 tests/unit_tests/crypto.cpp (limited to 'tests/unit_tests') diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 3c0e8067e..da95a97a9 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -36,6 +36,7 @@ set(unit_tests_sources chacha8.cpp checkpoints.cpp command_line.cpp + crypto.cpp decompose_amount_into_digits.cpp dns_resolver.cpp epee_boosted_tcp_server.cpp diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp new file mode 100644 index 000000000..9febb7709 --- /dev/null +++ b/tests/unit_tests/crypto.cpp @@ -0,0 +1,75 @@ +// Copyright (c) 2014-2017, 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 +#include +#include +#include +#include + +#include "cryptonote_basic/cryptonote_basic_impl.h" + +namespace +{ + static constexpr const std::uint8_t source[] = { + 0x8b, 0x65, 0x59, 0x70, 0x15, 0x37, 0x99, 0xaf, 0x2a, 0xea, 0xdc, 0x9f, 0xf1, 0xad, 0xd0, 0xea, + 0x6c, 0x72, 0x51, 0xd5, 0x41, 0x54, 0xcf, 0xa9, 0x2c, 0x17, 0x3a, 0x0d, 0xd3, 0x9c, 0x1f, 0x94, + 0x6c, 0x72, 0x51, 0xd5, 0x41, 0x54, 0xcf, 0xa9, 0x2c, 0x17, 0x3a, 0x0d, 0xd3, 0x9c, 0x1f, 0x94, + 0x8b, 0x65, 0x59, 0x70, 0x15, 0x37, 0x99, 0xaf, 0x2a, 0xea, 0xdc, 0x9f, 0xf1, 0xad, 0xd0, 0xea + }; + + static constexpr const char expected[] = + "8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94" + "6c7251d54154cfa92c173a0dd39c1f948b655970153799af2aeadc9ff1add0ea"; + + template + bool is_formatted() + { + T value{}; + + static_assert(alignof(T) == 1, "T must have 1 byte alignment"); + static_assert(sizeof(T) <= sizeof(source), "T is too large for source"); + static_assert(sizeof(T) * 2 <= sizeof(expected), "T is too large for destination"); + std::memcpy(std::addressof(value), source, sizeof(T)); + + std::stringstream out; + out << "BEGIN" << value << "END"; + return out.str() == "BEGIN<" + std::string{expected, sizeof(T) * 2} + ">END"; + } +} + +TEST(Crypto, Ostream) +{ + EXPECT_TRUE(is_formatted()); + EXPECT_TRUE(is_formatted()); + EXPECT_TRUE(is_formatted()); + EXPECT_TRUE(is_formatted()); + EXPECT_TRUE(is_formatted()); + EXPECT_TRUE(is_formatted()); + EXPECT_TRUE(is_formatted()); +} diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp index 25b2bddd9..e8ddbe3f5 100644 --- a/tests/unit_tests/epee_utils.cpp +++ b/tests/unit_tests/epee_utils.cpp @@ -27,18 +27,342 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +#include +#include +#include +#include +#include +#include +#include +#include + #ifdef _WIN32 # include #else # include #endif -#include -#include -#include - +#include "hex.h" +#include "span.h" #include "string_tools.h" +namespace +{ + template + bool can_construct() + { + const unsigned count = + unsigned(std::is_constructible()) + + unsigned(std::is_constructible()) + + unsigned(std::is_convertible()) + + unsigned(std::is_convertible()) + + unsigned(std::is_assignable()) + + unsigned(std::is_assignable()); + EXPECT_TRUE(count == 6 || count == 0) << + "Mismatch on construction results - " << count << " were true"; + return count == 6; + } + + // This is probably stressing the compiler more than the implementation ... + constexpr const epee::span test_string("a string"); + static_assert(!test_string.empty(), "test failure"); + static_assert(test_string.size() == 9, "test failure"); + static_assert(test_string.size_bytes() == 9, "test_failure"); + static_assert(test_string.begin() == test_string.cbegin(), "test failure"); + static_assert(test_string.end() == test_string.cend(), "test failure"); + static_assert(test_string.cend() - test_string.cbegin() == 9, "test failure"); + static_assert(*test_string.cbegin() == 'a', "test failure"); + static_assert(*(test_string.cend() - 2) == 'g', "test failure"); + static_assert( + epee::span(test_string).cbegin() + 3 == test_string.cbegin() + 3, + "test failure" + ); + + static_assert(epee::span().empty(), "test failure"); + static_assert(epee::span(nullptr).empty(), "test failure"); + static_assert(epee::span("foo", 2).size() == 2, "test failure"); + + std::string std_to_hex(const std::vector& source) + { + std::stringstream out; + out << std::hex; + for (const unsigned char byte : source) + { + out << std::setw(2) << std::setfill('0') << int(byte); + } + return out.str(); + } + + std::vector get_all_bytes() + { + std::vector out; + out.resize(256); + boost::range::iota(out, 0); + return out; + } +} + +TEST(Span, Traits) +{ + EXPECT_TRUE((std::is_same::size_type>())); + EXPECT_TRUE((std::is_same::difference_type>())); + EXPECT_TRUE((std::is_same::value_type>())); + EXPECT_TRUE((std::is_same::pointer>())); + EXPECT_TRUE((std::is_same::const_pointer>())); + EXPECT_TRUE((std::is_same::iterator>())); + EXPECT_TRUE((std::is_same::const_iterator>())); + EXPECT_TRUE((std::is_same::reference>())); + EXPECT_TRUE((std::is_same::const_reference>())); + + EXPECT_TRUE((std::is_same::size_type>())); + EXPECT_TRUE((std::is_same::difference_type>())); + EXPECT_TRUE((std::is_same::value_type>())); + EXPECT_TRUE((std::is_same::pointer>())); + EXPECT_TRUE((std::is_same::const_pointer>())); + EXPECT_TRUE((std::is_same::iterator>())); + EXPECT_TRUE((std::is_same::const_iterator>())); + EXPECT_TRUE((std::is_same::reference>())); + EXPECT_TRUE((std::is_same::const_reference>())); +} + +TEST(Span, MutableConstruction) +{ + struct no_conversion{}; + + EXPECT_TRUE(std::is_constructible>()); + EXPECT_TRUE((std::is_constructible, char*, std::size_t>())); + EXPECT_FALSE((std::is_constructible, const char*, std::size_t>())); + EXPECT_FALSE((std::is_constructible, unsigned char*, std::size_t>())); + + EXPECT_TRUE((can_construct, std::nullptr_t>())); + EXPECT_TRUE((can_construct, char(&)[1]>())); + + EXPECT_FALSE((can_construct, std::vector>())); + EXPECT_FALSE((can_construct, std::array>())); + + EXPECT_FALSE((can_construct, std::wstring>())); + EXPECT_FALSE((can_construct, const std::vector>())); + EXPECT_FALSE((can_construct, std::vector>())); + EXPECT_FALSE((can_construct, const std::array>())); + EXPECT_FALSE((can_construct, std::array>())); + EXPECT_FALSE((can_construct, const char[1]>())); + EXPECT_FALSE((can_construct, unsigned char[1]>())); + EXPECT_FALSE((can_construct, epee::span>())); + EXPECT_FALSE((can_construct, epee::span>())); + EXPECT_FALSE((can_construct, no_conversion>())); +} + +TEST(Span, ImmutableConstruction) +{ + struct no_conversion{}; + + EXPECT_TRUE(std::is_constructible>()); + EXPECT_TRUE((std::is_constructible, char*, std::size_t>())); + EXPECT_TRUE((std::is_constructible, const char*, std::size_t>())); + EXPECT_FALSE((std::is_constructible, unsigned char*, std::size_t>())); + + EXPECT_FALSE((can_construct, std::string>())); + EXPECT_FALSE((can_construct, std::vector>())); + EXPECT_FALSE((can_construct, const std::vector>())); + EXPECT_FALSE((can_construct, std::array>())); + EXPECT_FALSE((can_construct, const std::array>())); + + EXPECT_TRUE((can_construct, std::nullptr_t>())); + EXPECT_TRUE((can_construct, char[1]>())); + EXPECT_TRUE((can_construct, const char[1]>())); + EXPECT_TRUE((can_construct, epee::span>())); + + EXPECT_FALSE((can_construct, std::wstring>())); + EXPECT_FALSE((can_construct, std::vector>())); + EXPECT_FALSE((can_construct, std::array>())); + EXPECT_FALSE((can_construct, unsigned char[1]>())); + EXPECT_FALSE((can_construct, epee::span>())); + EXPECT_FALSE((can_construct, no_conversion>())); +} + +TEST(Span, NoExcept) +{ + EXPECT_TRUE(std::is_nothrow_default_constructible>()); + EXPECT_TRUE(std::is_nothrow_move_constructible>()); + EXPECT_TRUE(std::is_nothrow_copy_constructible>()); + EXPECT_TRUE(std::is_move_assignable>()); + EXPECT_TRUE(std::is_copy_assignable>()); + + char data[10]; + epee::span lvalue(data); + const epee::span clvalue(data); + EXPECT_TRUE(noexcept(epee::span())); + EXPECT_TRUE(noexcept(epee::span(nullptr))); + EXPECT_TRUE(noexcept(epee::span(nullptr, 0))); + EXPECT_TRUE(noexcept(epee::span(data))); + EXPECT_TRUE(noexcept(epee::span(lvalue))); + EXPECT_TRUE(noexcept(epee::span(clvalue))); + + // conversion from mutable to immutable not yet implemented + // EXPECT_TRUE(noexcept(epee::span(lvalue))); + // EXPECT_TRUE(noexcept(epee::span(clvalue))); + + EXPECT_TRUE(noexcept(epee::span(epee::span(lvalue)))); + EXPECT_TRUE(noexcept(lvalue = lvalue)); + EXPECT_TRUE(noexcept(lvalue = clvalue)); + EXPECT_TRUE(noexcept(lvalue = epee::span(lvalue))); +} + +TEST(Span, Nullptr) +{ + const auto check_empty = [](epee::span data) + { + EXPECT_TRUE(data.empty()); + EXPECT_EQ(data.cbegin(), data.begin()); + EXPECT_EQ(data.cend(), data.end()); + EXPECT_EQ(data.cend(), data.cbegin()); + EXPECT_EQ(0, data.size()); + EXPECT_EQ(0, data.size_bytes()); + }; + check_empty({}); + check_empty(nullptr); +} + +TEST(Span, Writing) +{ + const int expected[] = {-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector source; + + epee::span span; + EXPECT_TRUE(span.empty()); + EXPECT_EQ(0, span.size()); + EXPECT_EQ(0, span.size_bytes()); + + source.resize(15); + span = {source.data(), source.size()}; + EXPECT_FALSE(span.empty()); + EXPECT_EQ(15, span.size()); + EXPECT_EQ(15 * 4, span.size_bytes()); + + boost::range::iota(span, -5); + EXPECT_EQ(span.begin(), span.cbegin()); + EXPECT_EQ(span.end(), span.cend()); + EXPECT_TRUE(boost::range::equal(expected, source)); + EXPECT_TRUE(boost::range::equal(expected, span)); +} + +TEST(Span, ToByteSpan) +{ + const char expected[] = {56, 44, 11, 5}; + EXPECT_TRUE( + boost::range::equal( + std::array{{56, 44, 11, 5}}, + epee::to_byte_span(expected) + ) + ); + EXPECT_TRUE( + boost::range::equal( + std::array{{56, 44, 11, 5}}, + epee::to_byte_span(epee::span{expected}) + ) + ); +} + +TEST(Span, AsByteSpan) +{ + struct some_pod { char value[4]; }; + const some_pod immutable {{ 5, 10, 12, 127 }}; + EXPECT_TRUE( + boost::range::equal( + std::array{{5, 10, 12, 127}}, + epee::as_byte_span(immutable) + ) + ); + EXPECT_TRUE( + boost::range::equal( + std::array{{'a', 'y', 0x00}}, epee::as_byte_span("ay") + ) + ); +} + +TEST(ToHex, String) +{ + EXPECT_TRUE(epee::to_hex::string(nullptr).empty()); + EXPECT_EQ( + std::string{"ffab0100"}, + epee::to_hex::string(epee::as_byte_span("\xff\xab\x01")) + ); + + const std::vector all_bytes = get_all_bytes(); + EXPECT_EQ( + std_to_hex(all_bytes), epee::to_hex::string(epee::to_span(all_bytes)) + ); +} + +TEST(ToHex, Array) +{ + EXPECT_EQ( + (std::array{{'f', 'f', 'a', 'b', '0', '1', '0', '0'}}), + (epee::to_hex::array(std::array{{0xFF, 0xAB, 0x01, 0x00}})) + ); +} + +TEST(ToHex, Ostream) +{ + std::stringstream out; + epee::to_hex::buffer(out, nullptr); + EXPECT_TRUE(out.str().empty()); + + { + const std::uint8_t source[] = {0xff, 0xab, 0x01, 0x00}; + epee::to_hex::buffer(out, source); + } + + std::string expected{"ffab0100"}; + EXPECT_EQ(expected, out.str()); + + const std::vector all_bytes = get_all_bytes(); + + expected.append(std_to_hex(all_bytes)); + epee::to_hex::buffer(out, epee::to_span(all_bytes)); + EXPECT_EQ(expected, out.str()); +} + +TEST(ToHex, Formatted) +{ + std::stringstream out; + std::string expected{"<>"}; + + epee::to_hex::formatted(out, nullptr); + EXPECT_EQ(expected, out.str()); + + expected.append(""); + epee::to_hex::formatted(out, epee::as_byte_span("\xFF\xAB\x01")); + EXPECT_EQ(expected, out.str()); + + const std::vector all_bytes = get_all_bytes(); + + expected.append("<").append(std_to_hex(all_bytes)).append(">"); + epee::to_hex::formatted(out, epee::to_span(all_bytes)); + EXPECT_EQ(expected, out.str()); +} + +TEST(StringTools, BuffToHex) +{ + const std::vector all_bytes = get_all_bytes(); + + EXPECT_EQ( + std_to_hex(all_bytes), + (epee::string_tools::buff_to_hex_nodelimer( + std::string{reinterpret_cast(all_bytes.data()), all_bytes.size()} + )) + ); +} + +TEST(StringTools, PodToHex) +{ + struct some_pod { unsigned char data[4]; }; + EXPECT_EQ( + std::string{"ffab0100"}, + (epee::string_tools::pod_to_hex(some_pod{{0xFF, 0xAB, 0x01, 0x00}})) + ); +} + TEST(StringTools, GetIpString) { EXPECT_EQ( diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index 0e2cb3903..af6afa636 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -32,6 +32,7 @@ #include #include +#include #include "ringct/rctTypes.h" #include "ringct/rctSigs.h" @@ -1048,3 +1049,13 @@ TEST(ringct, reject_gen_non_simple_ver_simple) rct::rctSig sig = make_sample_rct_sig(NELTS(inputs), inputs, NELTS(outputs), outputs, true); ASSERT_FALSE(rct::verRctSimple(sig)); } + +TEST(ringct, key_ostream) +{ + std::stringstream out; + out << "BEGIN" << rct::H << "END"; + EXPECT_EQ( + std::string{"BEGIN<8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94>END"}, + out.str() + ); +} diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 7afc0d60c..6f9fe7d11 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -1000,8 +1000,6 @@ TEST(Serialization, portability_unsigned_tx) ASSERT_TRUE(tcd.selected_transfers.front() == 2); // tcd.extra ASSERT_TRUE(tcd.extra.size() == 68); - string tcd_extra_str = epee::string_tools::buff_to_hex(string(reinterpret_cast(tcd.extra.data()), tcd.extra.size())); - ASSERT_TRUE(tcd_extra_str == "0x2 0x21 0x0 0xf8 0xd 0xbc 0xfc 0xa2 0x2d 0x84 0x1e 0xa0 0x46 0x18 0x7a 0x5b 0x19 0xea 0x4d 0xd1 0xa2 0x8a 0x58 0xa8 0x72 0x9 0xd5 0xdf 0x2 0x30 0x60 0xac 0x9e 0x48 0x84 0x1 0xb2 0xfd 0x5d 0x4e 0x45 0x8b 0xf1 0x28 0xa0 0xc8 0x30 0xd1 0x35 0x4f 0x47 0xb9 0xed 0xc9 0x82 0x8c 0x83 0x37 0x7d 0xb6 0xb5 0xe5 0x3d 0xff 0x64 0xb0 0xde 0x7f "); // tcd.{unlock_time, use_rct} ASSERT_TRUE(tcd.unlock_time == 0); ASSERT_TRUE(tcd.use_rct); @@ -1157,8 +1155,6 @@ TEST(Serialization, portability_signed_tx) ASSERT_TRUE(tcd.selected_transfers.front() == 2); // ptx.construction_data.extra ASSERT_TRUE(tcd.extra.size() == 68); - string tcd_extra_str = epee::string_tools::buff_to_hex(string(reinterpret_cast(tcd.extra.data()), tcd.extra.size())); - ASSERT_TRUE(tcd_extra_str == "0x2 0x21 0x0 0xf8 0xd 0xbc 0xfc 0xa2 0x2d 0x84 0x1e 0xa0 0x46 0x18 0x7a 0x5b 0x19 0xea 0x4d 0xd1 0xa2 0x8a 0x58 0xa8 0x72 0x9 0xd5 0xdf 0x2 0x30 0x60 0xac 0x9e 0x48 0x84 0x1 0xb2 0xfd 0x5d 0x4e 0x45 0x8b 0xf1 0x28 0xa0 0xc8 0x30 0xd1 0x35 0x4f 0x47 0xb9 0xed 0xc9 0x82 0x8c 0x83 0x37 0x7d 0xb6 0xb5 0xe5 0x3d 0xff 0x64 0xb0 0xde 0x7f "); // ptx.construction_data.{unlock_time, use_rct} ASSERT_TRUE(tcd.unlock_time == 0); ASSERT_TRUE(tcd.use_rct); -- cgit v1.2.3