aboutsummaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authorAntonio Juarez <antonio.maria.juarez@live.com>2014-03-03 22:07:58 +0000
committerAntonio Juarez <antonio.maria.juarez@live.com>2014-03-03 22:07:58 +0000
commit296ae46ed8f8f6e5f986f978febad302e3df231a (patch)
tree1629164454a239308f33c9e12afb22e7f3cd8eeb /src/common
parentchanged name (diff)
downloadmonero-296ae46ed8f8f6e5f986f978febad302e3df231a.tar.xz
moved all stuff to github
Diffstat (limited to '')
-rw-r--r--src/common/base58.cpp246
-rw-r--r--src/common/base58.h20
-rw-r--r--src/common/boost_serialization_helper.h44
-rw-r--r--src/common/command_line.cpp12
-rw-r--r--src/common/command_line.h177
-rw-r--r--src/common/int-util.h205
-rw-r--r--src/common/pod-class.h11
-rw-r--r--src/common/unordered_containers_boost_serialization.h80
-rw-r--r--src/common/util.cpp363
-rw-r--r--src/common/util.h29
-rw-r--r--src/common/varint.h62
11 files changed, 1249 insertions, 0 deletions
diff --git a/src/common/base58.cpp b/src/common/base58.cpp
new file mode 100644
index 000000000..30042eeba
--- /dev/null
+++ b/src/common/base58.cpp
@@ -0,0 +1,246 @@
+// Copyright (c) 2012-2013 The Cryptonote developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+
+#include "base58.h"
+
+#include <assert.h>
+#include <string>
+#include <vector>
+
+#include "crypto/hash.h"
+#include "int-util.h"
+#include "util.h"
+#include "varint.h"
+
+namespace tools
+{
+ namespace base58
+ {
+ namespace
+ {
+ const char alphabet[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
+ const size_t alphabet_size = sizeof(alphabet) - 1;
+ const size_t encoded_block_sizes[] = {0, 2, 3, 5, 6, 7, 9, 10, 11};
+ const size_t full_block_size = sizeof(encoded_block_sizes) / sizeof(encoded_block_sizes[0]) - 1;
+ const size_t full_encoded_block_size = encoded_block_sizes[full_block_size];
+ const size_t addr_checksum_size = 4;
+
+ struct reverse_alphabet
+ {
+ reverse_alphabet()
+ {
+ m_data.resize(alphabet[alphabet_size - 1] - alphabet[0] + 1, -1);
+
+ for (size_t i = 0; i < alphabet_size; ++i)
+ {
+ size_t idx = static_cast<size_t>(alphabet[i] - alphabet[0]);
+ m_data[idx] = static_cast<int8_t>(i);
+ }
+ }
+
+ int operator()(char letter) const
+ {
+ size_t idx = static_cast<size_t>(letter - alphabet[0]);
+ return idx < m_data.size() ? m_data[idx] : -1;
+ }
+
+ static reverse_alphabet instance;
+
+ private:
+ std::vector<int8_t> m_data;
+ };
+
+ reverse_alphabet reverse_alphabet::instance;
+
+ struct decoded_block_sizes
+ {
+ decoded_block_sizes()
+ {
+ m_data.resize(encoded_block_sizes[full_block_size] + 1, -1);
+ for (size_t i = 0; i <= full_block_size; ++i)
+ {
+ m_data[encoded_block_sizes[i]] = static_cast<int>(i);
+ }
+ }
+
+ int operator()(size_t encoded_block_size) const
+ {
+ assert(encoded_block_size <= full_encoded_block_size);
+ return m_data[encoded_block_size];
+ }
+
+ static decoded_block_sizes instance;
+
+ private:
+ std::vector<int> m_data;
+ };
+
+ decoded_block_sizes decoded_block_sizes::instance;
+
+ uint64_t uint_8be_to_64(const uint8_t* data, size_t size)
+ {
+ assert(1 <= size && size <= sizeof(uint64_t));
+
+ uint64_t res = 0;
+ switch (9 - size)
+ {
+ case 1: res |= *data++;
+ case 2: res <<= 8; res |= *data++;
+ case 3: res <<= 8; res |= *data++;
+ case 4: res <<= 8; res |= *data++;
+ case 5: res <<= 8; res |= *data++;
+ case 6: res <<= 8; res |= *data++;
+ case 7: res <<= 8; res |= *data++;
+ case 8: res <<= 8; res |= *data; break;
+ default: assert(false);
+ }
+
+ return res;
+ }
+
+ void uint_64_to_8be(uint64_t num, size_t size, uint8_t* data)
+ {
+ assert(1 <= size && size <= sizeof(uint64_t));
+
+ uint64_t num_be = SWAP64BE(num);
+ memcpy(data, reinterpret_cast<uint8_t*>(&num_be) + sizeof(uint64_t) - size, size);
+ }
+
+ void encode_block(const char* block, size_t size, char* res)
+ {
+ assert(1 <= size && size <= sizeof(full_block_size));
+
+ uint64_t num = uint_8be_to_64(reinterpret_cast<const uint8_t*>(block), size);
+ int i = static_cast<int>(encoded_block_sizes[size]) - 1;
+ while (0 < num)
+ {
+ uint64_t remainder = num % alphabet_size;
+ num /= alphabet_size;
+ res[i] = alphabet[remainder];
+ --i;
+ }
+ }
+
+ bool decode_block(const char* block, size_t size, char* res)
+ {
+ assert(1 <= size && size <= full_encoded_block_size);
+
+ int res_size = decoded_block_sizes::instance(size);
+ if (res_size <= 0)
+ return false; // Invalid block size
+
+ uint64_t res_num = 0;
+ uint64_t order = 1;
+ for (size_t i = size - 1; i < size; --i)
+ {
+ int digit = reverse_alphabet::instance(block[i]);
+ if (digit < 0)
+ return false; // Invalid symbol
+
+ uint64_t product_hi;
+ uint64_t tmp = res_num + mul128(order, digit, &product_hi);
+ if (tmp < res_num || 0 != product_hi)
+ return false; // Overflow
+
+ res_num = tmp;
+ order *= alphabet_size; // Never overflows, 58^10 < 2^64
+ }
+
+ if (static_cast<size_t>(res_size) < full_block_size && (UINT64_C(1) << (8 * res_size)) <= res_num)
+ return false; // Overflow
+
+ uint_64_to_8be(res_num, res_size, reinterpret_cast<uint8_t*>(res));
+
+ return true;
+ }
+ }
+
+ std::string encode(const std::string& data)
+ {
+ if (data.empty())
+ return std::string();
+
+ size_t full_block_count = data.size() / full_block_size;
+ size_t last_block_size = data.size() % full_block_size;
+ size_t res_size = full_block_count * full_encoded_block_size + encoded_block_sizes[last_block_size];
+
+ std::string res(res_size, alphabet[0]);
+ for (size_t i = 0; i < full_block_count; ++i)
+ {
+ encode_block(data.data() + i * full_block_size, full_block_size, &res[i * full_encoded_block_size]);
+ }
+
+ if (0 < last_block_size)
+ {
+ encode_block(data.data() + full_block_count * full_block_size, last_block_size, &res[full_block_count * full_encoded_block_size]);
+ }
+
+ return res;
+ }
+
+ bool decode(const std::string& enc, std::string& data)
+ {
+ if (enc.empty())
+ {
+ data.clear();
+ return true;
+ }
+
+ size_t full_block_count = enc.size() / full_encoded_block_size;
+ size_t last_block_size = enc.size() % full_encoded_block_size;
+ int last_block_decoded_size = decoded_block_sizes::instance(last_block_size);
+ if (last_block_decoded_size < 0)
+ return false; // Invalid enc length
+ size_t data_size = full_block_count * full_block_size + last_block_decoded_size;
+
+ data.resize(data_size, 0);
+ for (size_t i = 0; i < full_block_count; ++i)
+ {
+ if (!decode_block(enc.data() + i * full_encoded_block_size, full_encoded_block_size, &data[i * full_block_size]))
+ return false;
+ }
+
+ if (0 < last_block_size)
+ {
+ if (!decode_block(enc.data() + full_block_count * full_encoded_block_size, last_block_size,
+ &data[full_block_count * full_block_size]))
+ return false;
+ }
+
+ return true;
+ }
+
+ std::string encode_addr(uint64_t tag, const std::string& data)
+ {
+ std::string buf = get_varint_data(tag);
+ buf += data;
+ crypto::hash hash = crypto::cn_fast_hash(buf.data(), buf.size());
+ const char* hash_data = reinterpret_cast<const char*>(&hash);
+ buf.append(hash_data, addr_checksum_size);
+ return encode(buf);
+ }
+
+ bool decode_addr(std::string addr, uint64_t& tag, std::string& data)
+ {
+ std::string addr_data;
+ bool r = decode(addr, addr_data);
+ if (!r) return false;
+
+ std::string checksum(addr_checksum_size, '\0');
+ checksum = addr_data.substr(addr_data.size() - addr_checksum_size);
+
+ addr_data.resize(addr_data.size() - addr_checksum_size);
+ crypto::hash hash = crypto::cn_fast_hash(addr_data.data(), addr_data.size());
+ std::string expected_checksum(reinterpret_cast<const char*>(&hash), addr_checksum_size);
+ if (expected_checksum != checksum) return false;
+
+ int read = tools::read_varint(addr_data.begin(), addr_data.end(), tag);
+ if (read <= 0) return false;
+
+ data = addr_data.substr(read);
+ return true;
+ }
+ }
+}
diff --git a/src/common/base58.h b/src/common/base58.h
new file mode 100644
index 000000000..4055f62ba
--- /dev/null
+++ b/src/common/base58.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2012-2013 The Cryptonote developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#pragma once
+
+#include <cstdint>
+#include <string>
+
+namespace tools
+{
+ namespace base58
+ {
+ std::string encode(const std::string& data);
+ bool decode(const std::string& enc, std::string& data);
+
+ std::string encode_addr(uint64_t tag, const std::string& data);
+ bool decode_addr(std::string addr, uint64_t& tag, std::string& data);
+ }
+}
diff --git a/src/common/boost_serialization_helper.h b/src/common/boost_serialization_helper.h
new file mode 100644
index 000000000..74016ae75
--- /dev/null
+++ b/src/common/boost_serialization_helper.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2012-2013 The Cryptonote developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#pragma once
+
+#include <boost/archive/binary_oarchive.hpp>
+#include <boost/archive/binary_iarchive.hpp>
+
+
+namespace tools
+{
+ template<class t_object>
+ bool serialize_obj_to_file(t_object& obj, const std::string& file_path)
+ {
+ TRY_ENTRY();
+ std::ofstream data_file;
+ data_file.open( file_path , std::ios_base::binary | std::ios_base::out| std::ios::trunc);
+ if(data_file.fail())
+ return false;
+
+ boost::archive::binary_oarchive a(data_file);
+ a << obj;
+
+ return !data_file.fail();
+ CATCH_ENTRY_L0("serialize_obj_to_file", false);
+ }
+
+ template<class t_object>
+ bool unserialize_obj_from_file(t_object& obj, const std::string& file_path)
+ {
+ TRY_ENTRY();
+
+ std::ifstream data_file;
+ data_file.open( file_path, std::ios_base::binary | std::ios_base::in);
+ if(data_file.fail())
+ return false;
+ boost::archive::binary_iarchive a(data_file);
+
+ a >> obj;
+ return !data_file.fail();
+ CATCH_ENTRY_L0("unserialize_obj_from_file", false);
+ }
+}
diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp
new file mode 100644
index 000000000..0b90345d9
--- /dev/null
+++ b/src/common/command_line.cpp
@@ -0,0 +1,12 @@
+// Copyright (c) 2012-2013 The Cryptonote developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "command_line.h"
+
+namespace command_line
+{
+ const arg_descriptor<bool> arg_help = {"help", "Produce help message"};
+ const arg_descriptor<bool> arg_version = {"version", "Output version information"};
+ const arg_descriptor<std::string> arg_data_dir = {"data-dir", "Specify data directory"};
+}
diff --git a/src/common/command_line.h b/src/common/command_line.h
new file mode 100644
index 000000000..860653772
--- /dev/null
+++ b/src/common/command_line.h
@@ -0,0 +1,177 @@
+// Copyright (c) 2012-2013 The Cryptonote developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#pragma once
+
+#include <iostream>
+#include <type_traits>
+
+#include <boost/program_options/parsers.hpp>
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include "include_base_utils.h"
+
+namespace command_line
+{
+ template<typename T, bool required = false>
+ struct arg_descriptor;
+
+ template<typename T>
+ struct arg_descriptor<T, false>
+ {
+ typedef T value_type;
+
+ const char* name;
+ const char* description;
+ T default_value;
+ bool not_use_default;
+ };
+
+ template<typename T>
+ struct arg_descriptor<std::vector<T>, false>
+ {
+ typedef std::vector<T> value_type;
+
+ const char* name;
+ const char* description;
+ };
+
+ template<typename T>
+ struct arg_descriptor<T, true>
+ {
+ static_assert(!std::is_same<T, bool>::value, "Boolean switch can't be required");
+
+ typedef T value_type;
+
+ const char* name;
+ const char* description;
+ };
+
+ template<typename T>
+ boost::program_options::typed_value<T, char>* make_semantic(const arg_descriptor<T, true>& /*arg*/)
+ {
+ return boost::program_options::value<T>()->required();
+ }
+
+ template<typename T>
+ boost::program_options::typed_value<T, char>* make_semantic(const arg_descriptor<T, false>& arg)
+ {
+ auto semantic = boost::program_options::value<T>();
+ if (!arg.not_use_default)
+ semantic->default_value(arg.default_value);
+ return semantic;
+ }
+
+ template<typename T>
+ boost::program_options::typed_value<T, char>* make_semantic(const arg_descriptor<T, false>& arg, const T& def)
+ {
+ auto semantic = boost::program_options::value<T>();
+ if (!arg.not_use_default)
+ semantic->default_value(def);
+ return semantic;
+ }
+
+ template<typename T>
+ boost::program_options::typed_value<std::vector<T>, char>* make_semantic(const arg_descriptor<std::vector<T>, false>& /*arg*/)
+ {
+ auto semantic = boost::program_options::value< std::vector<T> >();
+ semantic->default_value(std::vector<T>(), "");
+ return semantic;
+ }
+
+ template<typename T, bool required>
+ void add_arg(boost::program_options::options_description& description, const arg_descriptor<T, required>& arg, bool unique = true)
+ {
+ if (0 != description.find_nothrow(arg.name, false))
+ {
+ CHECK_AND_ASSERT_MES(!unique, void(), "Argument already exists: " << arg.name);
+ return;
+ }
+
+ description.add_options()(arg.name, make_semantic(arg), arg.description);
+ }
+
+ template<typename T>
+ void add_arg(boost::program_options::options_description& description, const arg_descriptor<T, false>& arg, const T& def, bool unique = true)
+ {
+ if (0 != description.find_nothrow(arg.name, false))
+ {
+ CHECK_AND_ASSERT_MES(!unique, void(), "Argument already exists: " << arg.name);
+ return;
+ }
+
+ description.add_options()(arg.name, make_semantic(arg, def), arg.description);
+ }
+
+ template<>
+ inline void add_arg(boost::program_options::options_description& description, const arg_descriptor<bool, false>& arg, bool unique)
+ {
+ if (0 != description.find_nothrow(arg.name, false))
+ {
+ CHECK_AND_ASSERT_MES(!unique, void(), "Argument already exists: " << arg.name);
+ return;
+ }
+
+ description.add_options()(arg.name, boost::program_options::bool_switch(), arg.description);
+ }
+
+ template<typename charT>
+ boost::program_options::basic_parsed_options<charT> parse_command_line(int argc, const charT* const argv[],
+ const boost::program_options::options_description& desc, bool allow_unregistered = false)
+ {
+ auto parser = boost::program_options::command_line_parser(argc, argv);
+ parser.options(desc);
+ if (allow_unregistered)
+ {
+ parser.allow_unregistered();
+ }
+ return parser.run();
+ }
+
+ template<typename F>
+ bool handle_error_helper(const boost::program_options::options_description& desc, F parser)
+ {
+ try
+ {
+ return parser();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Failed to parse arguments: " << e.what() << std::endl;
+ std::cerr << desc << std::endl;
+ return false;
+ }
+ catch (...)
+ {
+ std::cerr << "Failed to parse arguments: unknown exception" << std::endl;
+ std::cerr << desc << std::endl;
+ return false;
+ }
+ }
+
+ template<typename T, bool required>
+ bool has_arg(const boost::program_options::variables_map& vm, const arg_descriptor<T, required>& arg)
+ {
+ auto value = vm[arg.name];
+ return !value.empty();
+ }
+
+
+ template<typename T, bool required>
+ T get_arg(const boost::program_options::variables_map& vm, const arg_descriptor<T, required>& arg)
+ {
+ return vm[arg.name].template as<T>();
+ }
+
+ template<>
+ inline bool has_arg<bool, false>(const boost::program_options::variables_map& vm, const arg_descriptor<bool, false>& arg)
+ {
+ return get_arg<bool, false>(vm, arg);
+ }
+
+
+ extern const arg_descriptor<bool> arg_help;
+ extern const arg_descriptor<bool> arg_version;
+ extern const arg_descriptor<std::string> arg_data_dir;
+}
diff --git a/src/common/int-util.h b/src/common/int-util.h
new file mode 100644
index 000000000..ad0ef60e0
--- /dev/null
+++ b/src/common/int-util.h
@@ -0,0 +1,205 @@
+// Copyright (c) 2012-2013 The Cryptonote developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#pragma once
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/param.h>
+
+#if defined(_MSC_VER)
+#include <stdlib.h>
+
+static inline uint32_t rol32(uint32_t x, int r) {
+ static_assert(sizeof(uint32_t) == sizeof(unsigned int), "this code assumes 32-bit integers");
+ return _rotl(x, r);
+}
+
+static inline uint64_t rol64(uint64_t x, int r) {
+ return _rotl64(x, r);
+}
+
+#else
+
+static inline uint32_t rol32(uint32_t x, int r) {
+ return (x << (r & 31)) | (x >> (-r & 31));
+}
+
+static inline uint64_t rol64(uint64_t x, int r) {
+ return (x << (r & 63)) | (x >> (-r & 63));
+}
+
+#endif
+
+inline uint64_t hi_dword(uint64_t val) {
+ return val >> 32;
+}
+
+inline uint64_t lo_dword(uint64_t val) {
+ return val & 0xFFFFFFFF;
+}
+
+inline uint64_t mul128(uint64_t multiplier, uint64_t multiplicand, uint64_t* product_hi) {
+ // multiplier = ab = a * 2^32 + b
+ // multiplicand = cd = c * 2^32 + d
+ // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d
+ uint64_t a = hi_dword(multiplier);
+ uint64_t b = lo_dword(multiplier);
+ uint64_t c = hi_dword(multiplicand);
+ uint64_t d = lo_dword(multiplicand);
+
+ uint64_t ac = a * c;
+ uint64_t ad = a * d;
+ uint64_t bc = b * c;
+ uint64_t bd = b * d;
+
+ uint64_t adbc = ad + bc;
+ uint64_t adbc_carry = adbc < ad ? 1 : 0;
+
+ // multiplier * multiplicand = product_hi * 2^64 + product_lo
+ uint64_t product_lo = bd + (adbc << 32);
+ uint64_t product_lo_carry = product_lo < bd ? 1 : 0;
+ *product_hi = ac + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry;
+ assert(ac <= *product_hi);
+
+ return product_lo;
+}
+
+inline uint64_t div_with_reminder(uint64_t dividend, uint32_t divisor, uint32_t* remainder) {
+ dividend |= ((uint64_t)*remainder) << 32;
+ *remainder = dividend % divisor;
+ return dividend / divisor;
+}
+
+// Long division with 2^32 base
+inline uint32_t div128_32(uint64_t dividend_hi, uint64_t dividend_lo, uint32_t divisor, uint64_t* quotient_hi, uint64_t* quotient_lo) {
+ uint64_t dividend_dwords[4];
+ uint32_t remainder = 0;
+
+ dividend_dwords[3] = hi_dword(dividend_hi);
+ dividend_dwords[2] = lo_dword(dividend_hi);
+ dividend_dwords[1] = hi_dword(dividend_lo);
+ dividend_dwords[0] = lo_dword(dividend_lo);
+
+ *quotient_hi = div_with_reminder(dividend_dwords[3], divisor, &remainder) << 32;
+ *quotient_hi |= div_with_reminder(dividend_dwords[2], divisor, &remainder);
+ *quotient_lo = div_with_reminder(dividend_dwords[1], divisor, &remainder) << 32;
+ *quotient_lo |= div_with_reminder(dividend_dwords[0], divisor, &remainder);
+
+ return remainder;
+}
+
+#define IDENT32(x) ((uint32_t) (x))
+#define IDENT64(x) ((uint64_t) (x))
+
+#define SWAP32(x) ((((uint32_t) (x) & 0x000000ff) << 24) | \
+ (((uint32_t) (x) & 0x0000ff00) << 8) | \
+ (((uint32_t) (x) & 0x00ff0000) >> 8) | \
+ (((uint32_t) (x) & 0xff000000) >> 24))
+#define SWAP64(x) ((((uint64_t) (x) & 0x00000000000000ff) << 56) | \
+ (((uint64_t) (x) & 0x000000000000ff00) << 40) | \
+ (((uint64_t) (x) & 0x0000000000ff0000) << 24) | \
+ (((uint64_t) (x) & 0x00000000ff000000) << 8) | \
+ (((uint64_t) (x) & 0x000000ff00000000) >> 8) | \
+ (((uint64_t) (x) & 0x0000ff0000000000) >> 24) | \
+ (((uint64_t) (x) & 0x00ff000000000000) >> 40) | \
+ (((uint64_t) (x) & 0xff00000000000000) >> 56))
+
+static inline uint32_t ident32(uint32_t x) { return x; }
+static inline uint64_t ident64(uint64_t x) { return x; }
+
+static inline uint32_t swap32(uint32_t x) {
+ x = ((x & 0x00ff00ff) << 8) | ((x & 0xff00ff00) >> 8);
+ return (x << 16) | (x >> 16);
+}
+static inline uint64_t swap64(uint64_t x) {
+ x = ((x & 0x00ff00ff00ff00ff) << 8) | ((x & 0xff00ff00ff00ff00) >> 8);
+ x = ((x & 0x0000ffff0000ffff) << 16) | ((x & 0xffff0000ffff0000) >> 16);
+ return (x << 32) | (x >> 32);
+}
+
+#if defined(__GNUC__)
+#define UNUSED __attribute__((unused))
+#else
+#define UNUSED
+#endif
+static inline void mem_inplace_ident(void *mem UNUSED, size_t n UNUSED) { }
+#undef UNUSED
+
+static inline void mem_inplace_swap32(void *mem, size_t n) {
+ size_t i;
+ for (i = 0; i < n; i++) {
+ ((uint32_t *) mem)[i] = swap32(((const uint32_t *) mem)[i]);
+ }
+}
+static inline void mem_inplace_swap64(void *mem, size_t n) {
+ size_t i;
+ for (i = 0; i < n; i++) {
+ ((uint64_t *) mem)[i] = swap64(((const uint64_t *) mem)[i]);
+ }
+}
+
+static inline void memcpy_ident32(void *dst, const void *src, size_t n) {
+ memcpy(dst, src, 4 * n);
+}
+static inline void memcpy_ident64(void *dst, const void *src, size_t n) {
+ memcpy(dst, src, 8 * n);
+}
+
+static inline void memcpy_swap32(void *dst, const void *src, size_t n) {
+ size_t i;
+ for (i = 0; i < n; i++) {
+ ((uint32_t *) dst)[i] = swap32(((const uint32_t *) src)[i]);
+ }
+}
+static inline void memcpy_swap64(void *dst, const void *src, size_t n) {
+ size_t i;
+ for (i = 0; i < n; i++) {
+ ((uint64_t *) dst)[i] = swap64(((const uint64_t *) src)[i]);
+ }
+}
+
+#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) || !defined(BIG_ENDIAN)
+static_assert(false, "BYTE_ORDER is undefined. Perhaps, GNU extensions are not enabled");
+#endif
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define SWAP32LE IDENT32
+#define SWAP32BE SWAP32
+#define swap32le ident32
+#define swap32be swap32
+#define mem_inplace_swap32le mem_inplace_ident
+#define mem_inplace_swap32be mem_inplace_swap32
+#define memcpy_swap32le memcpy_ident32
+#define memcpy_swap32be memcpy_swap32
+#define SWAP64LE IDENT64
+#define SWAP64BE SWAP64
+#define swap64le ident64
+#define swap64be swap64
+#define mem_inplace_swap64le mem_inplace_ident
+#define mem_inplace_swap64be mem_inplace_swap64
+#define memcpy_swap64le memcpy_ident64
+#define memcpy_swap64be memcpy_swap64
+#endif
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define SWAP32BE IDENT32
+#define SWAP32LE SWAP32
+#define swap32be ident32
+#define swap32le swap32
+#define mem_inplace_swap32be mem_inplace_ident
+#define mem_inplace_swap32le mem_inplace_swap32
+#define memcpy_swap32be memcpy_ident32
+#define memcpy_swap32le memcpy_swap32
+#define SWAP64BE IDENT64
+#define SWAP64LE SWAP64
+#define swap64be ident64
+#define swap64le swap64
+#define mem_inplace_swap64be mem_inplace_ident
+#define mem_inplace_swap64le mem_inplace_swap64
+#define memcpy_swap64be memcpy_ident64
+#define memcpy_swap64le memcpy_swap64
+#endif
diff --git a/src/common/pod-class.h b/src/common/pod-class.h
new file mode 100644
index 000000000..c07edb208
--- /dev/null
+++ b/src/common/pod-class.h
@@ -0,0 +1,11 @@
+// Copyright (c) 2012-2013 The Cryptonote developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#pragma once
+
+#if defined(_MSC_VER)
+#define POD_CLASS struct
+#else
+#define POD_CLASS class
+#endif
diff --git a/src/common/unordered_containers_boost_serialization.h b/src/common/unordered_containers_boost_serialization.h
new file mode 100644
index 000000000..0804660ca
--- /dev/null
+++ b/src/common/unordered_containers_boost_serialization.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2012-2013 The Cryptonote developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#pragma once
+
+#include <boost/serialization/split_free.hpp>
+#include <unordered_map>
+#include <unordered_set>
+
+namespace boost
+{
+ namespace serialization
+ {
+ template <class Archive, class h_key, class hval>
+ inline void save(Archive &a, const std::unordered_map<h_key, hval> &x, const boost::serialization::version_type ver)
+ {
+ size_t s = x.size();
+ a << s;
+ BOOST_FOREACH(auto& v, x)
+ {
+ a << v.first;
+ a << v.second;
+ }
+ }
+
+ template <class Archive, class h_key, class hval>
+ inline void load(Archive &a, std::unordered_map<h_key, hval> &x, const boost::serialization::version_type ver)
+ {
+ size_t s = 0;
+ a >> s;
+ for(size_t i = 0; i != s; i++)
+ {
+ h_key k;
+ hval v;
+ a >> k;
+ a >> v;
+ x.insert(std::pair<h_key, hval>(k, v));
+ }
+ }
+
+
+ template <class Archive, class hval>
+ inline void save(Archive &a, const std::unordered_set<hval> &x, const boost::serialization::version_type ver)
+ {
+ size_t s = x.size();
+ a << s;
+ BOOST_FOREACH(auto& v, x)
+ {
+ a << v;
+ }
+ }
+
+ template <class Archive, class hval>
+ inline void load(Archive &a, std::unordered_set<hval> &x, const boost::serialization::version_type ver)
+ {
+ size_t s = 0;
+ a >> s;
+ for(size_t i = 0; i != s; i++)
+ {
+ hval v;
+ a >> v;
+ x.insert(v);
+ }
+ }
+
+
+ template <class Archive, class h_key, class hval>
+ inline void serialize(Archive &a, std::unordered_map<h_key, hval> &x, const boost::serialization::version_type ver)
+ {
+ split_free(a, x, ver);
+ }
+
+ template <class Archive, class hval>
+ inline void serialize(Archive &a, std::unordered_set<hval> &x, const boost::serialization::version_type ver)
+ {
+ split_free(a, x, ver);
+ }
+ }
+}
diff --git a/src/common/util.cpp b/src/common/util.cpp
new file mode 100644
index 000000000..b24016cc3
--- /dev/null
+++ b/src/common/util.cpp
@@ -0,0 +1,363 @@
+// Copyright (c) 2012-2013 The Cryptonote developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <cstdio>
+
+#include "include_base_utils.h"
+using namespace epee;
+
+#include "util.h"
+#include "cryptonote_config.h"
+
+#ifdef WIN32
+#include <windows.h>
+#include <shlobj.h>
+#include <strsafe.h>
+#else
+#include <sys/utsname.h>
+#endif
+
+
+namespace tools
+{
+
+#ifdef WIN32
+ std::string get_windows_version_display_string()
+ {
+ typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
+ typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD);
+#define BUFSIZE 10000
+
+ char pszOS[BUFSIZE] = {0};
+ OSVERSIONINFOEX osvi;
+ SYSTEM_INFO si;
+ PGNSI pGNSI;
+ PGPI pGPI;
+ BOOL bOsVersionInfoEx;
+ DWORD dwType;
+
+ ZeroMemory(&si, sizeof(SYSTEM_INFO));
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
+
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO*) &osvi);
+
+ if(!bOsVersionInfoEx) return pszOS;
+
+ // Call GetNativeSystemInfo if supported or GetSystemInfo otherwise.
+
+ pGNSI = (PGNSI) GetProcAddress(
+ GetModuleHandle(TEXT("kernel32.dll")),
+ "GetNativeSystemInfo");
+ if(NULL != pGNSI)
+ pGNSI(&si);
+ else GetSystemInfo(&si);
+
+ if ( VER_PLATFORM_WIN32_NT==osvi.dwPlatformId &&
+ osvi.dwMajorVersion > 4 )
+ {
+ StringCchCopy(pszOS, BUFSIZE, TEXT("Microsoft "));
+
+ // Test for the specific product.
+
+ if ( osvi.dwMajorVersion == 6 )
+ {
+ if( osvi.dwMinorVersion == 0 )
+ {
+ if( osvi.wProductType == VER_NT_WORKSTATION )
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows Vista "));
+ else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2008 " ));
+ }
+
+ if ( osvi.dwMinorVersion == 1 )
+ {
+ if( osvi.wProductType == VER_NT_WORKSTATION )
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows 7 "));
+ else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2008 R2 " ));
+ }
+
+ pGPI = (PGPI) GetProcAddress(
+ GetModuleHandle(TEXT("kernel32.dll")),
+ "GetProductInfo");
+
+ pGPI( osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType);
+
+ switch( dwType )
+ {
+ case PRODUCT_ULTIMATE:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Ultimate Edition" ));
+ break;
+ case PRODUCT_PROFESSIONAL:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Professional" ));
+ break;
+ case PRODUCT_HOME_PREMIUM:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Home Premium Edition" ));
+ break;
+ case PRODUCT_HOME_BASIC:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Home Basic Edition" ));
+ break;
+ case PRODUCT_ENTERPRISE:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition" ));
+ break;
+ case PRODUCT_BUSINESS:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Business Edition" ));
+ break;
+ case PRODUCT_STARTER:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Starter Edition" ));
+ break;
+ case PRODUCT_CLUSTER_SERVER:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Cluster Server Edition" ));
+ break;
+ case PRODUCT_DATACENTER_SERVER:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Datacenter Edition" ));
+ break;
+ case PRODUCT_DATACENTER_SERVER_CORE:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Datacenter Edition (core installation)" ));
+ break;
+ case PRODUCT_ENTERPRISE_SERVER:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition" ));
+ break;
+ case PRODUCT_ENTERPRISE_SERVER_CORE:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition (core installation)" ));
+ break;
+ case PRODUCT_ENTERPRISE_SERVER_IA64:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition for Itanium-based Systems" ));
+ break;
+ case PRODUCT_SMALLBUSINESS_SERVER:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Small Business Server" ));
+ break;
+ case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Small Business Server Premium Edition" ));
+ break;
+ case PRODUCT_STANDARD_SERVER:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Standard Edition" ));
+ break;
+ case PRODUCT_STANDARD_SERVER_CORE:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Standard Edition (core installation)" ));
+ break;
+ case PRODUCT_WEB_SERVER:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Web Server Edition" ));
+ break;
+ }
+ }
+
+ if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2 )
+ {
+ if( GetSystemMetrics(SM_SERVERR2) )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Server 2003 R2, "));
+ else if ( osvi.wSuiteMask & VER_SUITE_STORAGE_SERVER )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Storage Server 2003"));
+ else if ( osvi.wSuiteMask & VER_SUITE_WH_SERVER )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Home Server"));
+ else if( osvi.wProductType == VER_NT_WORKSTATION &&
+ si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64)
+ {
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Windows XP Professional x64 Edition"));
+ }
+ else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2003, "));
+
+ // Test for the server type.
+ if ( osvi.wProductType != VER_NT_WORKSTATION )
+ {
+ if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_IA64 )
+ {
+ if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Edition for Itanium-based Systems" ));
+ else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise Edition for Itanium-based Systems" ));
+ }
+
+ else if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 )
+ {
+ if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter x64 Edition" ));
+ else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise x64 Edition" ));
+ else StringCchCat(pszOS, BUFSIZE, TEXT( "Standard x64 Edition" ));
+ }
+
+ else
+ {
+ if ( osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Compute Cluster Edition" ));
+ else if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Edition" ));
+ else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise Edition" ));
+ else if ( osvi.wSuiteMask & VER_SUITE_BLADE )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Web Edition" ));
+ else StringCchCat(pszOS, BUFSIZE, TEXT( "Standard Edition" ));
+ }
+ }
+ }
+
+ if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 )
+ {
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows XP "));
+ if( osvi.wSuiteMask & VER_SUITE_PERSONAL )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Home Edition" ));
+ else StringCchCat(pszOS, BUFSIZE, TEXT( "Professional" ));
+ }
+
+ if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 )
+ {
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows 2000 "));
+
+ if ( osvi.wProductType == VER_NT_WORKSTATION )
+ {
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Professional" ));
+ }
+ else
+ {
+ if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Server" ));
+ else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Advanced Server" ));
+ else StringCchCat(pszOS, BUFSIZE, TEXT( "Server" ));
+ }
+ }
+
+ // Include service pack (if any) and build number.
+
+ if( strlen(osvi.szCSDVersion) > 0 )
+ {
+ StringCchCat(pszOS, BUFSIZE, TEXT(" ") );
+ StringCchCat(pszOS, BUFSIZE, osvi.szCSDVersion);
+ }
+
+ TCHAR buf[80];
+
+ StringCchPrintf( buf, 80, TEXT(" (build %d)"), osvi.dwBuildNumber);
+ StringCchCat(pszOS, BUFSIZE, buf);
+
+ if ( osvi.dwMajorVersion >= 6 )
+ {
+ if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 )
+ StringCchCat(pszOS, BUFSIZE, TEXT( ", 64-bit" ));
+ else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_INTEL )
+ StringCchCat(pszOS, BUFSIZE, TEXT(", 32-bit"));
+ }
+
+ return pszOS;
+ }
+ else
+ {
+ printf( "This sample does not support this version of Windows.\n");
+ return pszOS;
+ }
+ }
+#else
+std::string get_nix_version_display_string()
+{
+ utsname un;
+
+ if(uname(&un) < 0)
+ return std::string("*nix: failed to get os version");
+ return std::string() + un.sysname + " " + un.version + " " + un.release;
+}
+#endif
+
+
+
+ std::string get_os_version_string()
+ {
+#ifdef WIN32
+ return get_windows_version_display_string();
+#else
+ return get_nix_version_display_string();
+#endif
+ }
+
+
+
+#ifdef WIN32
+ std::string get_special_folder_path(int nfolder, bool iscreate)
+ {
+ namespace fs = boost::filesystem;
+ char psz_path[MAX_PATH] = "";
+
+ if(SHGetSpecialFolderPathA(NULL, psz_path, nfolder, iscreate))
+ {
+ return psz_path;
+ }
+
+ LOG_ERROR("SHGetSpecialFolderPathA() failed, could not obtain requested path.");
+ return "";
+ }
+#endif
+
+ std::string get_default_data_dir()
+ {
+ //namespace fs = boost::filesystem;
+ // Windows < Vista: C:\Documents and Settings\Username\Application Data\CRYPTONOTE_NAME
+ // Windows >= Vista: C:\Users\Username\AppData\Roaming\CRYPTONOTE_NAME
+ // Mac: ~/Library/Application Support/CRYPTONOTE_NAME
+ // Unix: ~/.CRYPTONOTE_NAME
+ std::string config_folder;
+#ifdef WIN32
+ // Windows
+ config_folder = get_special_folder_path(CSIDL_APPDATA, true) + "/" + CRYPTONOTE_NAME;
+#else
+ std::string pathRet;
+ char* pszHome = getenv("HOME");
+ if (pszHome == NULL || strlen(pszHome) == 0)
+ pathRet = "/";
+ else
+ pathRet = pszHome;
+#ifdef MAC_OSX
+ // Mac
+ pathRet /= "Library/Application Support";
+ config_folder = (pathRet + "/" + CRYPTONOTE_NAME);
+#else
+ // Unix
+ config_folder = (pathRet + "/." + CRYPTONOTE_NAME);
+#endif
+#endif
+
+ return config_folder;
+ }
+
+ bool create_directories_if_necessary(const std::string& path)
+ {
+ namespace fs = boost::filesystem;
+ boost::system::error_code ec;
+ fs::path fs_path(path);
+ if (fs::is_directory(fs_path, ec))
+ {
+ return true;
+ }
+
+ bool res = fs::create_directories(fs_path, ec);
+ if (res)
+ {
+ LOG_PRINT_L2("Created directory: " << path);
+ }
+ else
+ {
+ LOG_PRINT_L2("Can't create directory: " << path << ", err: "<< ec.message());
+ }
+
+ return res;
+ }
+
+ std::error_code replace_file(const std::string& replacement_name, const std::string& replaced_name)
+ {
+ int code;
+#if defined(WIN32)
+ // Maximizing chances for success
+ DWORD attributes = ::GetFileAttributes(replaced_name.c_str());
+ if (INVALID_FILE_ATTRIBUTES != attributes)
+ {
+ ::SetFileAttributes(replaced_name.c_str(), attributes & (~FILE_ATTRIBUTE_READONLY));
+ }
+
+ bool ok = 0 != ::MoveFileEx(replacement_name.c_str(), replaced_name.c_str(), MOVEFILE_REPLACE_EXISTING);
+ code = ok ? 0 : static_cast<int>(::GetLastError());
+#else
+ bool ok = 0 == std::rename(replacement_name.c_str(), replaced_name.c_str());
+ code = ok ? 0 : errno;
+#endif
+ return std::error_code(code, std::system_category());
+ }
+}
diff --git a/src/common/util.h b/src/common/util.h
new file mode 100644
index 000000000..a29a30fff
--- /dev/null
+++ b/src/common/util.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2012-2013 The Cryptonote developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#pragma once
+
+#include <system_error>
+#include <boost/filesystem.hpp>
+
+#include "crypto/crypto.h"
+#include "crypto/hash.h"
+#include "misc_language.h"
+#include "p2p/p2p_protocol_defs.h"
+
+namespace tools
+{
+ std::string get_default_data_dir();
+ std::string get_os_version_string();
+ bool create_directories_if_necessary(const std::string& path);
+ std::error_code replace_file(const std::string& replacement_name, const std::string& replaced_name);
+
+ inline crypto::hash get_proof_of_trust_hash(const nodetool::proof_of_trust& pot)
+ {
+ std::string s;
+ s.append(reinterpret_cast<const char*>(&pot.peer_id), sizeof(pot.peer_id));
+ s.append(reinterpret_cast<const char*>(&pot.time), sizeof(pot.time));
+ return crypto::cn_fast_hash(s.data(), s.size());
+ }
+}
diff --git a/src/common/varint.h b/src/common/varint.h
new file mode 100644
index 000000000..e62470fdf
--- /dev/null
+++ b/src/common/varint.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2012-2013 The Cryptonote developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#pragma once
+
+#include <limits>
+#include <type_traits>
+#include <utility>
+#include <sstream>
+#include <string>
+
+namespace tools {
+
+ template<typename OutputIt, typename T>
+ typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, void>::type
+ write_varint(OutputIt &&dest, T i) {
+ while (i >= 0x80) {
+ *dest++ = (static_cast<char>(i) & 0x7f) | 0x80;
+ i >>= 7;
+ }
+ *dest++ = static_cast<char>(i);
+ }
+
+ template<typename t_type>
+ std::string get_varint_data(const t_type& v)
+ {
+ std::stringstream ss;
+ write_varint(std::ostreambuf_iterator<char>(ss), v);
+ return ss.str();
+ }
+
+ template<int bits, typename InputIt, typename T>
+ typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value && 0 <= bits && bits <= std::numeric_limits<T>::digits, int>::type
+ read_varint(InputIt &&first, InputIt &&last, T &i) {
+ int read = 0;
+ i = 0;
+ for (int shift = 0;; shift += 7) {
+ if (first == last) {
+ return read; // End of input.
+ }
+ unsigned char byte = *first++;
+ ++read;
+ if (shift + 7 >= bits && byte >= 1 << (bits - shift)) {
+ return -1; // Overflow.
+ }
+ if (byte == 0 && shift != 0) {
+ return -2; // Non-canonical representation.
+ }
+ i |= static_cast<T>(byte & 0x7f) << shift;
+ if ((byte & 0x80) == 0) {
+ break;
+ }
+ }
+ return read;
+ }
+
+ template<typename InputIt, typename T>
+ int read_varint(InputIt &&first, InputIt &&last, T &i) {
+ return read_varint<std::numeric_limits<T>::digits, InputIt, T>(std::move(first), std::move(last), i);
+ }
+}