diff --git a/src/crypto/crypto-sugar.h b/src/crypto/crypto-sugar.h index 8fe7890a..9dad0b4e 100644 --- a/src/crypto/crypto-sugar.h +++ b/src/crypto/crypto-sugar.h @@ -51,38 +51,42 @@ namespace crypto } - template - std::string pod_to_hex_reversed(const pod_t &h) + inline std::string buff_to_hex(const void* pdata, size_t len, bool reversed = false) { constexpr char hexmap[] = "0123456789abcdef"; - const unsigned char* data = reinterpret_cast(&h); - size_t len = sizeof h; + const unsigned char* data = reinterpret_cast(pdata); std::string s(len * 2, ' '); - for (size_t i = 0; i < len; ++i) + if (!reversed) { - s[2 * i] = hexmap[data[len - 1 - i] >> 4]; - s[2 * i + 1] = hexmap[data[len - 1 - i] & 0x0F]; + for (size_t i = 0; i < len; ++i) + { + s[2 * i] = hexmap[data[i] >> 4]; + s[2 * i + 1] = hexmap[data[i] & 0x0F]; + } + } + else + { + for (size_t i = 0; i < len; ++i) + { + s[2 * i] = hexmap[data[len - 1 - i] >> 4]; + s[2 * i + 1] = hexmap[data[len - 1 - i] & 0x0F]; + } } return s; } + template + std::string pod_to_hex_reversed(const pod_t &h) + { + return buff_to_hex(&h, sizeof h, true); + } + template std::string pod_to_hex(const pod_t &h) { - constexpr char hexmap[] = "0123456789abcdef"; - const unsigned char* data = reinterpret_cast(&h); - size_t len = sizeof h; - - std::string s(len * 2, ' '); - for (size_t i = 0; i < len; ++i) - { - s[2 * i] = hexmap[data[i] >> 4]; - s[2 * i + 1] = hexmap[data[i] & 0x0F]; - } - - return s; + return buff_to_hex(&h, sizeof h); } template diff --git a/tests/functional_tests/crypto_tests_performance.h b/tests/functional_tests/crypto_tests_performance.h index 0996add2..b79a1bc6 100644 --- a/tests/functional_tests/crypto_tests_performance.h +++ b/tests/functional_tests/crypto_tests_performance.h @@ -5,6 +5,7 @@ // #pragma once #include +#include "crypto/crypto-sugar.h" // just for intellysense uint64_t get_bits_v1(const scalar_t& s, uint8_t bit_index_first, uint8_t bits_count) { @@ -1338,3 +1339,61 @@ TEST(perf, point_eq_vs_iszero) return true; } + + +TEST(perf, buff_to_hex) +{ + std::vector in_buffs; + std::vector out_hexs; + std::vector timings1, timings2; + + size_t N = 10000; + in_buffs.reserve(N); + out_hexs.reserve(N); + + for(size_t i = 0; i < N; ++i) + { + size_t len = (crypto::rand() % 128) + 1; // [1; 128] + std::string& buff = in_buffs.emplace_back(); + buff.resize(len); + generate_random_bytes(len, buff.data()); + } + + size_t rounds = 100, warmup_rounds = 20; + for(size_t i = 0; i < warmup_rounds + rounds; ++i) + { + out_hexs.clear(); + TIME_MEASURE_START(t1); + for(size_t j = 0; j < N; ++j) + out_hexs.push_back(epee::string_tools::buff_to_hex_nodelimer(in_buffs[j])); + TIME_MEASURE_FINISH(t1); + + uint64_t h1 = 0; + for(size_t j = 0; j < N; ++j) + h1 ^= hash_64(out_hexs[j].data(), out_hexs[j].size()); + + out_hexs.clear(); + TIME_MEASURE_START(t2); + for(size_t j = 0; j < N; ++j) + out_hexs.push_back(crypto::buff_to_hex(in_buffs[j].data(), in_buffs[j].size())); + TIME_MEASURE_FINISH(t2); + + uint64_t h2 = 0; + for(size_t j = 0; j < N; ++j) + h2 ^= hash_64(out_hexs[j].data(), out_hexs[j].size()); + + ASSERT_EQ(h1, h2); + + if (i >= warmup_rounds) + { + timings1.push_back(t1); + timings2.push_back(t2); + } + } + + std::cout << "After " << rounds << " rounds:" << ENDL << + "epee::string_tools::buff_to_hex_nodelimer : " << epee::misc_utils::median(timings1) << " mcs" << ENDL << + "crypto::buff_to_hex : " << epee::misc_utils::median(timings2) << " mcs" << ENDL; + + return true; +}