aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/crypto/slow-hash.c8
-rw-r--r--src/mnemonics/electrum-words.cpp82
-rw-r--r--src/mnemonics/electrum-words.h3
-rw-r--r--src/mnemonics/english.h1
-rw-r--r--src/mnemonics/japanese.h1
-rw-r--r--src/mnemonics/language_base.h8
-rw-r--r--src/mnemonics/old_english.h1
-rw-r--r--src/mnemonics/portuguese.h1
-rw-r--r--src/mnemonics/spanish.h1
-rw-r--r--src/rpc/core_rpc_server.cpp22
-rw-r--r--src/serialization/binary_archive.h66
-rw-r--r--src/serialization/binary_utils.h39
-rw-r--r--src/serialization/json_archive.h21
-rw-r--r--src/serialization/serialization.h265
-rw-r--r--src/serialization/variant.h27
-rw-r--r--tests/CMakeLists.txt2
-rw-r--r--tests/unit_tests/mnemonics.cpp154
-rw-r--r--tests/unit_tests/slow_memmem.cpp130
18 files changed, 667 insertions, 165 deletions
diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c
index ec356d53b..787545830 100644
--- a/src/crypto/slow-hash.c
+++ b/src/crypto/slow-hash.c
@@ -105,7 +105,7 @@
_a = _mm_load_si128(R128(a)); \
/*
- * An SSE-optimized implementation of the second half of CryptoNote step 3.
+ * An SSE-optimized implementation of the second half of CryptoNight step 3.
* After using AES to mix a scratchpad value into _c (done by the caller),
* this macro xors it with _b and stores the result back to the same index (j) that it
* loaded the scratchpad value from. It then performs a second random memory
@@ -281,7 +281,7 @@ STATIC INLINE void aes_expand_key(const uint8_t *key, uint8_t *expandedKey)
ek[10] = t1;
}
-/*
+/**
* @brief a "pseudo" round of AES (similar to but slightly different from normal AES encryption)
*
* To fill its 2MB scratch buffer, CryptoNight uses a nonstandard implementation
@@ -323,7 +323,7 @@ STATIC INLINE void aes_pseudo_round(const uint8_t *in, uint8_t *out,
}
}
-/*
+/**
* @brief aes_pseudo_round that loads data from *in and xors it with *xor first
*
* This function performs the same operations as aes_pseudo_round, but before
@@ -522,7 +522,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash)
hash_process(&state.hs, data, length);
memcpy(text, state.init, INIT_SIZE_BYTE);
- /* CryptoNight Step 2: Iteratively encrypt the results from keccak to fill
+ /* CryptoNight Step 2: Iteratively encrypt the results from Keccak to fill
* the 2MB large random access buffer.
*/
diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp
index b204e81cf..ffa82b21e 100644
--- a/src/mnemonics/electrum-words.cpp
+++ b/src/mnemonics/electrum-words.cpp
@@ -61,7 +61,6 @@
namespace
{
- const int seed_length = 24;
/*!
* \brief Finds the word list that contains the seed words and puts the indices
@@ -69,11 +68,11 @@ namespace
* \param seed List of words to match.
* \param has_checksum If word list passed checksum test, we need to only do a prefix check.
* \param matched_indices The indices where the seed words were found are added to this.
+ * \param language Language instance pointer to write to after it is found.
* \return true if all the words were present in some language false if not.
*/
bool find_seed_language(const std::vector<std::string> &seed,
- bool has_checksum, std::vector<uint32_t> &matched_indices, uint32_t &word_list_length,
- std::string &language_name)
+ bool has_checksum, std::vector<uint32_t> &matched_indices, Language::Base **language)
{
// If there's a new language added, add an instance of it here.
std::vector<Language::Base*> language_instances({
@@ -83,18 +82,6 @@ namespace
Language::Singleton<Language::Japanese>::instance(),
Language::Singleton<Language::OldEnglish>::instance()
});
- // To hold trimmed seed words in case of a checksum being present.
- std::vector<std::string> trimmed_seed;
- if (has_checksum)
- {
- // If it had a checksum, we'll just compare the unique prefix
- // So we create a list of trimmed seed words
- for (std::vector<std::string>::const_iterator it = seed.begin(); it != seed.end(); it++)
- {
- trimmed_seed.push_back(it->length() > Language::unique_prefix_length ?
- it->substr(0, Language::unique_prefix_length) : *it);
- }
- }
// Iterate through all the languages and find a match
for (std::vector<Language::Base*>::iterator it1 = language_instances.begin();
@@ -104,23 +91,22 @@ namespace
const std::unordered_map<std::string, uint32_t> &trimmed_word_map = (*it1)->get_trimmed_word_map();
// To iterate through seed words
std::vector<std::string>::const_iterator it2;
- // To iterate through trimmed seed words
- std::vector<std::string>::iterator it3;
bool full_match = true;
+ std::string trimmed_word;
// Iterate through all the words and see if they're all present
- for (it2 = seed.begin(), it3 = trimmed_seed.begin();
- it2 != seed.end(); it2++, it3++)
+ for (it2 = seed.begin(); it2 != seed.end(); it2++)
{
if (has_checksum)
{
+ trimmed_word = it2->substr(0, (*it1)->get_unique_prefix_length());
// Use the trimmed words and map
- if (trimmed_word_map.count(*it3) == 0)
+ if (trimmed_word_map.count(trimmed_word) == 0)
{
full_match = false;
break;
}
- matched_indices.push_back(trimmed_word_map.at(*it3));
+ matched_indices.push_back(trimmed_word_map.at(trimmed_word));
}
else
{
@@ -134,8 +120,7 @@ namespace
}
if (full_match)
{
- word_list_length = (*it1)->get_word_list().size();
- language_name = (*it1)->get_language_name();
+ *language = *it1;
return true;
}
// Some didn't match. Clear the index array.
@@ -146,18 +131,20 @@ namespace
/*!
* \brief Creates a checksum index in the word list array on the list of words.
- * \param word_list Vector of words
- * \return Checksum index
+ * \param word_list Vector of words
+ * \param unique_prefix_length the prefix length of each word to use for checksum
+ * \return Checksum index
*/
- uint32_t create_checksum_index(const std::vector<std::string> &word_list)
+ uint32_t create_checksum_index(const std::vector<std::string> &word_list,
+ uint32_t unique_prefix_length)
{
std::string trimmed_words = "";
for (std::vector<std::string>::const_iterator it = word_list.begin(); it != word_list.end(); it++)
{
- if (it->length() > 4)
+ if (it->length() > unique_prefix_length)
{
- trimmed_words += it->substr(0, Language::unique_prefix_length);
+ trimmed_words += it->substr(0, unique_prefix_length);
}
else
{
@@ -166,25 +153,26 @@ namespace
}
boost::crc_32_type result;
result.process_bytes(trimmed_words.data(), trimmed_words.length());
- return result.checksum() % seed_length;
+ return result.checksum() % crypto::ElectrumWords::seed_length;
}
/*!
* \brief Does the checksum test on the seed passed.
- * \param seed Vector of seed words
- * \return True if the test passed false if not.
+ * \param seed Vector of seed words
+ * \param unique_prefix_length the prefix length of each word to use for checksum
+ * \return True if the test passed false if not.
*/
- bool checksum_test(std::vector<std::string> seed)
+ bool checksum_test(std::vector<std::string> seed, uint32_t unique_prefix_length)
{
// The last word is the checksum.
std::string last_word = seed.back();
seed.pop_back();
- std::string checksum = seed[create_checksum_index(seed)];
+ std::string checksum = seed[create_checksum_index(seed, unique_prefix_length)];
- std::string trimmed_checksum = checksum.length() > 4 ? checksum.substr(0, Language::unique_prefix_length) :
+ std::string trimmed_checksum = checksum.length() > unique_prefix_length ? checksum.substr(0, unique_prefix_length) :
checksum;
- std::string trimmed_last_word = checksum.length() > 4 ? last_word.substr(0, Language::unique_prefix_length) :
+ std::string trimmed_last_word = last_word.length() > unique_prefix_length ? last_word.substr(0, unique_prefix_length) :
last_word;
return trimmed_checksum == trimmed_last_word;
}
@@ -211,11 +199,12 @@ namespace crypto
* \param language_name Language of the seed as found gets written here.
* \return false if not a multiple of 3 words, or if word is not in the words list
*/
- bool words_to_bytes(const std::string& words, crypto::secret_key& dst,
+ bool words_to_bytes(std::string words, crypto::secret_key& dst,
std::string &language_name)
{
std::vector<std::string> seed;
+ boost::algorithm::trim(words);
boost::split(seed, words, boost::is_any_of(" "));
// error on non-compliant word list
@@ -227,22 +216,25 @@ namespace crypto
// If it is seed with a checksum.
bool has_checksum = seed.size() == (seed_length + 1);
+
+ std::vector<uint32_t> matched_indices;
+ Language::Base *language;
+ if (!find_seed_language(seed, has_checksum, matched_indices, &language))
+ {
+ return false;
+ }
+ language_name = language->get_language_name();
+ uint32_t word_list_length = language->get_word_list().size();
+
if (has_checksum)
{
- if (!checksum_test(seed))
+ if (!checksum_test(seed, language->get_unique_prefix_length()))
{
// Checksum fail
return false;
}
seed.pop_back();
}
-
- std::vector<uint32_t> matched_indices;
- uint32_t word_list_length = 0;
- if (!find_seed_language(seed, has_checksum, matched_indices, word_list_length, language_name))
- {
- return false;
- }
for (unsigned int i=0; i < seed.size() / 3; i++)
{
@@ -335,7 +327,7 @@ namespace crypto
}
words.pop_back();
- words += (' ' + words_store[create_checksum_index(words_store)]);
+ words += (' ' + words_store[create_checksum_index(words_store, language->get_unique_prefix_length())]);
return false;
}
diff --git a/src/mnemonics/electrum-words.h b/src/mnemonics/electrum-words.h
index b822e7740..b75de30e9 100644
--- a/src/mnemonics/electrum-words.h
+++ b/src/mnemonics/electrum-words.h
@@ -59,6 +59,7 @@ namespace crypto
namespace ElectrumWords
{
+ const int seed_length = 24;
const std::string old_language_name = "OldEnglish";
/*!
* \brief Converts seed words to bytes (secret key).
@@ -67,7 +68,7 @@ namespace crypto
* \param language_name Language of the seed as found gets written here.
* \return false if not a multiple of 3 words, or if word is not in the words list
*/
- bool words_to_bytes(const std::string& words, crypto::secret_key& dst,
+ bool words_to_bytes(std::string words, crypto::secret_key& dst,
std::string &language_name);
/*!
diff --git a/src/mnemonics/english.h b/src/mnemonics/english.h
index ae39d44fd..8ca0a3fe3 100644
--- a/src/mnemonics/english.h
+++ b/src/mnemonics/english.h
@@ -1681,6 +1681,7 @@ namespace Language
"zones",
"zoom"
});
+ unique_prefix_length = 3;
word_map = new std::unordered_map<std::string, uint32_t>;
trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
language_name = "English";
diff --git a/src/mnemonics/japanese.h b/src/mnemonics/japanese.h
index 22c7a53ba..fc3d1ee74 100644
--- a/src/mnemonics/japanese.h
+++ b/src/mnemonics/japanese.h
@@ -1681,6 +1681,7 @@ namespace Language
"びじゅつかん",
"ひしょ"
});
+ unique_prefix_length = 4;
word_map = new std::unordered_map<std::string, uint32_t>;
trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
language_name = "Japanese";
diff --git a/src/mnemonics/language_base.h b/src/mnemonics/language_base.h
index 0f1062b09..06815e39d 100644
--- a/src/mnemonics/language_base.h
+++ b/src/mnemonics/language_base.h
@@ -45,7 +45,6 @@
*/
namespace Language
{
- const int unique_prefix_length = 4; /*!< Length of the prefix of all words guaranteed to be unique */
/*!
* \class Base
* \brief A base language class which all languages have to inherit from for
@@ -58,7 +57,7 @@ namespace Language
std::unordered_map<std::string, uint32_t> *word_map; /*!< hash table to find word's index */
std::unordered_map<std::string, uint32_t> *trimmed_word_map; /*!< hash table to find word's trimmed index */
std::string language_name; /*!< Name of language */
- int trim_length; /*!< Number of unique starting characters to trim the wordlist to when matching */
+ uint32_t unique_prefix_length; /*!< Number of unique starting characters to trim the wordlist to when matching */
/*!
* \brief Populates the word maps after the list is ready.
*/
@@ -85,6 +84,7 @@ namespace Language
word_list = new std::vector<std::string>;
word_map = new std::unordered_map<std::string, uint32_t>;
trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
+ unique_prefix_length = 4;
}
/*!
* \brief Returns a pointer to the word list.
@@ -122,9 +122,9 @@ namespace Language
* \brief Returns the number of unique starting characters to be used for matching.
* \return Number of unique starting characters.
*/
- int get_trim_length() const
+ uint32_t get_unique_prefix_length() const
{
- return trim_length;
+ return unique_prefix_length;
}
};
}
diff --git a/src/mnemonics/old_english.h b/src/mnemonics/old_english.h
index 09ac37e66..b91a593b6 100644
--- a/src/mnemonics/old_english.h
+++ b/src/mnemonics/old_english.h
@@ -1681,6 +1681,7 @@ namespace Language
"weapon",
"weary"
});
+ unique_prefix_length = 4;
word_map = new std::unordered_map<std::string, uint32_t>;
trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
language_name = "OldEnglish";
diff --git a/src/mnemonics/portuguese.h b/src/mnemonics/portuguese.h
index c040a0415..6a90659e0 100644
--- a/src/mnemonics/portuguese.h
+++ b/src/mnemonics/portuguese.h
@@ -1679,6 +1679,7 @@ namespace Language
"zenite",
"zumbi"
});
+ unique_prefix_length = 4;
word_map = new std::unordered_map<std::string, uint32_t>;
trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
language_name = "Portuguese";
diff --git a/src/mnemonics/spanish.h b/src/mnemonics/spanish.h
index 8d695a4b1..f71627086 100644
--- a/src/mnemonics/spanish.h
+++ b/src/mnemonics/spanish.h
@@ -1681,6 +1681,7 @@ namespace Language
"ritmo",
"rito"
});
+ unique_prefix_length = 4;
word_map = new std::unordered_map<std::string, uint32_t>;
trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
language_name = "Spanish";
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index e80451cda..036cb64ff 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -111,7 +111,7 @@ namespace cryptonote
}
return true;
}
-#define CHECK_CORE_BUSY() if(!check_core_busy()){res.status = CORE_RPC_STATUS_BUSY;return true;}
+#define CHECK_CORE_BUSY() do { if(!check_core_busy()){res.status = CORE_RPC_STATUS_BUSY;return true;} } while(0)
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::check_core_ready()
{
@@ -121,7 +121,7 @@ namespace cryptonote
}
return check_core_busy();
}
-#define CHECK_CORE_READY() if(!check_core_ready()){res.status = CORE_RPC_STATUS_BUSY;return true;}
+#define CHECK_CORE_READY() do { if(!check_core_ready()){res.status = CORE_RPC_STATUS_BUSY;return true;} } while(0)
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res, connection_context& cntx)
@@ -398,17 +398,19 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
- uint64_t slow_memmem(void* start_buff, size_t buflen,void* pat,size_t patlen)
+ // equivalent of strstr, but with arbitrary bytes (ie, NULs)
+ // This does not differentiate between "not found" and "found at offset 0"
+ uint64_t slow_memmem(const void* start_buff, size_t buflen,const void* pat,size_t patlen)
{
- void* buf = start_buff;
- void* end=(char*)buf+buflen-patlen;
- while((buf=memchr(buf,((char*)pat)[0],buflen)))
+ const void* buf = start_buff;
+ const void* end=(const char*)buf+buflen;
+ if (patlen > buflen || patlen == 0) return 0;
+ while(buflen>0 && (buf=memchr(buf,((const char*)pat)[0],buflen-patlen+1)))
{
- if(buf>end)
- return 0;
if(memcmp(buf,pat,patlen)==0)
- return (char*)buf - (char*)start_buff;
- buf=(char*)buf+1;
+ return (const char*)buf - (const char*)start_buff;
+ buf=(const char*)buf+1;
+ buflen = (const char*)end - (const char*)buf;
}
return 0;
}
diff --git a/src/serialization/binary_archive.h b/src/serialization/binary_archive.h
index baa6707db..56c94684e 100644
--- a/src/serialization/binary_archive.h
+++ b/src/serialization/binary_archive.h
@@ -28,7 +28,7 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
-/* binary_archive.h
+/*! \file binary_archive.h
*
* Portable (low-endian) binary archive */
#pragma once
@@ -41,11 +41,20 @@
#include "common/varint.h"
#include "warnings.h"
-PUSH_WARNINGS
-DISABLE_VS_WARNINGS(4244)
+/* I have no clue what these lines means */
+PUSH_WARNINGS;
+DISABLE_VS_WARNINGS(4244);
//TODO: fix size_t warning in x32 platform
+/*! \struct binary_archive_base
+ *
+ * \brief base for the binary archive type
+ *
+ * \detailed It isn't used outside of this file, which its only
+ * purpse is to define the functions used for the binary_archive. Its
+ * a header, basically. I think it was declared simply to save typing...
+ */
template <class Stream, bool IsSaving>
struct binary_archive_base
{
@@ -56,23 +65,39 @@ struct binary_archive_base
typedef uint8_t variant_tag_type;
explicit binary_archive_base(stream_type &s) : stream_(s) { }
-
+
+ /* definition of standard API functions */
void tag(const char *) { }
void begin_object() { }
void end_object() { }
void begin_variant() { }
void end_variant() { }
- stream_type &stream() { return stream_; }
+ /* I just want to leave a comment saying how this line really shows
+ flaws in the ownership model of many OOP languages, that is all. */
+ stream_type &stream() { return stream_; }
+
protected:
stream_type &stream_;
};
+/* \struct binary_archive
+ *
+ * \brief the actualy binary archive type
+ *
+ * \detailed The boolean template argument /a W is the is_saving
+ * parameter for binary_archive_base.
+ *
+ * The is_saving parameter says whether the archive is being read from
+ * (false) or written to (true)
+ */
template <bool W>
struct binary_archive;
+
template <>
struct binary_archive<false> : public binary_archive_base<std::istream, false>
{
+
explicit binary_archive(stream_type &s) : base_type(s) {
stream_type::streampos pos = stream_.tellg();
stream_.seekg(0, std::ios_base::end);
@@ -86,6 +111,10 @@ struct binary_archive<false> : public binary_archive_base<std::istream, false>
serialize_uint(*(typename boost::make_unsigned<T>::type *)&v);
}
+ /*! \fn serialize_uint
+ *
+ * \brief serializes an unsigned integer
+ */
template <class T>
void serialize_uint(T &v, size_t width = sizeof(T))
{
@@ -96,13 +125,17 @@ struct binary_archive<false> : public binary_archive_base<std::istream, false>
char c;
stream_.get(c);
T b = (unsigned char)c;
- ret += (b << shift);
+ ret += (b << shift); // can this be changed to OR, i think it can.
shift += 8;
}
v = ret;
}
- void serialize_blob(void *buf, size_t len, const char *delimiter="") { stream_.read((char *)buf, len); }
-
+
+ void serialize_blob(void *buf, size_t len, const char *delimiter="")
+ {
+ stream_.read((char *)buf, len);
+ }
+
template <class T>
void serialize_varint(T &v)
{
@@ -115,17 +148,18 @@ struct binary_archive<false> : public binary_archive_base<std::istream, false>
typedef std::istreambuf_iterator<char> it;
tools::read_varint(it(stream_), it(), v); // XXX handle failure
}
+
void begin_array(size_t &s)
{
serialize_varint(s);
}
- void begin_array() { }
+ void begin_array() { }
void delimit_array() { }
void end_array() { }
- void begin_string(const char *delimiter="\"") { }
- void end_string(const char *delimiter="\"") { }
+ void begin_string(const char *delimiter /*="\""*/) { }
+ void end_string(const char *delimiter /*="\""*/) { }
void read_variant_tag(variant_tag_type &t) {
serialize_int(t);
@@ -157,12 +191,14 @@ struct binary_archive<true> : public binary_archive_base<std::ostream, true>
{
for (size_t i = 0; i < sizeof(T); i++) {
stream_.put((char)(v & 0xff));
- if (1 < sizeof(T)) {
- v >>= 8;
- }
+ if (1 < sizeof(T)) v >>= 8;
}
}
- void serialize_blob(void *buf, size_t len, const char *delimiter="") { stream_.write((char *)buf, len); }
+
+ void serialize_blob(void *buf, size_t len, const char *delimiter="")
+ {
+ stream_.write((char *)buf, len);
+ }
template <class T>
void serialize_varint(T &v)
diff --git a/src/serialization/binary_utils.h b/src/serialization/binary_utils.h
index 3b1db3e48..f2cfadd4e 100644
--- a/src/serialization/binary_utils.h
+++ b/src/serialization/binary_utils.h
@@ -34,23 +34,26 @@
#include "binary_archive.h"
namespace serialization {
+ /*! creates a new archive with the passed blob and serializes it into v
+ */
+ template <class T>
+ bool parse_binary(const std::string &blob, T &v)
+ {
+ std::istringstream istr(blob);
+ binary_archive<false> iar(istr);
+ return ::serialization::serialize(iar, v);
+ }
-template <class T>
-bool parse_binary(const std::string &blob, T &v)
-{
- std::istringstream istr(blob);
- binary_archive<false> iar(istr);
- return ::serialization::serialize(iar, v);
-}
-
-template<class T>
-bool dump_binary(T& v, std::string& blob)
-{
- std::stringstream ostr;
- binary_archive<true> oar(ostr);
- bool success = ::serialization::serialize(oar, v);
- blob = ostr.str();
- return success && ostr.good();
-};
+ /*! dumps the data in v into the blob string
+ */
+ template<class T>
+ bool dump_binary(T& v, std::string& blob)
+ {
+ std::stringstream ostr;
+ binary_archive<true> oar(ostr);
+ bool success = ::serialization::serialize(oar, v);
+ blob = ostr.str();
+ return success && ostr.good();
+ };
-} // namespace serialization
+}
diff --git a/src/serialization/json_archive.h b/src/serialization/json_archive.h
index 7a040a126..bd8b16130 100644
--- a/src/serialization/json_archive.h
+++ b/src/serialization/json_archive.h
@@ -28,9 +28,10 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
-/* json_archive.h
+/*! \file json_archive.h
*
- * JSON archive */
+ * \brief JSON archive
+ */
#pragma once
@@ -39,6 +40,12 @@
#include <iostream>
#include <iomanip>
+/*! \struct json_archive_base
+ *
+ * \brief the base class of json archive type
+ *
+ * \detailed contains the basic logic for serializing a json archive
+ */
template <class Stream, bool IsSaving>
struct json_archive_base
{
@@ -48,7 +55,8 @@ struct json_archive_base
typedef const char *variant_tag_type;
- json_archive_base(stream_type &s, bool indent = false) : stream_(s), indent_(indent), object_begin(false), depth_(0) { }
+ json_archive_base(stream_type &s, bool indent = false)
+ : stream_(s), indent_(indent), object_begin(false), depth_(0) { }
void tag(const char *tag) {
if (!object_begin)
@@ -92,6 +100,13 @@ protected:
size_t depth_;
};
+
+/*! \struct json_archive
+ *
+ * \brief a archive using the JSON standard
+ *
+ * \detailed only supports being written to
+ */
template <bool W>
struct json_archive;
diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h
index eab3c43d4..9e534cbc6 100644
--- a/src/serialization/serialization.h
+++ b/src/serialization/serialization.h
@@ -28,9 +28,16 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
-/* serialization.h
+/*! \file serialization.h
+ * \breif Simple DSL AAPI based on
*
- * Simple templated serialization API */
+ * \detailed is_blob_type and has_free_serializer are
+ * both descriptors for dispatching on to the serailize function.
+ *
+ * The API itself defines a domain specific language via dirty macro
+ * hacks. Greenspun's tenth rule is very much in action throughout
+ * this entire code base.
+ */
#pragma once
#include <vector>
@@ -38,14 +45,35 @@
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/integral_constant.hpp>
+/*! \struct is_blob_type
+ *
+ * \brief a descriptor for dispatching serialize
+ */
template <class T>
struct is_blob_type { typedef boost::false_type type; };
+
+/*! \struct has_free_serializer
+ *
+ * \brief a descriptor for dispatching serialize
+ */
template <class T>
struct has_free_serializer { typedef boost::true_type type; };
+/*! \struct serializer
+ *
+ * \brief ... wouldn't a class be better?
+ *
+ * \detailed The logic behind serializing data. Places the archive
+ * data into the supplied parameter. This dispatches based on the
+ * supplied \a T template parameter's traits of is_blob_type or it is
+ * an integral (as defined by the is_integral trait). Depends on the
+ * \a Archive parameter to have overloaded the serialize_blob(T v,
+ * size_t size) and serialize_int(T v) base on which trait it
+ * applied. When the class has neither types, it falls to the
+ * overloaded method do_serialize(Archive ar) in T to do the work.
+ */
template <class Archive, class T>
-struct serializer
-{
+struct serializer{
static bool serialize(Archive &ar, T &v) {
return serialize(ar, v, typename boost::is_integral<T>::type(), typename is_blob_type<T>::type());
}
@@ -65,73 +93,171 @@ struct serializer
}
};
+/*! \fn do_serialize(Archive &ar, T &v)
+ *
+ * \brief just calls the serialize function defined for ar and v...
+ */
template <class Archive, class T>
inline bool do_serialize(Archive &ar, T &v)
{
return ::serializer<Archive, T>::serialize(ar, v);
}
-#ifndef __GNUC__
-#ifndef constexpr
-#define constexpr
-#endif
-#endif
-
-#define BLOB_SERIALIZER(T) \
- template<> struct is_blob_type<T> { typedef boost::true_type type; }
-#define FREE_SERIALIZER(T) \
- template<> struct has_free_serializer<T> { typedef boost::true_type type; }
-#define VARIANT_TAG(A, T, Tg) \
- template <bool W> struct variant_serialization_traits<A<W>, T> { static inline typename A<W>::variant_tag_type get_tag() { return Tg; } }
-#define BEGIN_SERIALIZE() \
- template <bool W, template <bool> class Archive> bool do_serialize(Archive<W> &ar) {
-#define BEGIN_SERIALIZE_OBJECT() \
- template <bool W, template <bool> class Archive> bool do_serialize(Archive<W> &ar) { ar.begin_object(); bool r = do_serialize_object(ar); ar.end_object(); return r; } \
- template <bool W, template <bool> class Archive> bool do_serialize_object(Archive<W> &ar){
-#define PREPARE_CUSTOM_VECTOR_SERIALIZATION(size, vec) ::serialization::detail::prepare_custom_vector_serialization(size, vec, typename Archive<W>::is_saving())
-
-#define END_SERIALIZE() return true;}
-
-
-#define VALUE(f) \
- do { \
- ar.tag(#f); \
- bool r = ::do_serialize(ar, f); \
- if (!r || !ar.stream().good()) return false; \
+// Never used in the code base
+// #ifndef __GNUC__
+// #ifndef constexpr
+// #define constexpr
+// #endif
+// #endif
+
+/* the following add a trait to a set and define the serialization DSL*/
+
+/*! \macro BLOB_SERIALIZER
+ *
+ * \brief makes the type have a blob serializer trait defined
+ */
+#define BLOB_SERIALIZER(T) \
+ template<> \
+ struct is_blob_type<T> { \
+ typedef boost::true_type type; \
+ }
+
+/*! \macro FREE_SERIALIZER
+ *
+ * \brief adds the has_free_serializer to the type
+ */
+#define FREE_SERIALIZER(T) \
+ template<> \
+ struct has_free_serializer<T> { \
+ typedef boost::true_type type; \
+ }
+
+/*! \macro VARIANT_TAG
+ *
+ * \brief Adds the tag \tag to the \a Archive of \a Type
+ */
+#define VARIANT_TAG(Archive, Type, Tag) \
+ template <bool W> \
+ struct variant_serialization_traits<Archive<W>, Type> { \
+ static inline typename Archive<W>::variant_tag_type get_tag() { \
+ return Tag; \
+ } \
+ }
+
+/*! \macro BEGIN_SERIALIZE
+ *
+ * \brief Begins the environment of the DSL
+ * \detailed for describing how to
+ * serialize an of an archive type
+ */
+#define BEGIN_SERIALIZE() \
+ template <bool W, template <bool> class Archive> \
+ bool do_serialize(Archive<W> &ar) {
+
+/*! \macro BEGIN_SERIALIZE_OBJECT
+ *
+ * \brief begins the environment of the DSL
+ * \detailed for described the serialization of an object
+ */
+#define BEGIN_SERIALIZE_OBJECT() \
+ template <bool W, template <bool> class Archive> \
+ bool do_serialize(Archive<W> &ar) { \
+ ar.begin_object(); \
+ bool r = do_serialize_object(ar); \
+ ar.end_object(); \
+ return r; \
+ } \
+ template <bool W, template <bool> class Archive> \
+ bool do_serialize_object(Archive<W> &ar){
+
+/*! \macro PREPARE_CUSTON_VECTOR_SERIALIZATION
+ */
+#define PREPARE_CUSTOM_VECTOR_SERIALIZATION(size, vec) \
+ ::serialization::detail::prepare_custom_vector_serialization(size, vec, typename Archive<W>::is_saving())
+
+/*! \macro END_SERIALIZE
+ * \brief self-explanatory
+ */
+#define END_SERIALIZE() \
+ return true; \
+ }
+
+/*! \macro VALUE(f)
+ * \brief the same as FIELD(f)
+ */
+#define VALUE(f) \
+ do { \
+ ar.tag(#f); \
+ bool r = ::do_serialize(ar, f); \
+ if (!r || !ar.stream().good()) return false; \
} while(0);
-#define FIELD_N(t, f) \
- do { \
- ar.tag(t); \
- bool r = ::do_serialize(ar, f); \
- if (!r || !ar.stream().good()) return false; \
+
+/*! \macro FIELD_N(t,f)
+ *
+ * \brief serializes a field \a f tagged \a t
+ */
+#define FIELD_N(t, f) \
+ do { \
+ ar.tag(t); \
+ bool r = ::do_serialize(ar, f); \
+ if (!r || !ar.stream().good()) return false; \
} while(0);
-#define FIELDS(f) \
- do { \
- bool r = ::do_serialize(ar, f); \
- if (!r || !ar.stream().good()) return false; \
+
+/*! \macro FIELD(f)
+ *
+ * \brief tags the field with the variable name and then serializes it
+ */
+#define FIELD(f) \
+ do { \
+ ar.tag(#f); \
+ bool r = ::do_serialize(ar, f); \
+ if (!r || !ar.stream().good()) return false; \
} while(0);
-#define FIELD(f) \
- do { \
- ar.tag(#f); \
- bool r = ::do_serialize(ar, f); \
- if (!r || !ar.stream().good()) return false; \
+
+/*! \macro FIELDS(f)
+ *
+ * \brief does not add a tag to the serialized value
+ */
+#define FIELDS(f) \
+ do { \
+ bool r = ::do_serialize(ar, f); \
+ if (!r || !ar.stream().good()) return false; \
} while(0);
-#define VARINT_FIELD(f) \
- do { \
- ar.tag(#f); \
- ar.serialize_varint(f); \
- if (!ar.stream().good()) return false; \
+
+/*! \macro VARING_FIELD(f)
+ * \brief tags and serializes the varint \a f
+ */
+#define VARINT_FIELD(f) \
+ do { \
+ ar.tag(#f); \
+ ar.serialize_varint(f); \
+ if (!ar.stream().good()) return false; \
} while(0);
-#define VARINT_FIELD_N(t, f) \
- do { \
- ar.tag(t); \
- ar.serialize_varint(f); \
- if (!ar.stream().good()) return false; \
+
+/*! \macro VARING_FIELD_N(t, f)
+ *
+ * \brief tags (as \a t) and serializes the varint \a f
+ */
+#define VARINT_FIELD_N(t, f) \
+ do { \
+ ar.tag(t); \
+ ar.serialize_varint(f); \
+ if (!ar.stream().good()) return false; \
} while(0);
+
namespace serialization {
+ /*! \namespace detail
+ *
+ * \brief declaration and default definition for the functions used the API
+ *
+ */
namespace detail
{
+ /*! \fn prepare_custom_vector_serialization
+ *
+ * prepares the vector /vec for serialization
+ */
template <typename T>
void prepare_custom_vector_serialization(size_t size, std::vector<T>& vec, const boost::mpl::bool_<true>& /*is_saving*/)
{
@@ -143,32 +269,49 @@ namespace serialization {
vec.resize(size);
}
+ /*! \fn do_check_stream_state
+ *
+ * \brief self explanatory
+ */
template<class Stream>
bool do_check_stream_state(Stream& s, boost::mpl::bool_<true>)
{
return s.good();
}
-
+ /*! \fn do_check_stream_state
+ *
+ * \brief self explanatory
+ *
+ * \detailed Also checks to make sure that the stream is not at EOF
+ */
template<class Stream>
bool do_check_stream_state(Stream& s, boost::mpl::bool_<false>)
{
bool result = false;
if (s.good())
- {
- std::ios_base::iostate state = s.rdstate();
- result = EOF == s.peek();
- s.clear(state);
- }
+ {
+ std::ios_base::iostate state = s.rdstate();
+ result = EOF == s.peek();
+ s.clear(state);
+ }
return result;
}
}
+ /*! \fn check_stream_state
+ *
+ * \brief calls detail::do_check_stream_state for ar
+ */
template<class Archive>
bool check_stream_state(Archive& ar)
{
return detail::do_check_stream_state(ar.stream(), typename Archive::is_saving());
}
+ /*! \fn serialize
+ *
+ * \brief serializes \a v into \a ar
+ */
template <class Archive, class T>
inline bool serialize(Archive &ar, T &v)
{
diff --git a/src/serialization/variant.h b/src/serialization/variant.h
index 3b03f1ba5..9b5d54237 100644
--- a/src/serialization/variant.h
+++ b/src/serialization/variant.h
@@ -28,6 +28,12 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+/*! \file variant.h
+ *
+ * \brief for dealing with variants
+ *
+ * \detailed Variant: OOP Union
+ */
#pragma once
#include <boost/variant/variant.hpp>
@@ -39,11 +45,21 @@
#include <boost/mpl/pop_front.hpp>
#include "serialization.h"
+/*! \struct variant_serialization_triats
+ *
+ * \brief used internally to contain a variant's traits/possible types
+ *
+ * \detailed see the macro VARIANT_TAG in serialization.h:140
+ */
template <class Archive, class T>
struct variant_serialization_traits
{
};
+/*! \struct variant_reader
+ *
+ * \brief reads a variant
+ */
template <class Archive, class Variant, class TBegin, class TEnd>
struct variant_reader
{
@@ -51,9 +67,10 @@ struct variant_reader
typedef typename boost::mpl::next<TBegin>::type TNext;
typedef typename boost::mpl::deref<TBegin>::type current_type;
+ // A tail recursive inline function.... okay...
static inline bool read(Archive &ar, Variant &v, variant_tag_type t)
{
- if (variant_serialization_traits<Archive, current_type>::get_tag() == t) {
+ if(variant_serialization_traits<Archive, current_type>::get_tag() == t) {
current_type x;
if(!::do_serialize(ar, x))
{
@@ -62,12 +79,15 @@ struct variant_reader
}
v = x;
} else {
+ // Tail recursive.... but no mutation is going on. Why?
return variant_reader<Archive, Variant, TNext, TEnd>::read(ar, v, t);
}
return true;
}
};
+// This one just fails when you call it.... okay
+// So the TEnd parameter must be specified/differnt from TBegin
template <class Archive, class Variant, class TBegin>
struct variant_reader<Archive, Variant, TBegin, TBegin>
{
@@ -78,7 +98,6 @@ struct variant_reader<Archive, Variant, TBegin, TBegin>
ar.stream().setstate(std::ios::failbit);
return false;
}
-
};
@@ -93,7 +112,9 @@ struct serializer<Archive<false>, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>>
variant_tag_type t;
ar.begin_variant();
ar.read_variant_tag(t);
- if(!variant_reader<Archive<false>, variant_type, typename boost::mpl::begin<types>::type, typename boost::mpl::end<types>::type>::read(ar, v, t))
+ if(!variant_reader<Archive<false>, variant_type,
+ typename boost::mpl::begin<types>::type,
+ typename boost::mpl::end<types>::type>::read(ar, v, t))
{
ar.stream().setstate(std::ios::failbit);
return false;
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 3fda229b6..533bf8c58 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -70,7 +70,7 @@ target_link_libraries(functional_tests cryptonote_core wallet common crypto ${UN
target_link_libraries(hash-tests crypto)
target_link_libraries(hash-target-tests crypto cryptonote_core)
target_link_libraries(performance_tests cryptonote_core common crypto ${UNBOUND_LIBRARY} ${Boost_LIBRARIES} ${EXTRA_LIBRARIES})
-target_link_libraries(unit_tests gtest_main cryptonote_core wallet crypto common ${UNBOUND_LIBRARY} ${Boost_LIBRARIES} ${EXTRA_LIBRARIES})
+target_link_libraries(unit_tests gtest_main rpc cryptonote_core wallet crypto common ${UNBOUND_LIBRARY} ${Boost_LIBRARIES} ${EXTRA_LIBRARIES})
target_link_libraries(net_load_tests_clt cryptonote_core common crypto gtest_main ${UNBOUND_LIBRARY} ${Boost_LIBRARIES} ${EXTRA_LIBRARIES})
target_link_libraries(net_load_tests_srv cryptonote_core common crypto gtest_main ${UNBOUND_LIBRARY} ${Boost_LIBRARIES} ${EXTRA_LIBRARIES})
diff --git a/tests/unit_tests/mnemonics.cpp b/tests/unit_tests/mnemonics.cpp
new file mode 100644
index 000000000..d17bb7f85
--- /dev/null
+++ b/tests/unit_tests/mnemonics.cpp
@@ -0,0 +1,154 @@
+// Copyright (c) 2014, 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 "gtest/gtest.h"
+#include "mnemonics/electrum-words.h"
+#include "crypto/crypto.h"
+#include <stdlib.h>
+#include <vector>
+#include <time.h>
+#include <iostream>
+#include <boost/algorithm/string.hpp>
+#include "mnemonics/english.h"
+#include "mnemonics/spanish.h"
+#include "mnemonics/portuguese.h"
+#include "mnemonics/japanese.h"
+#include "mnemonics/old_english.h"
+#include "mnemonics/language_base.h"
+#include "mnemonics/singleton.h"
+
+namespace
+{
+ /*!
+ * \brief Returns random index from 0 to max-1
+ * \param max Range maximum
+ * \return required random index
+ */
+ uint32_t get_random_index(int max)
+ {
+ return rand() % max;
+ }
+
+ /*!
+ * \brief Compares vectors for equality
+ * \param expected expected vector
+ * \param present current vector
+ */
+ void compare_vectors(const std::vector<std::string> &expected, const std::vector<std::string> &present)
+ {
+ std::vector<std::string>::const_iterator it1, it2;
+ for (it1 = expected.begin(), it2 = present.begin(); it1 != expected.end() && it2 != present.end();
+ it1++, it2++)
+ {
+ ASSERT_STREQ(it1->c_str(), it2->c_str());
+ }
+ }
+
+ /*!
+ * \brief Tests the given language mnemonics.
+ * \param language A Language instance to test
+ */
+ void test_language(const Language::Base &language)
+ {
+ const std::vector<std::string> &word_list = language.get_word_list();
+ std::string seed = "", return_seed = "";
+ // Generate a random seed without checksum
+ for (int ii = 0; ii < crypto::ElectrumWords::seed_length; ii++)
+ {
+ seed += (word_list[get_random_index(word_list.size())] + ' ');
+ }
+ seed.pop_back();
+ std::cout << "Test seed without checksum:\n";
+ std::cout << seed << std::endl;
+
+ crypto::secret_key key;
+ std::string language_name;
+ bool res;
+ std::vector<std::string> seed_vector, return_seed_vector;
+ std::string checksum_word;
+
+ // Convert it to secret key
+ res = crypto::ElectrumWords::words_to_bytes(seed, key, language_name);
+ ASSERT_EQ(true, res);
+ std::cout << "Detected language: " << language_name << std::endl;
+ ASSERT_STREQ(language.get_language_name().c_str(), language_name.c_str());
+
+ // Convert the secret key back to seed
+ crypto::ElectrumWords::bytes_to_words(key, return_seed, language.get_language_name());
+ ASSERT_EQ(true, res);
+ std::cout << "Returned seed:\n";
+ std::cout << return_seed << std::endl;
+ boost::split(seed_vector, seed, boost::is_any_of(" "));
+ boost::split(return_seed_vector, return_seed, boost::is_any_of(" "));
+
+ // Extract the checksum word
+ checksum_word = return_seed_vector.back();
+ return_seed_vector.pop_back();
+ ASSERT_EQ(seed_vector.size(), return_seed_vector.size());
+ // Ensure that the rest of it is same
+ compare_vectors(seed_vector, return_seed_vector);
+
+ // Append the checksum word to repeat the entire process with a seed with checksum
+ seed += (" " + checksum_word);
+ std::cout << "Test seed with checksum:\n";
+ std::cout << seed << std::endl;
+ res = crypto::ElectrumWords::words_to_bytes(seed, key, language_name);
+ ASSERT_EQ(true, res);
+ std::cout << "Detected language: " << language_name << std::endl;
+ ASSERT_STREQ(language.get_language_name().c_str(), language_name.c_str());
+
+ return_seed = "";
+ crypto::ElectrumWords::bytes_to_words(key, return_seed, language.get_language_name());
+ ASSERT_EQ(true, res);
+ std::cout << "Returned seed:\n";
+ std::cout << return_seed << std::endl;
+
+ seed_vector.clear();
+ return_seed_vector.clear();
+ boost::split(seed_vector, seed, boost::is_any_of(" "));
+ boost::split(return_seed_vector, return_seed, boost::is_any_of(" "));
+ ASSERT_EQ(seed_vector.size(), return_seed_vector.size());
+ compare_vectors(seed_vector, return_seed_vector);
+ }
+}
+
+TEST(mnemonics, all_languages)
+{
+ srand(time(NULL));
+ std::vector<Language::Base*> languages({
+ Language::Singleton<Language::English>::instance(),
+ Language::Singleton<Language::Spanish>::instance(),
+ Language::Singleton<Language::Portuguese>::instance(),
+ Language::Singleton<Language::Japanese>::instance(),
+ });
+
+ for (std::vector<Language::Base*>::iterator it = languages.begin(); it != languages.end(); it++)
+ {
+ test_language(*(*it));
+ }
+}
diff --git a/tests/unit_tests/slow_memmem.cpp b/tests/unit_tests/slow_memmem.cpp
new file mode 100644
index 000000000..2613209f8
--- /dev/null
+++ b/tests/unit_tests/slow_memmem.cpp
@@ -0,0 +1,130 @@
+// Copyright (c) 2014, 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.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include "gtest/gtest.h"
+
+// Both OS X and FreeBSD don't need malloc.h
+#if !defined(__APPLE__) && !defined(__FreeBSD__)
+ #include <malloc.h>
+#endif
+
+//#define TEST_ORIGINAL
+//#define VERBOSE
+
+#ifdef TEST_ORIGINAL
+uint64_t slow_memmem_original(void* start_buff, size_t buflen,void* pat,size_t patlen)
+{
+ void* buf = start_buff;
+ void* end=(char*)buf+buflen-patlen;
+ while((buf=memchr(buf,((char*)pat)[0],buflen)))
+ {
+ if(buf>end)
+ return 0;
+ if(memcmp(buf,pat,patlen)==0)
+ return (char*)buf - (char*)start_buff;
+ buf=(char*)buf+1;
+ }
+ return 0;
+}
+
+#define slow_memmem slow_memmem_original
+#else
+namespace cryptonote {
+ uint64_t slow_memmem(const void* start_buff, size_t buflen,const void* pat,size_t patlen);
+}
+using namespace cryptonote;
+#endif
+
+static const struct {
+ size_t buflen;
+ const char *buf;
+ size_t patlen;
+ const char *pat;
+ uint64_t res;
+} T[]={
+ {0,"",0,"",0},
+ {1,"",0,"",0},
+ {0,"",1,"",0},
+ {1,"x",1,"x",0},
+ {2,"x",1,"",1},
+ {1,"x",1,"",0},
+ {1,"x",2,"",0},
+ {1,"x",2,"x",0},
+ {2,"ax",2,"x",0},
+ {1,"xx",2,"xx",0},
+ {4,"abcd",3,"abc",0},
+ {4,"abcd",3,"bcd",1},
+ {4,"abcd",4,"abcd",0},
+ {4,"abcd",1,"d",3},
+ {4,"abcd",1,"c",2},
+ {4,"abcd",1,"bc",1},
+ {4,"abcd",1,"",0},
+ {3,"abcd",1,"d",0},
+ {5,"aaaab",2,"ab",3},
+ {7,"aaaabab",2,"ab",3},
+ {7,"aaaabab",3,"abc",0},
+ {4,"abcd",2,"cd",2},
+ {3,"abcd",2,"cd",0},
+ {3,"a\0b",1,"",1},
+ {3,"a\0b",2,"\0b",1},
+ {8,"xxxxxxab",3,"xyz",0},
+ {8,"xxxxxxab",6,"abcdef",0},
+ {9,"\0xxxxxab",3,"ab",6},
+ {4,"\0\0a",3,"\0a",1},
+};
+
+TEST(slowmem,Success)
+{
+ size_t n;
+ for (n=0;n<sizeof(T)/sizeof(T[0]);++n) {
+#ifdef VERBOSE
+ printf("%3zu: ",n);
+ fflush(stdout);
+#endif
+ void *buf=malloc(T[n].buflen);
+ memcpy(buf,T[n].buf,T[n].buflen);
+ void *pat=malloc(T[n].patlen);
+ memcpy(pat,T[n].pat,T[n].patlen);
+ uint64_t res=slow_memmem(buf,T[n].buflen,pat,T[n].patlen);
+ free(pat);
+ free(buf);
+ ASSERT_EQ(res,T[n].res);
+ASSERT_EQ(1,1);
+#ifdef VERBOSE
+ if (res!=T[n].res) printf("failed (got %zu, expected %zu)",res,T[n].res); else printf("ok");
+ printf("\n");
+#endif
+ }
+}