Merge remote-tracking branch 'bytecoin/master'

This commit is contained in:
cryptonotefoundation 2015-04-23 20:07:22 +04:00
commit fbd72656b2
472 changed files with 45901 additions and 13393 deletions

View file

@ -12,14 +12,22 @@ if(APPLE)
include_directories(SYSTEM /usr/include/malloc)
endif()
if(MSVC)
include_directories(src/Platform/Windows)
elseif(APPLE)
include_directories(src/Platform/OSX)
else()
include_directories(src/Platform/Linux)
endif()
set(STATIC ${MSVC} CACHE BOOL "Link libraries statically")
if(MSVC)
add_definitions("/bigobj /MP /W3 /GS- /D_CRT_SECURE_NO_WARNINGS /wd4996 /wd4345 /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /DGTEST_HAS_TR1_TUPLE=0 /D_VARIADIC_MAX=8 /FIinline_c.h /D__SSE4_1__")
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Dinline=__inline")
add_definitions("/bigobj /MP /W3 /GS- /D_CRT_SECURE_NO_WARNINGS /wd4996 /wd4345 /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /DGTEST_HAS_TR1_TUPLE=0 /D_VARIADIC_MAX=8 /D__SSE4_1__")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:10485760")
if(STATIC)
foreach(VAR CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE)
foreach(VAR CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO)
string(REPLACE "/MD" "/MT" ${VAR} "${${VAR}}")
endforeach()
endif()
@ -31,11 +39,11 @@ else()
else()
set(ARCH_FLAG "-march=${ARCH}")
endif()
set(WARNINGS "-Wall -Wextra -Wpointer-arith -Wundef -Wvla -Wwrite-strings -Werror -Wno-error=extra -Wno-error=deprecated-declarations -Wno-error=sign-compare -Wno-error=strict-aliasing -Wno-error=type-limits -Wno-unused-parameter -Wno-error=unused-variable -Wno-error=undef -Wno-error=uninitialized")
set(WARNINGS "-Wall -Wextra -Wpointer-arith -Wundef -Wvla -Wwrite-strings -Werror -Wno-error=extra -Wno-error=deprecated-declarations -Wno-error=sign-compare -Wno-error=strict-aliasing -Wno-error=type-limits -Wno-unused-parameter -Wno-error=unused-variable -Wno-error=undef -Wno-error=uninitialized -Wno-error=unused-result")
if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
set(WARNINGS "${WARNINGS} -Wno-error=mismatched-tags -Wno-error=null-conversion -Wno-overloaded-shift-op-parentheses -Wno-error=shift-count-overflow -Wno-error=tautological-constant-out-of-range-compare -Wno-error=unused-private-field -Wno-error=unneeded-internal-declaration")
set(WARNINGS "${WARNINGS} -Wno-error=mismatched-tags -Wno-error=null-conversion -Wno-overloaded-shift-op-parentheses -Wno-error=shift-count-overflow -Wno-error=tautological-constant-out-of-range-compare -Wno-error=unused-private-field -Wno-error=unneeded-internal-declaration -Wno-error=unused-function")
else()
set(WARNINGS "${WARNINGS} -Wlogical-op -Wno-error=maybe-uninitialized")
set(WARNINGS "${WARNINGS} -Wlogical-op -Wno-error=maybe-uninitialized -Wno-error=clobbered -Wno-error=unused-but-set-variable")
endif()
if(MINGW)
set(WARNINGS "${WARNINGS} -Wno-error=unused-value")
@ -46,13 +54,7 @@ else()
endif()
set(C_WARNINGS "-Waggregate-return -Wnested-externs -Wold-style-definition -Wstrict-prototypes")
set(CXX_WARNINGS "-Wno-reorder -Wno-missing-field-initializers")
try_compile(STATIC_ASSERT_RES "${CMAKE_CURRENT_BINARY_DIR}/static-assert" "${CMAKE_CURRENT_SOURCE_DIR}/utils/test-static-assert.c" COMPILE_DEFINITIONS "-std=c11")
if(STATIC_ASSERT_RES)
set(STATIC_ASSERT_FLAG "")
else()
set(STATIC_ASSERT_FLAG "-Dstatic_assert=_Static_assert")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_FLAG} ${WARNINGS} ${C_WARNINGS} ${ARCH_FLAG} -maes")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_GNU_SOURCE ${MINGW_FLAG} ${WARNINGS} ${C_WARNINGS} ${ARCH_FLAG} -maes")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -D_GNU_SOURCE ${MINGW_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${ARCH_FLAG} -maes")
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGTEST_HAS_TR1_TUPLE=0")
@ -83,7 +85,7 @@ if(STATIC)
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_STATIC_RUNTIME ON)
endif()
find_package(Boost 1.53 REQUIRED COMPONENTS system filesystem thread date_time chrono regex serialization program_options)
find_package(Boost 1.53 REQUIRED COMPONENTS system filesystem thread date_time chrono regex serialization program_options coroutine context)
if((${Boost_MAJOR_VERSION} EQUAL 1) AND (${Boost_MINOR_VERSION} EQUAL 54))
message(SEND_ERROR "Boost version 1.54 is unsupported, more details are available here http://goo.gl/RrCFmA")
endif()
@ -122,6 +124,7 @@ else()
endif()
endif()
add_subdirectory(contrib)
add_subdirectory(external)
add_subdirectory(src)
add_subdirectory(tests)

View file

@ -1,4 +1,5 @@
This is the reference code for [CryptoNote](https://cryptonote.org) cryptocurrency protocol.
* Launch your own CryptoNote currency: [CryptoNote Starter](https://cryptonotestarter.org/)
* CryptoNote reference implementation: [CryptoNoteCoin](https://cryptonote-coin.org)
* Discussion board and support: [CryptoNote Forum](https://forum.cryptonote.org)
@ -15,18 +16,18 @@ This is the reference code for [CryptoNote](https://cryptonote.org) cryptocurren
### First step. Give a name to your coin
**Good name must be unique.*** Check uniqueness with [google](http://google.com) and [Map of Coins](mapofcoins.com) or any other similar service.
**Good name must be unique.** Check uniqueness with [google](http://google.com) and [Map of Coins](mapofcoins.com) or any other similar service.
Name must be specified twice:
**1. in file src/cryptonote_config.h** - CRYPTONOTE_NAME macro
**1. in file src/cryptonote_config.h** - `CRYPTONOTE_NAME` constant
Example:
```
#define CRYPTONOTE_NAME "furiouscoin"
const char CRYPTONOTE_NAME[] = "furiouscoin";
```
**2. in CMakeList.txt file** - set_property(TARGET daemon PROPERTY OUTPUT_NAME "YOURCOINNAMEd")
**2. in src/CMakeList.txt file** - set_property(TARGET daemon PROPERTY OUTPUT_NAME "YOURCOINNAME**d**")
Example:
```
@ -40,22 +41,22 @@ set_property(TARGET daemon PROPERTY OUTPUT_NAME "furiouscoind")
**1. Total money supply** (src/cryptonote_config.h)
Total amount of coins to be emitted. Most of CryptoNote based coins use `(uint64_t)(-1)` (equals to 18446744073709551616). You can define number explicitly (for example UINT64_C(858986905600000000)).
Total amount of coins to be emitted. Most of CryptoNote based coins use `(uint64_t)(-1)` (equals to 18446744073709551616). You can define number explicitly (for example `UINT64_C(858986905600000000)`).
Example:
```
#define MONEY_SUPPLY ((uint64_t)(-1))
const uint64_t MONEY_SUPPLY = (uint64_t)(-1);
```
**2. Emission curve** (src/cryptonote_config.h)
Be default CryptoNote provides emission formula with slight decrease of block reward with each block. This is different from Bitcoin where block reward halves every 4 years.
EMISSION_SPEED_FACTOR macro defines emission curve slope. This parameter is required to calulate block reward.
`EMISSION_SPEED_FACTOR` constant defines emission curve slope. This parameter is required to calulate block reward.
Example:
```
#define EMISSION_SPEED_FACTOR (18)
const unsigned EMISSION_SPEED_FACTOR = 18;
```
**3. Difficulty target** (src/cryptonote_config.h)
@ -72,19 +73,19 @@ For most coins difficulty target is 60 or 120 seconds.
Example:
```
#define DIFFICULTY_TARGET 120
const uint64_t DIFFICULTY_TARGET = 120;
```
**4. Block reward formula**
In case you are not satisfied with CryptoNote default implementation of block reward logic you can also change it. The implementation is in src/cryptonote_core/cryptonote_basic_impl.cpp:
In case you are not satisfied with CryptoNote default implementation of block reward logic you can also change it. The implementation is in `src/cryptonote_core/Currency.cpp`:
```
bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward)
bool Currency::getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, uint64_t& reward, int64_t& emissionChange) const
```
This function has two parts:
- basic block reward calculation: `uint64_t base_reward = (MONEY_SUPPLY - already_generated_coins) >> EMISSION_SPEED_FACTOR;`
- basic block reward calculation: `uint64_t baseReward = (m_moneySupply - alreadyGeneratedCoins) >> m_emissionSpeedFactor;`
- big block penalty calculation: this is the way CryptoNote protects the block chain from transaction flooding attacks and preserves opportunities for organic network growth at the same time.
Only the first part of this function is directly related to the emission logic. You can change it the way you want. See MonetaVerde and DuckNote as the examples where this function is modified.
@ -105,8 +106,8 @@ It's better to choose ports that aren't used by other software or coins. See kno
Example:
```
#define P2P_DEFAULT_PORT 17236
#define RPC_DEFAULT_PORT 18236
const int P2P_DEFAULT_PORT = 17236;
const int RPC_DEFAULT_PORT = 18236;
```
@ -114,72 +115,84 @@ Example:
This identifier is used in network packages in order not to mix two different cryptocoin networks. Change all the bytes to random values for your network:
```
const static boost::uuids::uuid CRYPTONOTE_NETWORK = { { 0xA1 ,0x1A, 0xA1, 0x1A , 0xA1, 0x0A , 0xA1, 0x0A, 0xA0, 0x1A, 0xA0, 0x1A, 0xA0, 0x1A, 0xA1, 0x1A} };
const static boost::uuids::uuid CRYPTONOTE_NETWORK = { { 0xA1, 0x1A, 0xA1, 0x1A, 0xA1, 0x0A, 0xA1, 0x0A, 0xA0, 0x1A, 0xA0, 0x1A, 0xA0, 0x1A, 0xA1, 0x1A } };
```
**3. Seed nodes** (src/p2p/net_node.inl)
**3. Seed nodes** (src/cryptonote_config.h)
Add ip addresses of your seed nodes to the beginning of `node_server<t_payload_net_handler>::init(const boost::program_options::variables_map& vm)` function.
Add IP addresses of your seed nodes.
Example:
```
ADD_HARDCODED_SEED_NODE("111.11.11.11:17236");
ADD_HARDCODED_SEED_NODE("222.22.22.22:17236");
const std::initializer_list<const char*> SEED_NODES = {
"111.11.11.11:17236",
"222.22.22.22:17236",
};
```
### Fourth step. Transaction fee and related parameters
**1. Default transaction fee** (src/cryptonote_config.h)
**1. Minimum transaction fee** (src/cryptonote_config.h)
Zero default fee can lead to transaction flooding. Transactions cheaper than the default transaction fee wouldn't be accepted by daemons. 100000 value for DEFAULT_FEE is usually enough.
Zero minimum fee can lead to transaction flooding. Transactions cheaper than the minimum transaction fee wouldn't be accepted by daemons. 100000 value for `MINIMUM_FEE` is usually enough.
Example:
```
#define DEFAULT_FEE 100000
const uint64_t MINIMUM_FEE = 100000;
```
**2. Penalty free block size** (src/cryptonote_config.h)
CryptoNote protects chain from tx flooding by reducing block reward for blocks larger than the median block size. However, this rule applies for blocks larger than CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE bytes.
CryptoNote protects chain from tx flooding by reducing block reward for blocks larger than the median block size. However, this rule applies for blocks larger than `CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE` bytes.
Example:
```
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE 20000
const size_t CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE = 20000;
```
### Fifth step. Genesis block
### Fifth step. Address prefix
You may choose a letter (in some cases several letters) all the coin's public addresses will start with. It is defined by `CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX` constant. Since the rules for address prefixes are nontrivial you may use the [prefix generator tool](https://cryptonotestarter.org/tools.html).
Example:
```
const uint64_t CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 0xe9; // addresses start with "f"
```
### Sixth step. Genesis block
**1. Build the binaries with blank genesis tx hex** (src/cryptonote_config.h)
You should leave #define GENESIS_COINBASE_TX_HEX blank and compile the binaries without it.
You should leave `const char GENESIS_COINBASE_TX_HEX[]` blank and compile the binaries without it.
Example:
```
#define GENESIS_COINBASE_TX_HEX ""
const char GENESIS_COINBASE_TX_HEX[] = "";
```
**2. Start the daemon to print out the genesis block**
Run your daemon with --print-genesis-tx argument. It will print out the genesis block coinbase transaction hash.
Run your daemon with `--print-genesis-tx` argument. It will print out the genesis block coinbase transaction hash.
Example:
```
cryptonotecoind --print-genesis-tx
furiouscoind --print-genesis-tx
```
**3. Copy the printed transaction hash** (src/cryptonote_config.h)
Copy the tx hash that has been printed by the daemon to GENESIS_COINBASE_TX_HEX in /src/cryptonote_config.h
Copy the tx hash that has been printed by the daemon to `GENESIS_COINBASE_TX_HEX` in `src/cryptonote_config.h`
Example:
```
#define GENESIS_COINBASE_TX_HEX "013c01ff0001ffff...785a33d9ebdba68b0"
const char GENESIS_COINBASE_TX_HEX[] = "013c01ff0001ffff...785a33d9ebdba68b0";
```
@ -192,25 +205,27 @@ Recompile everything again. Your coin code is ready now. Make an announcement fo
### On *nix
Dependencies: GCC 4.7.3 or later, CMake 2.8.6 or later, and Boost 1.53 or later (except 1.54, more details here: http://goo.gl/RrCFmA).
Dependencies: GCC 4.7.3 or later, CMake 2.8.6 or later, and Boost 1.55.
You may download them from:
* http://gcc.gnu.org/
* http://www.cmake.org/
* http://www.boost.org/
*
Alternatively, it may be possible to install them using a package manager.
* Alternatively, it may be possible to install them using a package manager.
To build, change to a directory where this file is located, and run `make'. The resulting executables can be found in build/release/src.
To build, change to a directory where this file is located, and run `make`. The resulting executables can be found in `build/release/src`.
**Advanced options:**
* Parallel build: run `make -j<number of threads>` instead of `make`.
* Debug build: run `make build-debug`.
* Test suite: run `make test-release` to run tests in addition to building. Running `make test-debug` will do the same to the debug version.
* Building with Clang: it may be possible to use Clang instead of GCC, but this may not work everywhere. To build, run `export CC=clang CXX=clang++` before running `make`.
### On Windows
Dependencies: MSVC 2012 or later, CMake 2.8.6 or later, and Boost 1.53 or later. You may download them from:
Dependencies: MSVC 2012 or later, CMake 2.8.6 or later, and Boost 1.55. You may download them from:
* http://www.microsoft.com/
* http://www.cmake.org/
* http://www.boost.org/

View file

@ -1,4 +1,25 @@
Release notes 0.8.11
Release notes 1.0.2
- Multisignature with API
- Low level API
- High level API improvements
- Updated block reward scheme
- Dynamic maximum block size limit
- Instant transaction pool notifications
- Transaction priority based on tx fee
- Transactions are returned from tx pools after 24 hours
- Fully refactored simplewallet
- Transaction history for simplewallet
- Reset command for simplewallet
- Faster wallet refresh
- Further optimization in daemon RAM consumption
- Various network health updates
- Config and CryptoNote forking how-to update
Release notes 1.0.1
- High level API implementation
- CryptoNight hash function optimization

7
contrib/CMakeLists.txt Normal file
View file

@ -0,0 +1,7 @@
file(GLOB_RECURSE EPEE epee/include/*)
source_group(epee FILES ${EPEE})
add_library(epee ${EPEE})
set_property(TARGET epee PROPERTY FOLDER "external")

View file

@ -35,7 +35,4 @@
#define BOOST_FILESYSTEM_VERSION 3
#define ENABLE_RELEASE_LOGGING
#include "log_opt_defs.h"
#include "misc_log_ex.h"

View file

@ -32,6 +32,11 @@
#include <mutex>
#include <thread>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include "string_tools.h"
namespace epee
{
class async_stdin_reader
@ -294,7 +299,7 @@ namespace epee
bool start_default_console(t_server* ptsrv, t_handler handlr, const std::string& prompt, const std::string& usage = "")
{
std::shared_ptr<async_console_handler> console_handler = std::make_shared<async_console_handler>();
boost::thread([=](){console_handler->run<t_server, t_handler>(ptsrv, handlr, prompt, usage);}).detach();
std::thread([=](){console_handler->run<t_server, t_handler>(ptsrv, handlr, prompt, usage);}).detach();
return true;
}
@ -314,46 +319,24 @@ namespace epee
bool run_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, const std::string& prompt, const std::string& usage = "")
{
async_console_handler console_handler;
return console_handler.run(ptsrv, boost::bind<bool>(no_srv_param_adapter<t_server, t_handler>, _1, _2, handlr), prompt, usage);
return console_handler.run(ptsrv, std::bind<bool>(no_srv_param_adapter<t_server, t_handler>, std::placeholders::_1, std::placeholders::_2, handlr), prompt, usage);
}
template<class t_server, class t_handler>
bool start_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, const std::string& prompt, const std::string& usage = "")
{
boost::thread( boost::bind(run_default_console_handler_no_srv_param<t_server, t_handler>, ptsrv, handlr, prompt, usage) );
std::thread( std::bind(run_default_console_handler_no_srv_param<t_server, t_handler>, ptsrv, handlr, prompt, usage) );
return true;
}
/*template<class a>
bool f(int i, a l)
{
return true;
}*/
/*
template<class chain_handler>
bool default_console_handler2(chain_handler ch_handler, const std::string usage)
*/
/*template<class t_handler>
bool start_default_console2(t_handler handlr, const std::string& usage = "")
{
//std::string usage_local = usage;
boost::thread( boost::bind(default_console_handler2<t_handler>, handlr, usage) );
//boost::function<bool ()> p__ = boost::bind(f<t_handler>, 1, handlr);
//boost::function<bool ()> p__ = boost::bind(default_console_handler2<t_handler>, handlr, usage);
//boost::thread tr(p__);
return true;
}*/
/************************************************************************/
/* */
/************************************************************************/
class console_handlers_binder
{
typedef boost::function<bool (const std::vector<std::string> &)> console_command_handler;
typedef std::function<bool (const std::vector<std::string> &)> console_command_handler;
typedef std::map<std::string, std::pair<console_command_handler, std::string> > command_handlers_map;
std::unique_ptr<boost::thread> m_console_thread;
std::unique_ptr<std::thread> m_console_thread;
command_handlers_map m_command_handlers;
async_console_handler m_console_handler;
public:
@ -396,16 +379,9 @@ namespace epee
return process_command_vec(cmd_v);
}
/*template<class t_srv>
bool start_handling(t_srv& srv, const std::string& usage_string = "")
{
start_default_console_handler_no_srv_param(&srv, boost::bind(&console_handlers_binder::process_command_str, this, _1));
return true;
}*/
bool start_handling(const std::string& prompt, const std::string& usage_string = "")
{
m_console_thread.reset(new boost::thread(boost::bind(&console_handlers_binder::run_handling, this, prompt, usage_string)));
m_console_thread.reset(new std::thread(std::bind(&console_handlers_binder::run_handling, this, prompt, usage_string)));
m_console_thread->detach();
return true;
}
@ -417,14 +393,8 @@ namespace epee
bool run_handling(const std::string& prompt, const std::string& usage_string)
{
return m_console_handler.run(boost::bind(&console_handlers_binder::process_command_str, this, _1), prompt, usage_string);
return m_console_handler.run(std::bind(&console_handlers_binder::process_command_str, this, std::placeholders::_1), prompt, usage_string);
}
/*template<class t_srv>
bool run_handling(t_srv& srv, const std::string& usage_string)
{
return run_default_console_handler_no_srv_param(&srv, boost::bind<bool>(&console_handlers_binder::process_command_str, this, _1), usage_string);
}*/
};
/* work around because of broken boost bind */
@ -438,13 +408,14 @@ namespace epee
public:
bool start_handling(t_server* psrv, const std::string& prompt, const std::string& usage_string = "")
{
boost::thread(boost::bind(&srv_console_handlers_binder<t_server>::run_handling, this, psrv, prompt, usage_string)).detach();
std::thread(std::bind(&srv_console_handlers_binder<t_server>::run_handling, this, psrv, prompt, usage_string)).detach();
return true;
}
bool run_handling(t_server* psrv, const std::string& prompt, const std::string& usage_string)
{
return m_console_handler.run(psrv, boost::bind(&srv_console_handlers_binder<t_server>::process_command_str, this, _1, _2), prompt, usage_string);
return m_console_handler.run(psrv, std::bind(&srv_console_handlers_binder<t_server>::process_command_str, this,
std::placeholders::_1, std::placeholders::_2), prompt, usage_string);
}
void stop_handling()

View file

@ -28,10 +28,7 @@
#ifndef _FILE_IO_UTILS_H_
#define _FILE_IO_UTILS_H_
//#include <sys/types.h>
//#include <sys/stat.h>
#include <fstream>
#include <iostream>
#include <boost/filesystem.hpp>
@ -75,7 +72,7 @@ namespace file_io_utils
#ifdef BOOST_LEXICAL_CAST_INCLUDED
inline
bool get_not_used_filename(const std::string& folder, OUT std::string& result_name)
bool get_not_used_filename(const std::string& folder, std::string& result_name)
{
DWORD folder_attr = ::GetFileAttributesA(folder.c_str());
if(folder_attr == INVALID_FILE_ATTRIBUTES)
@ -302,7 +299,7 @@ namespace file_io_utils
}
*/
inline
bool get_file_time(const std::string& path_to_file, OUT time_t& ft)
bool get_file_time(const std::string& path_to_file, time_t& ft)
{
boost::system::error_code ec;
ft = boost::filesystem::last_write_time(boost::filesystem::path(path_to_file), ec);
@ -408,7 +405,7 @@ namespace file_io_utils
}
*/
#ifdef WINDOWS_PLATFORM
inline bool get_folder_content(const std::string& path, std::list<WIN32_FIND_DATAA>& OUT target_list)
inline bool get_folder_content(const std::string& path, std::list<WIN32_FIND_DATAA>& target_list)
{
WIN32_FIND_DATAA find_data = {0};
HANDLE hfind = ::FindFirstFileA((path + "\\*.*").c_str(), &find_data);
@ -426,7 +423,7 @@ namespace file_io_utils
return true;
}
#endif
inline bool get_folder_content(const std::string& path, std::list<std::string>& OUT target_list, bool only_files = false)
inline bool get_folder_content(const std::string& path, std::list<std::string>& target_list, bool only_files = false)
{
try
{

View file

@ -37,6 +37,7 @@
#include <boost/uuid/random_generator.hpp>
#include "misc_os_dependent.h"
#include "pragma_comp_defs.h"
namespace epee
{

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,97 @@
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * 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.
// * Neither the name of the Andrey N. Sabelnikov 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 OWNER 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 "misc_os_dependent.h"
#include <iostream>
#include <boost/lexical_cast.hpp>
#ifdef __MACH__
#include <mach/clock.h>
#include <mach/mach.h>
#endif
namespace epee
{
namespace misc_utils
{
uint64_t get_tick_count()
{
#if defined(_MSC_VER)
return ::GetTickCount64();
#elif defined(__MACH__)
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
return (mts.tv_sec * 1000) + (mts.tv_nsec/1000000);
#else
struct timespec ts;
if(clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
return 0;
}
return (ts.tv_sec * 1000) + (ts.tv_nsec/1000000);
#endif
}
int call_sys_cmd(const std::string& cmd)
{
std::cout << "# " << cmd << std::endl;
FILE * fp ;
//char tstCommand[] ="ls *";
char path[1000] = {0};
#if !defined(__GNUC__)
fp = _popen(cmd.c_str(), "r");
#else
fp = popen(cmd.c_str(), "r");
#endif
while ( fgets( path, 1000, fp ) != NULL )
std::cout << path;
#if !defined(__GNUC__)
_pclose(fp);
#else
pclose(fp);
#endif
return 0;
}
std::string get_thread_string_id()
{
#if defined(_MSC_VER)
return boost::lexical_cast<std::string>(GetCurrentThreadId());
#elif defined(__GNUC__)
return boost::lexical_cast<std::string>(pthread_self());
#endif
}
}
}

View file

@ -23,86 +23,30 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#pragma once
#include <cstdint>
#include <string>
#ifdef WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
//#ifdef _WIN32_WINNT
// #undef _WIN32_WINNT
// #define _WIN32_WINNT 0x0600
//#endif
#include <windows.h>
#if !defined(NOMINMAX)
#define NOMINMAX 1
#endif // !defined(NOMINMAX)
#include <windows.h>
#endif
#ifdef __MACH__
#include <mach/clock.h>
#include <mach/mach.h>
#endif
#pragma once
namespace epee
{
namespace misc_utils
{
inline uint64_t get_tick_count()
{
#if defined(_MSC_VER)
return ::GetTickCount64();
#elif defined(__MACH__)
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
return (mts.tv_sec * 1000) + (mts.tv_nsec/1000000);
#else
struct timespec ts;
if(clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
return 0;
}
return (ts.tv_sec * 1000) + (ts.tv_nsec/1000000);
#endif
}
inline int call_sys_cmd(const std::string& cmd)
{
std::cout << "# " << cmd << std::endl;
FILE * fp ;
//char tstCommand[] ="ls *";
char path[1000] = {0};
#if !defined(__GNUC__)
fp = _popen(cmd.c_str(), "r");
#else
fp = popen(cmd.c_str(), "r");
#endif
while ( fgets( path, 1000, fp ) != NULL )
std::cout << path;
#if !defined(__GNUC__)
_pclose(fp);
#else
pclose(fp);
#endif
return 0;
}
inline std::string get_thread_string_id()
{
#if defined(_MSC_VER)
return boost::lexical_cast<std::string>(GetCurrentThreadId());
#elif defined(__GNUC__)
return boost::lexical_cast<std::string>(pthread_self());
#endif
}
uint64_t get_tick_count();
int call_sys_cmd(const std::string& cmd);
std::string get_thread_string_id();
}
}

View file

@ -30,10 +30,12 @@
#include <boost/lambda/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/uuid/random_generator.hpp>
#include <boost/chrono.hpp>
#include <boost/utility/value_init.hpp>
#include <boost/asio/deadline_timer.hpp>
#include "include_base_utils.h"
#include "misc_language.h"
#include "pragma_comp_defs.h"
@ -304,7 +306,7 @@ PRAGMA_WARNING_DISABLE_VS(4355)
if(m_send_que.size() > ABSTRACT_SERVER_SEND_QUE_MAX_COUNT)
{
send_guard.unlock();
// LOG_ERROR("send que size is more than ABSTRACT_SERVER_SEND_QUE_MAX_COUNT(" << ABSTRACT_SERVER_SEND_QUE_MAX_COUNT << "), shutting down connection");
LOG_WARNING("send que size is more than ABSTRACT_SERVER_SEND_QUE_MAX_COUNT(" << ABSTRACT_SERVER_SEND_QUE_MAX_COUNT << "), shutting down connection", LOG_LEVEL_2);
close();
return false;
}

View file

@ -31,6 +31,7 @@
#define _HTTP_SERVER_H_
#include <string>
#include "include_base_utils.h"
#include "net_utils_base.h"
#include "to_nonconst_iterator.h"
#include "http_base.h"

View file

@ -28,8 +28,10 @@
#include <boost/regex.hpp>
#include <boost/lexical_cast.hpp>
#include "http_protocol_handler.h"
#include "include_base_utils.h"
#include "reg_exp_definer.h"
#include "string_tools.h"
#include "time_helper.h"
#include "file_io_utils.h"
#include "net_parse_helpers.h"

View file

@ -28,6 +28,7 @@
#pragma once
#include "http_base.h"
#include "jsonrpc_structs.h"
#include "misc_os_dependent.h"
#include "storages/portable_storage.h"
#include "storages/portable_storage_template_helper.h"
@ -59,7 +60,7 @@
else if(query_info.m_URI == s_pattern) \
{ \
handled = true; \
uint64_t ticks = misc_utils::get_tick_count(); \
uint64_t ticks = epee::misc_utils::get_tick_count(); \
boost::value_initialized<command_type::request> req; \
bool parse_res = epee::serialization::load_t_from_json(static_cast<command_type::request&>(req), query_info.m_body); \
CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse json: \r\n" << query_info.m_body); \
@ -84,11 +85,11 @@
else if(query_info.m_URI == s_pattern) \
{ \
handled = true; \
uint64_t ticks = misc_utils::get_tick_count(); \
uint64_t ticks = epee::misc_utils::get_tick_count(); \
boost::value_initialized<command_type::request> req; \
bool parse_res = epee::serialization::load_t_from_binary(static_cast<command_type::request&>(req), query_info.m_body); \
CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse bin body data, body size=" << query_info.m_body.size()); \
uint64_t ticks1 = misc_utils::get_tick_count(); \
uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
boost::value_initialized<command_type::response> resp;\
if(!callback_f(static_cast<command_type::request&>(req), static_cast<command_type::response&>(resp), m_conn_context)) \
{ \
@ -97,7 +98,7 @@
response_info.m_response_comment = "Internal Server Error"; \
return true; \
} \
uint64_t ticks2 = misc_utils::get_tick_count(); \
uint64_t ticks2 = epee::misc_utils::get_tick_count(); \
epee::serialization::store_t_to_binary(static_cast<command_type::response&>(resp), response_info.m_body); \
uint64_t ticks3 = epee::misc_utils::get_tick_count(); \
response_info.m_mime_tipe = " application/octet-stream"; \

View file

@ -25,6 +25,7 @@
//
#pragma once
#include <boost/asio/deadline_timer.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/interprocess/detail/atomic.hpp>
#include <boost/smart_ptr/make_shared.hpp>

View file

@ -29,6 +29,7 @@
#pragma once
#include "http_base.h"
#include "include_base_utils.h"
#include "reg_exp_definer.h"

View file

@ -36,6 +36,11 @@
#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24))
#endif
namespace boost {
namespace asio {
class io_service;
}
}
namespace epee
{

View file

@ -28,6 +28,8 @@
#ifndef _PROFILE_TOOLS_H_
#define _PROFILE_TOOLS_H_
#include <boost/date_time/posix_time/ptime.hpp>
namespace epee
{

View file

@ -26,6 +26,12 @@
#pragma once
#include <cstdint>
#include <vector>
#include <boost/mpl/contains.hpp>
#include <boost/mpl/vector.hpp>
namespace epee
{
namespace serialization

View file

@ -44,38 +44,11 @@ public:
initializer()
{
to_initialize::init();
//get_set_is_initialized(true, true);
}
~initializer()
{
to_initialize::un_init();
//get_set_is_uninitialized(true, true);
}
/*static inline bool is_initialized()
{
return get_set_is_initialized();
}
static inline bool is_uninitialized()
{
return get_set_is_uninitialized();
}
private:
static inline bool get_set_is_initialized(bool need_to_set = false, bool val_to_set= false)
{
static bool val_is_initialized = false;
if(need_to_set)
val_is_initialized = val_to_set;
return val_is_initialized;
}
static inline bool get_set_is_uninitialized(bool need_to_set = false, bool val_to_set = false)
{
static bool val_is_uninitialized = false;
if(need_to_set)
val_is_uninitialized = val_to_set;
return val_is_uninitialized;
}*/
};
}

View file

@ -114,6 +114,7 @@ namespace epee
const_cast<t_arg&>(out_struct).store(stg);//TODO: add true const support to searilzation
std::string buff_to_send, buff_to_recv;
stg.store_to_binary(buff_to_send);
int res = transport.invoke_async(command, buff_to_send, conn_id, [cb, command](int code, const std::string& buff, typename t_transport::connection_context& context)->bool
{
t_result result_struct = AUTO_VAL_INIT(result_struct);
@ -131,6 +132,7 @@ namespace epee
return false;
}
result_struct.load(stg_ret);
cb(code, result_struct, context);
return true;
}, inv_timeout);

View file

@ -150,7 +150,8 @@ namespace epee
m_root.m_entries.clear();
if(source.size() < sizeof(storage_block_header))
{
LOG_ERROR("portable_storage: wrong binary format, packet size = " << source.size() << " less than expected sizeof(storage_block_header)=" << sizeof(storage_block_header));
LOG_WARNING("portable_storage: wrong binary format, packet size = " << source.size() << " less than expected sizeof(storage_block_header)="
<< sizeof(storage_block_header), LOG_LEVEL_2)
return false;
}
storage_block_header* pbuff = (storage_block_header*)source.data();
@ -158,12 +159,12 @@ namespace epee
pbuff->m_signature_b != PORTABLE_STORAGE_SIGNATUREB
)
{
LOG_ERROR("portable_storage: wrong binary format - signature missmatch");
LOG_WARNING("portable_storage: wrong binary format - signature missmatch", LOG_LEVEL_2);
return false;
}
if(pbuff->m_ver != PORTABLE_STORAGE_FORMAT_VER)
{
LOG_ERROR("portable_storage: wrong binary format - unknown format ver = " << pbuff->m_ver);
LOG_WARNING("portable_storage: wrong binary format - unknown format ver = " << pbuff->m_ver, LOG_LEVEL_2);
return false;
}
TRY_ENTRY();

View file

@ -25,6 +25,10 @@
//
#pragma once
#include <boost/algorithm/string/predicate.hpp>
#include <boost/lexical_cast.hpp>
#include "parserse_base_utils.h"
#include "file_io_utils.h"
@ -365,6 +369,7 @@ namespace epee
}
catch(const std::exception& ex)
{
(void)(ex);
LOG_PRINT_RED_L0("Failed to parse json, what: " << ex.what());
return false;
}

View file

@ -30,6 +30,7 @@
#include "misc_language.h"
#include "portable_storage_base.h"
#include "pragma_comp_defs.h"
namespace epee
{

View file

@ -24,10 +24,10 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#pragma once
#include <boost/numeric/conversion/bounds.hpp>
#include "misc_language.h"
#include "portable_storage_base.h"
#include "warnings.h"

View file

@ -0,0 +1,487 @@
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * 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.
// * Neither the name of the Andrey N. Sabelnikov 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 OWNER 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 "string_tools.h"
#include <locale>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/asio.hpp>
#include <boost/algorithm/string/compare.hpp>
#include <boost/algorithm/string.hpp>
#ifdef WINDOWS_PLATFORM
#pragma comment (lib, "Rpcrt4.lib")
#endif
namespace epee
{
namespace string_tools
{
std::wstring get_str_from_guid(const boost::uuids::uuid& rid)
{
return boost::lexical_cast<std::wstring>(rid);
}
//----------------------------------------------------------------------------
std::string get_str_from_guid_a(const boost::uuids::uuid& rid)
{
return boost::lexical_cast<std::string>(rid);
}
//----------------------------------------------------------------------------
bool get_guid_from_string( boost::uuids::uuid& inetifer, std::wstring str_id)
{
if(str_id.size() < 36)
return false;
if('{' == *str_id.begin())
str_id.erase(0, 1);
if('}' == *(--str_id.end()))
str_id.erase(--str_id.end());
try
{
inetifer = boost::lexical_cast<boost::uuids::uuid>(str_id);
return true;
}
catch(...)
{
return false;
}
}
//----------------------------------------------------------------------------
bool get_guid_from_string(OUT boost::uuids::uuid& inetifer, const std::string& str_id)
{
std::string local_str_id = str_id;
if(local_str_id.size() < 36)
return false;
if('{' == *local_str_id.begin())
local_str_id.erase(0, 1);
if('}' == *(--local_str_id.end()))
local_str_id.erase(--local_str_id.end());
try
{
inetifer = boost::lexical_cast<boost::uuids::uuid>(local_str_id);
return true;
}
catch(...)
{
return false;
}
}
//----------------------------------------------------------------------------
//#ifdef _WINSOCK2API_
std::string get_ip_string_from_int32(uint32_t ip)
{
in_addr adr;
adr.s_addr = ip;
const char* pbuf = inet_ntoa(adr);
if(pbuf)
return pbuf;
else
return "[failed]";
}
//----------------------------------------------------------------------------
bool get_ip_int32_from_string(uint32_t& ip, const std::string& ip_str)
{
ip = inet_addr(ip_str.c_str());
if(INADDR_NONE == ip)
return false;
return true;
}
//----------------------------------------------------------------------------
bool parse_peer_from_string(uint32_t& ip, uint32_t& port, const std::string& addres)
{
//parse ip and address
std::string::size_type p = addres.find(':');
if(p == std::string::npos)
{
return false;
}
std::string ip_str = addres.substr(0, p);
std::string port_str = addres.substr(p+1, addres.size());
if(!get_ip_int32_from_string(ip, ip_str))
{
return false;
}
if(!get_xtype_from_string(port, port_str))
{
return false;
}
return true;
}
//----------------------------------------------------------------------------
std::string num_to_string_fast(int64_t val)
{
/*
char buff[30] = {0};
i64toa_s(val, buff, sizeof(buff)-1, 10);
return buff;*/
return boost::lexical_cast<std::string>(val);
}
//----------------------------------------------------------------------------
bool string_to_num_fast(const std::string& buff, int64_t& val)
{
//return get_xtype_from_string(val, buff);
#if (defined _MSC_VER)
val = _atoi64(buff.c_str());
#else
val = atoll(buff.c_str());
#endif
/*
* val = atoi64(buff.c_str());
*/
if(buff != "0" && val == 0)
return false;
return true;
}
//----------------------------------------------------------------------------
bool string_to_num_fast(const std::string& buff, int& val)
{
val = atoi(buff.c_str());
if(buff != "0" && val == 0)
return false;
return true;
}
//----------------------------------------------------------------------------
#ifdef WINDOWS_PLATFORM
std::string system_time_to_string(const SYSTEMTIME& st)
{
/*
TIME_ZONE_INFORMATION tzi;
GetTimeZoneInformation(&tzi);
SystemTimeToTzSpecificLocalTime(&tzi, &stUTC, &stLocal);
*/
char szTime[25], szDate[25];
::GetTimeFormatA(
LOCALE_USER_DEFAULT, // locale
TIME_FORCE24HOURFORMAT, // options
&st, // time
NULL, // time format string
szTime, // formatted string buffer
25 // size of string buffer
);
::GetDateFormatA(
LOCALE_USER_DEFAULT, // locale
NULL, // options
&st, // date
NULL, // date format
szDate, // formatted string buffer
25 // size of buffer
);
szTime[24] = szDate[24] = 0; //be happy :)
std::string res = szDate;
(res += " " )+= szTime;
return res;
}
#endif
//----------------------------------------------------------------------------
bool compare_no_case(const std::string& str1, const std::string& str2)
{
return !boost::iequals(str1, str2);
}
//----------------------------------------------------------------------------
bool compare_no_case(const std::wstring& str1, const std::wstring& str2)
{
return !boost::iequals(str1, str2);
}
//----------------------------------------------------------------------------
bool is_match_prefix(const std::wstring& str1, const std::wstring& prefix)
{
if(prefix.size()>str1.size())
return false;
if(!compare_no_case(str1.substr(0, prefix.size()), prefix))
return true;
else
return false;
}
//----------------------------------------------------------------------------
bool is_match_prefix(const std::string& str1, const std::string& prefix)
{
if(prefix.size()>str1.size())
return false;
if(!compare_no_case(str1.substr(0, prefix.size()), prefix))
return true;
else
return false;
}
//----------------------------------------------------------------------------
std::string& get_current_module_name()
{
static std::string module_name;
return module_name;
}
//----------------------------------------------------------------------------
std::string& get_current_module_folder()
{
static std::string module_folder;
return module_folder;
}
//----------------------------------------------------------------------------
#ifdef _WIN32
std::string get_current_module_path()
{
char pname [5000] = {0};
GetModuleFileNameA( NULL, pname, sizeof(pname));
pname[sizeof(pname)-1] = 0; //be happy ;)
return pname;
}
#endif
//----------------------------------------------------------------------------
bool set_module_name_and_folder(const std::string& path_to_process_)
{
std::string path_to_process = path_to_process_;
#ifdef _WIN32
path_to_process = get_current_module_path();
#endif
std::string::size_type a = path_to_process.rfind( '\\' );
if(a == std::string::npos )
{
a = path_to_process.rfind( '/' );
}
if ( a != std::string::npos )
{
get_current_module_name() = path_to_process.substr(a+1, path_to_process.size());
get_current_module_folder() = path_to_process.substr(0, a);
return true;
}else
return false;
}
//----------------------------------------------------------------------------
bool trim_left(std::string& str)
{
for(std::string::iterator it = str.begin(); it!= str.end() && isspace(static_cast<unsigned char>(*it));)
str.erase(str.begin());
return true;
}
//----------------------------------------------------------------------------
bool trim_right(std::string& str)
{
for(std::string::reverse_iterator it = str.rbegin(); it!= str.rend() && isspace(static_cast<unsigned char>(*it));)
str.erase( --((it++).base()));
return true;
}
//----------------------------------------------------------------------------
std::string& trim(std::string& str)
{
trim_left(str);
trim_right(str);
return str;
}
//----------------------------------------------------------------------------
std::string trim(const std::string& str_)
{
std::string str = str_;
trim_left(str);
trim_right(str);
return str;
}
//----------------------------------------------------------------------------
std::string get_extension(const std::string& str)
{
std::string res;
std::string::size_type pos = str.rfind('.');
if(std::string::npos == pos)
return res;
res = str.substr(pos+1, str.size()-pos);
return res;
}
//----------------------------------------------------------------------------
std::string get_filename_from_path(const std::string& str)
{
std::string res;
std::string::size_type pos = str.rfind('\\');
if(std::string::npos == pos)
return str;
res = str.substr(pos+1, str.size()-pos);
return res;
}
//----------------------------------------------------------------------------
std::string cut_off_extension(const std::string& str)
{
std::string res;
std::string::size_type pos = str.rfind('.');
if(std::string::npos == pos)
return str;
res = str.substr(0, pos);
return res;
}
//----------------------------------------------------------------------------
#ifdef _WININET_
std::string get_string_from_systemtime(const SYSTEMTIME& sys_time)
{
std::string result_string;
char buff[100] = {0};
BOOL res = ::InternetTimeFromSystemTimeA(&sys_time, INTERNET_RFC1123_FORMAT, buff, 99);
if(!res)
{
LOG_ERROR("Failed to load SytemTime to string");
}
result_string = buff;
return result_string;
}
//-------------------------------------------------------------------------------------
SYSTEMTIME get_systemtime_from_string(const std::string& buff)
{
SYSTEMTIME result_time = {0};
BOOL res = ::InternetTimeToSystemTimeA(buff.c_str(), &result_time, NULL);
if(!res)
{
LOG_ERROR("Failed to load SytemTime from string " << buff << "interval set to 15 minutes");
}
return result_time;
}
#endif
#ifdef WINDOWS_PLATFORM
const wchar_t* get_pc_name()
{
static wchar_t info[INFO_BUFFER_SIZE];
static DWORD bufCharCount = INFO_BUFFER_SIZE;
static bool init = false;
if (!init) {
if (!GetComputerNameW( info, &bufCharCount ))
info[0] = 0;
else
init = true;
}
return info;
}
const wchar_t* get_user_name()
{
static wchar_t info[INFO_BUFFER_SIZE];
static DWORD bufCharCount = INFO_BUFFER_SIZE;
static bool init = false;
if (!init) {
if (!GetUserNameW( info, &bufCharCount ))
info[0] = 0;
else
init = true;
}
return info;
}
#endif
#ifdef _LM_
const wchar_t* get_domain_name()
{
static wchar_t info[INFO_BUFFER_SIZE];
static DWORD bufCharCount = 0;
static bool init = false;
if (!init) {
LPWSTR domain( NULL );
NETSETUP_JOIN_STATUS status;
info[0] = 0;
if (NET_API_STATUS result = NetGetJoinInformation( NULL, &domain, &status ))
{
LOG_ERROR("get_domain_name error: " << log_space::get_win32_err_descr(result));
} else
{
StringCchCopyW( info, sizeof(info)/sizeof( info[0] ), domain );
NetApiBufferFree((void*)domain);
init = true;
}
}
return info;
}
#endif
#ifdef WINDOWS_PLATFORM
inline
std::string load_resource_string_a(int id, const char* pmodule_name = NULL)
{
//slow realization
HMODULE h = ::GetModuleHandleA( pmodule_name );
char buff[2000] = {0};
::LoadStringA( h, id, buff, sizeof(buff));
buff[sizeof(buff)-1] = 0; //be happy :)
return buff;
}
inline
std::wstring load_resource_string_w(int id, const char* pmodule_name = NULL)
{
//slow realization
HMODULE h = ::GetModuleHandleA( pmodule_name );
wchar_t buff[2000] = {0};
::LoadStringW( h, id, buff, sizeof(buff) / sizeof( buff[0] ) );
buff[(sizeof(buff)/sizeof(buff[0]))-1] = 0; //be happy :)
return buff;
}
#endif
}
}

View file

@ -24,24 +24,23 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#pragma once
#ifndef _STRING_TOOLS_H_
#define _STRING_TOOLS_H_
//#include <objbase.h>
#include <locale>
#include <cstdlib>
#include <cctype>
#include <cstdint>
#include <iomanip>
//#include <strsafe.h>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/asio.hpp>
#include <boost/algorithm/string/compare.hpp>
#include <boost/algorithm/string.hpp>
#include "warnings.h"
#include <iostream>
#include <limits>
#include <map>
#include <string>
#include <sstream>
#include <boost/lexical_cast.hpp>
#include "warnings.h"
#ifndef OUT
#define OUT
@ -51,71 +50,32 @@
#pragma comment (lib, "Rpcrt4.lib")
#endif
// Don't include lexical_cast.hpp, to reduce compilation time
//#include <boost/lexical_cast.hpp>
namespace boost {
namespace uuids {
struct uuid;
}
template <typename Target, typename Source>
inline Target lexical_cast(const Source &arg);
}
namespace epee
{
namespace string_tools
{
inline std::wstring get_str_from_guid(const boost::uuids::uuid& rid)
{
return boost::lexical_cast<std::wstring>(rid);
}
//----------------------------------------------------------------------------
inline std::string get_str_from_guid_a(const boost::uuids::uuid& rid)
{
return boost::lexical_cast<std::string>(rid);
}
//----------------------------------------------------------------------------
inline bool get_guid_from_string( boost::uuids::uuid& inetifer, std::wstring str_id)
{
if(str_id.size() < 36)
return false;
if('{' == *str_id.begin())
str_id.erase(0, 1);
if('}' == *(--str_id.end()))
str_id.erase(--str_id.end());
try
{
inetifer = boost::lexical_cast<boost::uuids::uuid>(str_id);
return true;
}
catch(...)
{
return false;
}
}
//----------------------------------------------------------------------------
inline bool get_guid_from_string(OUT boost::uuids::uuid& inetifer, const std::string& str_id)
{
std::string local_str_id = str_id;
if(local_str_id.size() < 36)
return false;
if('{' == *local_str_id.begin())
local_str_id.erase(0, 1);
if('}' == *(--local_str_id.end()))
local_str_id.erase(--local_str_id.end());
try
{
inetifer = boost::lexical_cast<boost::uuids::uuid>(local_str_id);
return true;
}
catch(...)
{
return false;
}
}
//----------------------------------------------------------------------------
std::wstring get_str_from_guid(const boost::uuids::uuid& rid);
std::string get_str_from_guid_a(const boost::uuids::uuid& rid);
bool get_guid_from_string( boost::uuids::uuid& inetifer, std::wstring str_id);
bool get_guid_from_string(OUT boost::uuids::uuid& inetifer, const std::string& str_id);
//----------------------------------------------------------------------------
template<class CharT>
std::basic_string<CharT> buff_to_hex(const std::basic_string<CharT>& s)
{
using namespace std;
basic_stringstream<CharT> hexStream;
hexStream << hex << noshowbase << setw(2);
std::basic_stringstream<CharT> hexStream;
hexStream << std::hex << std::noshowbase << std::setw(2);
for(typename std::basic_string<CharT>::const_iterator it = s.begin(); it != s.end(); it++)
{
@ -127,13 +87,12 @@ namespace string_tools
template<class CharT>
std::basic_string<CharT> buff_to_hex_nodelimer(const std::basic_string<CharT>& s)
{
using namespace std;
basic_stringstream<CharT> hexStream;
hexStream << hex << noshowbase;
std::basic_stringstream<CharT> hexStream;
hexStream << std::hex << std::noshowbase;
for(typename std::basic_string<CharT>::const_iterator it = s.begin(); it != s.end(); it++)
{
hexStream << setw(2) << setfill('0') << static_cast<unsigned int>(static_cast<unsigned char>(*it));
hexStream << std::setw(2) << std::setfill('0') << static_cast<unsigned int>(static_cast<unsigned char>(*it));
}
return hexStream.str();
}
@ -273,13 +232,6 @@ POP_WARNINGS
return true;
}
/* template<typename t_type>
bool get_xparam_from_command_line(const std::map<std::string, std::string>& res, const std::basic_string<typename t_string::value_type> & key, t_type& val)
{
}
*/
template<class t_string, typename t_type>
bool get_xparam_from_command_line(const std::map<t_string, t_string>& res, const t_string & key, t_type& val)
{
@ -295,22 +247,22 @@ POP_WARNINGS
return true;
}
template<class t_string, typename t_type>
t_type get_xparam_from_command_line(const std::map<t_string, t_string>& res, const t_string & key, const t_type& default_value)
{
typename std::map<t_string, t_string>::const_iterator it = res.find(key);
if(it == res.end())
return default_value;
template<class t_string, typename t_type>
t_type get_xparam_from_command_line(const std::map<t_string, t_string>& res, const t_string & key, const t_type& default_value)
{
typename std::map<t_string, t_string>::const_iterator it = res.find(key);
if(it == res.end())
return default_value;
if(it->second.size())
{
t_type s;
get_xtype_from_string(s, it->second);
return s;
}
if(it->second.size())
{
t_type s;
get_xtype_from_string(s, it->second);
return s;
}
return default_value;
}
return default_value;
}
template<class t_string>
bool have_in_command_line(const std::map<t_string, t_string>& res, const std::basic_string<typename t_string::value_type>& key)
@ -322,243 +274,40 @@ POP_WARNINGS
return true;
}
//----------------------------------------------------------------------------
//#ifdef _WINSOCK2API_
inline std::string get_ip_string_from_int32(uint32_t ip)
{
in_addr adr;
adr.s_addr = ip;
const char* pbuf = inet_ntoa(adr);
if(pbuf)
return pbuf;
else
return "[failed]";
}
//----------------------------------------------------------------------------
inline bool get_ip_int32_from_string(uint32_t& ip, const std::string& ip_str)
{
ip = inet_addr(ip_str.c_str());
if(INADDR_NONE == ip)
return false;
return true;
}
//----------------------------------------------------------------------------
inline bool parse_peer_from_string(uint32_t& ip, uint32_t& port, const std::string& addres)
{
//parse ip and address
std::string::size_type p = addres.find(':');
if(p == std::string::npos)
{
return false;
}
std::string ip_str = addres.substr(0, p);
std::string port_str = addres.substr(p+1, addres.size());
if(!get_ip_int32_from_string(ip, ip_str))
{
return false;
}
if(!get_xtype_from_string(port, port_str))
{
return false;
}
return true;
}
//#endif
//----------------------------------------------------------------------------
std::string get_ip_string_from_int32(uint32_t ip);
bool get_ip_int32_from_string(uint32_t& ip, const std::string& ip_str);
bool parse_peer_from_string(uint32_t& ip, uint32_t& port, const std::string& addres);
//----------------------------------------------------------------------------
template<typename t>
inline std::string get_t_as_hex_nwidth(const t& v, std::streamsize w = 8)
inline std::string get_t_as_hex_nwidth(const t& v, std::streamsize w = 8)
{
std::stringstream ss;
ss << std::setfill ('0') << std::setw (w) << std::hex << std::noshowbase;
ss << v;
return ss.str();
}
inline std::string num_to_string_fast(int64_t val)
{
/*
char buff[30] = {0};
i64toa_s(val, buff, sizeof(buff)-1, 10);
return buff;*/
return boost::lexical_cast<std::string>(val);
}
//----------------------------------------------------------------------------
inline bool string_to_num_fast(const std::string& buff, int64_t& val)
{
//return get_xtype_from_string(val, buff);
#if (defined _MSC_VER)
val = _atoi64(buff.c_str());
#else
val = atoll(buff.c_str());
#endif
/*
* val = atoi64(buff.c_str());
*/
if(buff != "0" && val == 0)
return false;
return true;
}
//----------------------------------------------------------------------------
inline bool string_to_num_fast(const std::string& buff, int& val)
{
val = atoi(buff.c_str());
if(buff != "0" && val == 0)
return false;
return true;
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
std::string num_to_string_fast(int64_t val);
bool string_to_num_fast(const std::string& buff, int64_t& val);
bool string_to_num_fast(const std::string& buff, int& val);
#ifdef WINDOWS_PLATFORM
inline std::string system_time_to_string(const SYSTEMTIME& st)
{
/*
TIME_ZONE_INFORMATION tzi;
GetTimeZoneInformation(&tzi);
SystemTimeToTzSpecificLocalTime(&tzi, &stUTC, &stLocal);
*/
char szTime[25], szDate[25];
::GetTimeFormatA(
LOCALE_USER_DEFAULT, // locale
TIME_FORCE24HOURFORMAT, // options
&st, // time
NULL, // time format string
szTime, // formatted string buffer
25 // size of string buffer
);
::GetDateFormatA(
LOCALE_USER_DEFAULT, // locale
NULL, // options
&st, // date
NULL, // date format
szDate, // formatted string buffer
25 // size of buffer
);
szTime[24] = szDate[24] = 0; //be happy :)
std::string res = szDate;
(res += " " )+= szTime;
return res;
}
std::string system_time_to_string(const SYSTEMTIME& st);
#endif
//----------------------------------------------------------------------------
inline bool compare_no_case(const std::string& str1, const std::string& str2)
{
return !boost::iequals(str1, str2);
}
//----------------------------------------------------------------------------
inline bool compare_no_case(const std::wstring& str1, const std::wstring& str2)
{
return !boost::iequals(str1, str2);
}
//----------------------------------------------------------------------------
inline bool is_match_prefix(const std::wstring& str1, const std::wstring& prefix)
{
if(prefix.size()>str1.size())
return false;
if(!compare_no_case(str1.substr(0, prefix.size()), prefix))
return true;
else
return false;
}
//----------------------------------------------------------------------------
inline bool is_match_prefix(const std::string& str1, const std::string& prefix)
{
if(prefix.size()>str1.size())
return false;
if(!compare_no_case(str1.substr(0, prefix.size()), prefix))
return true;
else
return false;
}
//----------------------------------------------------------------------------
inline std::string& get_current_module_name()
{
static std::string module_name;
return module_name;
}
//----------------------------------------------------------------------------
inline std::string& get_current_module_folder()
{
static std::string module_folder;
return module_folder;
}
//----------------------------------------------------------------------------
bool compare_no_case(const std::string& str1, const std::string& str2);
bool compare_no_case(const std::wstring& str1, const std::wstring& str2);
bool is_match_prefix(const std::wstring& str1, const std::wstring& prefix);
bool is_match_prefix(const std::string& str1, const std::string& prefix);
std::string& get_current_module_name();
std::string& get_current_module_folder();
#ifdef _WIN32
inline std::string get_current_module_path()
{
char pname [5000] = {0};
GetModuleFileNameA( NULL, pname, sizeof(pname));
pname[sizeof(pname)-1] = 0; //be happy ;)
return pname;
}
std::string get_current_module_path();
#endif
//----------------------------------------------------------------------------
inline bool set_module_name_and_folder(const std::string& path_to_process_)
{
std::string path_to_process = path_to_process_;
#ifdef _WIN32
path_to_process = get_current_module_path();
#endif
std::string::size_type a = path_to_process.rfind( '\\' );
if(a == std::string::npos )
{
a = path_to_process.rfind( '/' );
}
if ( a != std::string::npos )
{
get_current_module_name() = path_to_process.substr(a+1, path_to_process.size());
get_current_module_folder() = path_to_process.substr(0, a);
return true;
}else
return false;
}
//----------------------------------------------------------------------------
inline bool trim_left(std::string& str)
{
for(std::string::iterator it = str.begin(); it!= str.end() && isspace(static_cast<unsigned char>(*it));)
str.erase(str.begin());
return true;
}
//----------------------------------------------------------------------------
inline bool trim_right(std::string& str)
{
for(std::string::reverse_iterator it = str.rbegin(); it!= str.rend() && isspace(static_cast<unsigned char>(*it));)
str.erase( --((it++).base()));
return true;
}
//----------------------------------------------------------------------------
inline std::string& trim(std::string& str)
{
trim_left(str);
trim_right(str);
return str;
}
//----------------------------------------------------------------------------
inline std::string trim(const std::string& str_)
{
std::string str = str_;
trim_left(str);
trim_right(str);
return str;
}
bool set_module_name_and_folder(const std::string& path_to_process_);
bool trim_left(std::string& str);
bool trim_right(std::string& str);
std::string& trim(std::string& str);
std::string trim(const std::string& str_);
//----------------------------------------------------------------------------
template<class t_pod_type>
std::string pod_to_hex(const t_pod_type& s)
@ -584,161 +333,27 @@ POP_WARNINGS
return true;
}
//----------------------------------------------------------------------------
inline std::string get_extension(const std::string& str)
{
std::string res;
std::string::size_type pos = str.rfind('.');
if(std::string::npos == pos)
return res;
res = str.substr(pos+1, str.size()-pos);
return res;
}
//----------------------------------------------------------------------------
inline std::string get_filename_from_path(const std::string& str)
{
std::string res;
std::string::size_type pos = str.rfind('\\');
if(std::string::npos == pos)
return str;
res = str.substr(pos+1, str.size()-pos);
return res;
}
//----------------------------------------------------------------------------
inline std::string cut_off_extension(const std::string& str)
{
std::string res;
std::string::size_type pos = str.rfind('.');
if(std::string::npos == pos)
return str;
res = str.substr(0, pos);
return res;
}
//----------------------------------------------------------------------------
std::string get_extension(const std::string& str);
std::string get_filename_from_path(const std::string& str);
std::string cut_off_extension(const std::string& str);
#ifdef _WININET_
inline std::string get_string_from_systemtime(const SYSTEMTIME& sys_time)
{
std::string result_string;
char buff[100] = {0};
BOOL res = ::InternetTimeFromSystemTimeA(&sys_time, INTERNET_RFC1123_FORMAT, buff, 99);
if(!res)
{
LOG_ERROR("Failed to load SytemTime to string");
}
result_string = buff;
return result_string;
}
//-------------------------------------------------------------------------------------
inline SYSTEMTIME get_systemtime_from_string(const std::string& buff)
{
SYSTEMTIME result_time = {0};
BOOL res = ::InternetTimeToSystemTimeA(buff.c_str(), &result_time, NULL);
if(!res)
{
LOG_ERROR("Failed to load SytemTime from string " << buff << "interval set to 15 minutes");
}
return result_time;
}
std::string get_string_from_systemtime(const SYSTEMTIME& sys_time);
SYSTEMTIME get_systemtime_from_string(const std::string& buff);
#endif
#ifdef WINDOWS_PLATFORM
static const DWORD INFO_BUFFER_SIZE = 10000;
const DWORD INFO_BUFFER_SIZE = 10000;
static const wchar_t* get_pc_name()
{
static wchar_t info[INFO_BUFFER_SIZE];
static DWORD bufCharCount = INFO_BUFFER_SIZE;
static bool init = false;
if (!init) {
if (!GetComputerNameW( info, &bufCharCount ))
info[0] = 0;
else
init = true;
}
return info;
}
static const wchar_t* get_user_name()
{
static wchar_t info[INFO_BUFFER_SIZE];
static DWORD bufCharCount = INFO_BUFFER_SIZE;
static bool init = false;
if (!init) {
if (!GetUserNameW( info, &bufCharCount ))
info[0] = 0;
else
init = true;
}
return info;
}
const wchar_t* get_pc_name();
const wchar_t* get_user_name();
#endif
#ifdef _LM_
static const wchar_t* get_domain_name()
{
static wchar_t info[INFO_BUFFER_SIZE];
static DWORD bufCharCount = 0;
static bool init = false;
if (!init) {
LPWSTR domain( NULL );
NETSETUP_JOIN_STATUS status;
info[0] = 0;
if (NET_API_STATUS result = NetGetJoinInformation( NULL, &domain, &status ))
{
LOG_ERROR("get_domain_name error: " << log_space::get_win32_err_descr(result));
} else
{
StringCchCopyW( info, sizeof(info)/sizeof( info[0] ), domain );
NetApiBufferFree((void*)domain);
init = true;
}
}
return info;
}
const wchar_t* get_domain_name();
#endif
#ifdef WINDOWS_PLATFORM
inline
std::string load_resource_string_a(int id, const char* pmodule_name = NULL)
{
//slow realization
HMODULE h = ::GetModuleHandleA( pmodule_name );
char buff[2000] = {0};
::LoadStringA( h, id, buff, sizeof(buff));
buff[sizeof(buff)-1] = 0; //be happy :)
return buff;
}
inline
std::wstring load_resource_string_w(int id, const char* pmodule_name = NULL)
{
//slow realization
HMODULE h = ::GetModuleHandleA( pmodule_name );
wchar_t buff[2000] = {0};
::LoadStringW( h, id, buff, sizeof(buff) / sizeof( buff[0] ) );
buff[(sizeof(buff)/sizeof(buff[0]))-1] = 0; //be happy :)
return buff;
}
std::string load_resource_string_a(int id, const char* pmodule_name = NULL);
std::wstring load_resource_string_w(int id, const char* pmodule_name = NULL);
#endif
}
}

View file

@ -32,13 +32,9 @@
#include <condition_variable>
#include <mutex>
#include <boost/thread/locks.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/recursive_mutex.hpp>
namespace epee
{
struct simple_event
{
simple_event() : m_rised(false)
@ -70,7 +66,7 @@ namespace epee
class critical_section
{
boost::recursive_mutex m_section;
std::recursive_mutex m_section;
public:
//to make copy fake!
@ -89,7 +85,6 @@ namespace epee
void lock()
{
m_section.lock();
//EnterCriticalSection( &m_section );
}
void unlock()

333
external/google/dense_hash_map vendored Normal file
View file

@ -0,0 +1,333 @@
// Copyright (c) 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * 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.
// * Neither the name of Google Inc. 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
// OWNER 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.
// ----
// Author: Craig Silverstein
//
// This is just a very thin wrapper over densehashtable.h, just
// like sgi stl's stl_hash_map is a very thin wrapper over
// stl_hashtable. The major thing we define is operator[], because
// we have a concept of a data_type which stl_hashtable doesn't
// (it only has a key and a value).
//
// NOTE: this is exactly like sparse_hash_map.h, with the word
// "sparse" replaced by "dense", except for the addition of
// set_empty_key().
//
// YOU MUST CALL SET_EMPTY_KEY() IMMEDIATELY AFTER CONSTRUCTION.
//
// Otherwise your program will die in mysterious ways. (Note if you
// use the constructor that takes an InputIterator range, you pass in
// the empty key in the constructor, rather than after. As a result,
// this constructor differs from the standard STL version.)
//
// In other respects, we adhere mostly to the STL semantics for
// hash-map. One important exception is that insert() may invalidate
// iterators entirely -- STL semantics are that insert() may reorder
// iterators, but they all still refer to something valid in the
// hashtable. Not so for us. Likewise, insert() may invalidate
// pointers into the hashtable. (Whether insert invalidates iterators
// and pointers depends on whether it results in a hashtable resize).
// On the plus side, delete() doesn't invalidate iterators or pointers
// at all, or even change the ordering of elements.
//
// Here are a few "power user" tips:
//
// 1) set_deleted_key():
// If you want to use erase() you *must* call set_deleted_key(),
// in addition to set_empty_key(), after construction.
// The deleted and empty keys must differ.
//
// 2) resize(0):
// When an item is deleted, its memory isn't freed right
// away. This allows you to iterate over a hashtable,
// and call erase(), without invalidating the iterator.
// To force the memory to be freed, call resize(0).
// For tr1 compatibility, this can also be called as rehash(0).
//
// 3) min_load_factor(0.0)
// Setting the minimum load factor to 0.0 guarantees that
// the hash table will never shrink.
//
// Roughly speaking:
// (1) dense_hash_map: fastest, uses the most memory unless entries are small
// (2) sparse_hash_map: slowest, uses the least memory
// (3) hash_map / unordered_map (STL): in the middle
//
// Typically I use sparse_hash_map when I care about space and/or when
// I need to save the hashtable on disk. I use hash_map otherwise. I
// don't personally use dense_hash_set ever; some people use it for
// small sets with lots of lookups.
//
// - dense_hash_map has, typically, about 78% memory overhead (if your
// data takes up X bytes, the hash_map uses .78X more bytes in overhead).
// - sparse_hash_map has about 4 bits overhead per entry.
// - sparse_hash_map can be 3-7 times slower than the others for lookup and,
// especially, inserts. See time_hash_map.cc for details.
//
// See /usr/(local/)?doc/sparsehash-*/dense_hash_map.html
// for information about how to use this class.
#ifndef _DENSE_HASH_MAP_H_
#define _DENSE_HASH_MAP_H_
#include <google/sparsehash/sparseconfig.h>
#include <stdio.h> // for FILE * in read()/write()
#include <algorithm> // for the default template args
#include <functional> // for equal_to
#include <memory> // for alloc<>
#include <utility> // for pair<>
#include HASH_FUN_H // defined in config.h
#include <google/sparsehash/libc_allocator_with_realloc.h>
#include <google/sparsehash/densehashtable.h>
_START_GOOGLE_NAMESPACE_
using STL_NAMESPACE::pair;
template <class Key, class T,
class HashFcn = SPARSEHASH_HASH<Key>, // defined in sparseconfig.h
class EqualKey = STL_NAMESPACE::equal_to<Key>,
class Alloc = libc_allocator_with_realloc<pair<const Key, T> > >
class dense_hash_map {
private:
// Apparently select1st is not stl-standard, so we define our own
struct SelectKey {
typedef const Key& result_type;
const Key& operator()(const pair<const Key, T>& p) const {
return p.first;
}
};
struct SetKey {
void operator()(pair<const Key, T>* value, const Key& new_key) const {
*const_cast<Key*>(&value->first) = new_key;
// It would be nice to clear the rest of value here as well, in
// case it's taking up a lot of memory. We do this by clearing
// the value. This assumes T has a zero-arg constructor!
value->second = T();
}
};
// For operator[].
struct DefaultValue {
STL_NAMESPACE::pair<const Key, T> operator()(const Key& key) {
return STL_NAMESPACE::make_pair(key, T());
}
};
// The actual data
typedef dense_hashtable<pair<const Key, T>, Key, HashFcn, SelectKey,
SetKey, EqualKey, Alloc> ht;
ht rep;
public:
typedef typename ht::key_type key_type;
typedef T data_type;
typedef T mapped_type;
typedef typename ht::value_type value_type;
typedef typename ht::hasher hasher;
typedef typename ht::key_equal key_equal;
typedef Alloc allocator_type;
typedef typename ht::size_type size_type;
typedef typename ht::difference_type difference_type;
typedef typename ht::pointer pointer;
typedef typename ht::const_pointer const_pointer;
typedef typename ht::reference reference;
typedef typename ht::const_reference const_reference;
typedef typename ht::iterator iterator;
typedef typename ht::const_iterator const_iterator;
typedef typename ht::local_iterator local_iterator;
typedef typename ht::const_local_iterator const_local_iterator;
// Iterator functions
iterator begin() { return rep.begin(); }
iterator end() { return rep.end(); }
const_iterator begin() const { return rep.begin(); }
const_iterator end() const { return rep.end(); }
// These come from tr1's unordered_map. For us, a bucket has 0 or 1 elements.
local_iterator begin(size_type i) { return rep.begin(i); }
local_iterator end(size_type i) { return rep.end(i); }
const_local_iterator begin(size_type i) const { return rep.begin(i); }
const_local_iterator end(size_type i) const { return rep.end(i); }
// Accessor functions
allocator_type get_allocator() const { return rep.get_allocator(); }
hasher hash_funct() const { return rep.hash_funct(); }
hasher hash_function() const { return hash_funct(); }
key_equal key_eq() const { return rep.key_eq(); }
// Constructors
explicit dense_hash_map(size_type expected_max_items_in_table = 0,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type())
: rep(expected_max_items_in_table, hf, eql, SelectKey(), SetKey(), alloc) {
}
template <class InputIterator>
dense_hash_map(InputIterator f, InputIterator l,
const key_type& empty_key_val,
size_type expected_max_items_in_table = 0,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type())
: rep(expected_max_items_in_table, hf, eql, SelectKey(), SetKey(), alloc) {
set_empty_key(empty_key_val);
rep.insert(f, l);
}
// We use the default copy constructor
// We use the default operator=()
// We use the default destructor
void clear() { rep.clear(); }
// This clears the hash map without resizing it down to the minimum
// bucket count, but rather keeps the number of buckets constant
void clear_no_resize() { rep.clear_no_resize(); }
void swap(dense_hash_map& hs) { rep.swap(hs.rep); }
// Functions concerning size
size_type size() const { return rep.size(); }
size_type max_size() const { return rep.max_size(); }
bool empty() const { return rep.empty(); }
size_type bucket_count() const { return rep.bucket_count(); }
size_type max_bucket_count() const { return rep.max_bucket_count(); }
// These are tr1 methods. bucket() is the bucket the key is or would be in.
size_type bucket_size(size_type i) const { return rep.bucket_size(i); }
size_type bucket(const key_type& key) const { return rep.bucket(key); }
float load_factor() const {
return size() * 1.0f / bucket_count();
}
float max_load_factor() const {
float shrink, grow;
rep.get_resizing_parameters(&shrink, &grow);
return grow;
}
void max_load_factor(float new_grow) {
float shrink, grow;
rep.get_resizing_parameters(&shrink, &grow);
rep.set_resizing_parameters(shrink, new_grow);
}
// These aren't tr1 methods but perhaps ought to be.
float min_load_factor() const {
float shrink, grow;
rep.get_resizing_parameters(&shrink, &grow);
return shrink;
}
void min_load_factor(float new_shrink) {
float shrink, grow;
rep.get_resizing_parameters(&shrink, &grow);
rep.set_resizing_parameters(new_shrink, grow);
}
// Deprecated; use min_load_factor() or max_load_factor() instead.
void set_resizing_parameters(float shrink, float grow) {
rep.set_resizing_parameters(shrink, grow);
}
void resize(size_type hint) { rep.resize(hint); }
void rehash(size_type hint) { resize(hint); } // the tr1 name
// Lookup routines
iterator find(const key_type& key) { return rep.find(key); }
const_iterator find(const key_type& key) const { return rep.find(key); }
data_type& operator[](const key_type& key) { // This is our value-add!
// If key is in the hashtable, returns find(key)->second,
// otherwise returns insert(value_type(key, T()).first->second.
// Note it does not create an empty T unless the find fails.
return rep.template find_or_insert<DefaultValue>(key).second;
}
size_type count(const key_type& key) const { return rep.count(key); }
pair<iterator, iterator> equal_range(const key_type& key) {
return rep.equal_range(key);
}
pair<const_iterator, const_iterator> equal_range(const key_type& key) const {
return rep.equal_range(key);
}
// Insertion routines
pair<iterator, bool> insert(const value_type& obj) { return rep.insert(obj); }
template <class InputIterator>
void insert(InputIterator f, InputIterator l) { rep.insert(f, l); }
void insert(const_iterator f, const_iterator l) { rep.insert(f, l); }
// required for std::insert_iterator; the passed-in iterator is ignored
iterator insert(iterator, const value_type& obj) { return insert(obj).first; }
// Deletion and empty routines
// THESE ARE NON-STANDARD! I make you specify an "impossible" key
// value to identify deleted and empty buckets. You can change the
// deleted key as time goes on, or get rid of it entirely to be insert-only.
void set_empty_key(const key_type& key) { // YOU MUST CALL THIS!
rep.set_empty_key(value_type(key, data_type())); // rep wants a value
}
key_type empty_key() const {
return rep.empty_key().first; // rep returns a value
}
void set_deleted_key(const key_type& key) { rep.set_deleted_key(key); }
void clear_deleted_key() { rep.clear_deleted_key(); }
key_type deleted_key() const { return rep.deleted_key(); }
// These are standard
size_type erase(const key_type& key) { return rep.erase(key); }
void erase(iterator it) { rep.erase(it); }
void erase(iterator f, iterator l) { rep.erase(f, l); }
// Comparison
bool operator==(const dense_hash_map& hs) const { return rep == hs.rep; }
bool operator!=(const dense_hash_map& hs) const { return rep != hs.rep; }
// I/O -- this is an add-on for writing metainformation to disk
bool write_metadata(FILE *fp) { return rep.write_metadata(fp); }
bool read_metadata(FILE *fp) { return rep.read_metadata(fp); }
bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); }
bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); }
};
// We need a global swap as well
template <class Key, class T, class HashFcn, class EqualKey, class Alloc>
inline void swap(dense_hash_map<Key, T, HashFcn, EqualKey, Alloc>& hm1,
dense_hash_map<Key, T, HashFcn, EqualKey, Alloc>& hm2) {
hm1.swap(hm2);
}
_END_GOOGLE_NAMESPACE_
#endif /* _DENSE_HASH_MAP_H_ */

308
external/google/dense_hash_set vendored Normal file
View file

@ -0,0 +1,308 @@
// Copyright (c) 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * 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.
// * Neither the name of Google Inc. 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
// OWNER 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.
// ---
// Author: Craig Silverstein
//
// This is just a very thin wrapper over densehashtable.h, just
// like sgi stl's stl_hash_set is a very thin wrapper over
// stl_hashtable. The major thing we define is operator[], because
// we have a concept of a data_type which stl_hashtable doesn't
// (it only has a key and a value).
//
// This is more different from dense_hash_map than you might think,
// because all iterators for sets are const (you obviously can't
// change the key, and for sets there is no value).
//
// NOTE: this is exactly like sparse_hash_set.h, with the word
// "sparse" replaced by "dense", except for the addition of
// set_empty_key().
//
// YOU MUST CALL SET_EMPTY_KEY() IMMEDIATELY AFTER CONSTRUCTION.
//
// Otherwise your program will die in mysterious ways. (Note if you
// use the constructor that takes an InputIterator range, you pass in
// the empty key in the constructor, rather than after. As a result,
// this constructor differs from the standard STL version.)
//
// In other respects, we adhere mostly to the STL semantics for
// hash-map. One important exception is that insert() may invalidate
// iterators entirely -- STL semantics are that insert() may reorder
// iterators, but they all still refer to something valid in the
// hashtable. Not so for us. Likewise, insert() may invalidate
// pointers into the hashtable. (Whether insert invalidates iterators
// and pointers depends on whether it results in a hashtable resize).
// On the plus side, delete() doesn't invalidate iterators or pointers
// at all, or even change the ordering of elements.
//
// Here are a few "power user" tips:
//
// 1) set_deleted_key():
// If you want to use erase() you must call set_deleted_key(),
// in addition to set_empty_key(), after construction.
// The deleted and empty keys must differ.
//
// 2) resize(0):
// When an item is deleted, its memory isn't freed right
// away. This allows you to iterate over a hashtable,
// and call erase(), without invalidating the iterator.
// To force the memory to be freed, call resize(0).
// For tr1 compatibility, this can also be called as rehash(0).
//
// 3) min_load_factor(0.0)
// Setting the minimum load factor to 0.0 guarantees that
// the hash table will never shrink.
//
// Roughly speaking:
// (1) dense_hash_set: fastest, uses the most memory unless entries are small
// (2) sparse_hash_set: slowest, uses the least memory
// (3) hash_set / unordered_set (STL): in the middle
//
// Typically I use sparse_hash_set when I care about space and/or when
// I need to save the hashtable on disk. I use hash_set otherwise. I
// don't personally use dense_hash_set ever; some people use it for
// small sets with lots of lookups.
//
// - dense_hash_set has, typically, about 78% memory overhead (if your
// data takes up X bytes, the hash_set uses .78X more bytes in overhead).
// - sparse_hash_set has about 4 bits overhead per entry.
// - sparse_hash_set can be 3-7 times slower than the others for lookup and,
// especially, inserts. See time_hash_map.cc for details.
//
// See /usr/(local/)?doc/sparsehash-*/dense_hash_set.html
// for information about how to use this class.
#ifndef _DENSE_HASH_SET_H_
#define _DENSE_HASH_SET_H_
#include <google/sparsehash/sparseconfig.h>
#include <stdio.h> // for FILE * in read()/write()
#include <algorithm> // for the default template args
#include <functional> // for equal_to
#include <memory> // for alloc<>
#include <utility> // for pair<>
#include HASH_FUN_H // defined in config.h
#include <google/sparsehash/libc_allocator_with_realloc.h>
#include <google/sparsehash/densehashtable.h>
_START_GOOGLE_NAMESPACE_
using STL_NAMESPACE::pair;
template <class Value,
class HashFcn = SPARSEHASH_HASH<Value>, // defined in sparseconfig.h
class EqualKey = STL_NAMESPACE::equal_to<Value>,
class Alloc = libc_allocator_with_realloc<Value> >
class dense_hash_set {
private:
// Apparently identity is not stl-standard, so we define our own
struct Identity {
typedef const Value& result_type;
const Value& operator()(const Value& v) const { return v; }
};
struct SetKey {
void operator()(Value* value, const Value& new_key) const {
*value = new_key;
}
};
// The actual data
typedef dense_hashtable<Value, Value, HashFcn, Identity, SetKey,
EqualKey, Alloc> ht;
ht rep;
public:
typedef typename ht::key_type key_type;
typedef typename ht::value_type value_type;
typedef typename ht::hasher hasher;
typedef typename ht::key_equal key_equal;
typedef Alloc allocator_type;
typedef typename ht::size_type size_type;
typedef typename ht::difference_type difference_type;
typedef typename ht::const_pointer pointer;
typedef typename ht::const_pointer const_pointer;
typedef typename ht::const_reference reference;
typedef typename ht::const_reference const_reference;
typedef typename ht::const_iterator iterator;
typedef typename ht::const_iterator const_iterator;
typedef typename ht::const_local_iterator local_iterator;
typedef typename ht::const_local_iterator const_local_iterator;
// Iterator functions -- recall all iterators are const
iterator begin() const { return rep.begin(); }
iterator end() const { return rep.end(); }
// These come from tr1's unordered_set. For us, a bucket has 0 or 1 elements.
local_iterator begin(size_type i) const { return rep.begin(i); }
local_iterator end(size_type i) const { return rep.end(i); }
// Accessor functions
allocator_type get_allocator() const { return rep.get_allocator(); }
hasher hash_funct() const { return rep.hash_funct(); }
hasher hash_function() const { return hash_funct(); } // tr1 name
key_equal key_eq() const { return rep.key_eq(); }
// Constructors
explicit dense_hash_set(size_type expected_max_items_in_table = 0,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type())
: rep(expected_max_items_in_table, hf, eql, Identity(), SetKey(), alloc) {
}
template <class InputIterator>
dense_hash_set(InputIterator f, InputIterator l,
const key_type& empty_key_val,
size_type expected_max_items_in_table = 0,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type())
: rep(expected_max_items_in_table, hf, eql, Identity(), SetKey(), alloc) {
set_empty_key(empty_key_val);
rep.insert(f, l);
}
// We use the default copy constructor
// We use the default operator=()
// We use the default destructor
void clear() { rep.clear(); }
// This clears the hash set without resizing it down to the minimum
// bucket count, but rather keeps the number of buckets constant
void clear_no_resize() { rep.clear_no_resize(); }
void swap(dense_hash_set& hs) { rep.swap(hs.rep); }
// Functions concerning size
size_type size() const { return rep.size(); }
size_type max_size() const { return rep.max_size(); }
bool empty() const { return rep.empty(); }
size_type bucket_count() const { return rep.bucket_count(); }
size_type max_bucket_count() const { return rep.max_bucket_count(); }
// These are tr1 methods. bucket() is the bucket the key is or would be in.
size_type bucket_size(size_type i) const { return rep.bucket_size(i); }
size_type bucket(const key_type& key) const { return rep.bucket(key); }
float load_factor() const {
return size() * 1.0f / bucket_count();
}
float max_load_factor() const {
float shrink, grow;
rep.get_resizing_parameters(&shrink, &grow);
return grow;
}
void max_load_factor(float new_grow) {
float shrink, grow;
rep.get_resizing_parameters(&shrink, &grow);
rep.set_resizing_parameters(shrink, new_grow);
}
// These aren't tr1 methods but perhaps ought to be.
float min_load_factor() const {
float shrink, grow;
rep.get_resizing_parameters(&shrink, &grow);
return shrink;
}
void min_load_factor(float new_shrink) {
float shrink, grow;
rep.get_resizing_parameters(&shrink, &grow);
rep.set_resizing_parameters(new_shrink, grow);
}
// Deprecated; use min_load_factor() or max_load_factor() instead.
void set_resizing_parameters(float shrink, float grow) {
rep.set_resizing_parameters(shrink, grow);
}
void resize(size_type hint) { rep.resize(hint); }
void rehash(size_type hint) { resize(hint); } // the tr1 name
// Lookup routines
iterator find(const key_type& key) const { return rep.find(key); }
size_type count(const key_type& key) const { return rep.count(key); }
pair<iterator, iterator> equal_range(const key_type& key) const {
return rep.equal_range(key);
}
// Insertion routines
pair<iterator, bool> insert(const value_type& obj) {
pair<typename ht::iterator, bool> p = rep.insert(obj);
return pair<iterator, bool>(p.first, p.second); // const to non-const
}
template <class InputIterator>
void insert(InputIterator f, InputIterator l) { rep.insert(f, l); }
void insert(const_iterator f, const_iterator l) { rep.insert(f, l); }
// required for std::insert_iterator; the passed-in iterator is ignored
iterator insert(iterator, const value_type& obj) { return insert(obj).first; }
// Deletion and empty routines
// THESE ARE NON-STANDARD! I make you specify an "impossible" key
// value to identify deleted and empty buckets. You can change the
// deleted key as time goes on, or get rid of it entirely to be insert-only.
void set_empty_key(const key_type& key) { rep.set_empty_key(key); }
key_type empty_key() const { return rep.empty_key(); }
void set_deleted_key(const key_type& key) { rep.set_deleted_key(key); }
void clear_deleted_key() { rep.clear_deleted_key(); }
key_type deleted_key() const { return rep.deleted_key(); }
// These are standard
size_type erase(const key_type& key) { return rep.erase(key); }
void erase(iterator it) { rep.erase(it); }
void erase(iterator f, iterator l) { rep.erase(f, l); }
// Comparison
bool operator==(const dense_hash_set& hs) const { return rep == hs.rep; }
bool operator!=(const dense_hash_set& hs) const { return rep != hs.rep; }
// I/O -- this is an add-on for writing metainformation to disk
bool write_metadata(FILE *fp) { return rep.write_metadata(fp); }
bool read_metadata(FILE *fp) { return rep.read_metadata(fp); }
bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); }
bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); }
};
template <class Val, class HashFcn, class EqualKey, class Alloc>
inline void swap(dense_hash_set<Val, HashFcn, EqualKey, Alloc>& hs1,
dense_hash_set<Val, HashFcn, EqualKey, Alloc>& hs2) {
hs1.swap(hs2);
}
_END_GOOGLE_NAMESPACE_
#endif /* _DENSE_HASH_SET_H_ */

310
external/google/sparse_hash_map vendored Normal file
View file

@ -0,0 +1,310 @@
// Copyright (c) 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * 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.
// * Neither the name of Google Inc. 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
// OWNER 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.
// ---
// Author: Craig Silverstein
//
// This is just a very thin wrapper over sparsehashtable.h, just
// like sgi stl's stl_hash_map is a very thin wrapper over
// stl_hashtable. The major thing we define is operator[], because
// we have a concept of a data_type which stl_hashtable doesn't
// (it only has a key and a value).
//
// We adhere mostly to the STL semantics for hash-map. One important
// exception is that insert() may invalidate iterators entirely -- STL
// semantics are that insert() may reorder iterators, but they all
// still refer to something valid in the hashtable. Not so for us.
// Likewise, insert() may invalidate pointers into the hashtable.
// (Whether insert invalidates iterators and pointers depends on
// whether it results in a hashtable resize). On the plus side,
// delete() doesn't invalidate iterators or pointers at all, or even
// change the ordering of elements.
//
// Here are a few "power user" tips:
//
// 1) set_deleted_key():
// Unlike STL's hash_map, if you want to use erase() you
// *must* call set_deleted_key() after construction.
//
// 2) resize(0):
// When an item is deleted, its memory isn't freed right
// away. This is what allows you to iterate over a hashtable
// and call erase() without invalidating the iterator.
// To force the memory to be freed, call resize(0).
// For tr1 compatibility, this can also be called as rehash(0).
//
// 3) min_load_factor(0.0)
// Setting the minimum load factor to 0.0 guarantees that
// the hash table will never shrink.
//
// Roughly speaking:
// (1) dense_hash_map: fastest, uses the most memory unless entries are small
// (2) sparse_hash_map: slowest, uses the least memory
// (3) hash_map / unordered_map (STL): in the middle
//
// Typically I use sparse_hash_map when I care about space and/or when
// I need to save the hashtable on disk. I use hash_map otherwise. I
// don't personally use dense_hash_map ever; some people use it for
// small maps with lots of lookups.
//
// - dense_hash_map has, typically, about 78% memory overhead (if your
// data takes up X bytes, the hash_map uses .78X more bytes in overhead).
// - sparse_hash_map has about 4 bits overhead per entry.
// - sparse_hash_map can be 3-7 times slower than the others for lookup and,
// especially, inserts. See time_hash_map.cc for details.
//
// See /usr/(local/)?doc/sparsehash-*/sparse_hash_map.html
// for information about how to use this class.
#ifndef _SPARSE_HASH_MAP_H_
#define _SPARSE_HASH_MAP_H_
#include <google/sparsehash/sparseconfig.h>
#include <stdio.h> // for FILE * in read()/write()
#include <algorithm> // for the default template args
#include <functional> // for equal_to
#include <memory> // for alloc<>
#include <utility> // for pair<>
#include HASH_FUN_H // defined in config.h
#include <google/sparsehash/libc_allocator_with_realloc.h>
#include <google/sparsehash/sparsehashtable.h>
_START_GOOGLE_NAMESPACE_
using STL_NAMESPACE::pair;
template <class Key, class T,
class HashFcn = SPARSEHASH_HASH<Key>, // defined in sparseconfig.h
class EqualKey = STL_NAMESPACE::equal_to<Key>,
class Alloc = libc_allocator_with_realloc<pair<const Key, T> > >
class sparse_hash_map {
private:
// Apparently select1st is not stl-standard, so we define our own
struct SelectKey {
typedef const Key& result_type;
const Key& operator()(const pair<const Key, T>& p) const {
return p.first;
}
};
struct SetKey {
void operator()(pair<const Key, T>* value, const Key& new_key) const {
*const_cast<Key*>(&value->first) = new_key;
// It would be nice to clear the rest of value here as well, in
// case it's taking up a lot of memory. We do this by clearing
// the value. This assumes T has a zero-arg constructor!
value->second = T();
}
};
// For operator[].
struct DefaultValue {
STL_NAMESPACE::pair<const Key, T> operator()(const Key& key) {
return STL_NAMESPACE::make_pair(key, T());
}
};
// The actual data
typedef sparse_hashtable<pair<const Key, T>, Key, HashFcn, SelectKey,
SetKey, EqualKey, Alloc> ht;
ht rep;
public:
typedef typename ht::key_type key_type;
typedef T data_type;
typedef T mapped_type;
typedef typename ht::value_type value_type;
typedef typename ht::hasher hasher;
typedef typename ht::key_equal key_equal;
typedef Alloc allocator_type;
typedef typename ht::size_type size_type;
typedef typename ht::difference_type difference_type;
typedef typename ht::pointer pointer;
typedef typename ht::const_pointer const_pointer;
typedef typename ht::reference reference;
typedef typename ht::const_reference const_reference;
typedef typename ht::iterator iterator;
typedef typename ht::const_iterator const_iterator;
typedef typename ht::local_iterator local_iterator;
typedef typename ht::const_local_iterator const_local_iterator;
// Iterator functions
iterator begin() { return rep.begin(); }
iterator end() { return rep.end(); }
const_iterator begin() const { return rep.begin(); }
const_iterator end() const { return rep.end(); }
// These come from tr1's unordered_map. For us, a bucket has 0 or 1 elements.
local_iterator begin(size_type i) { return rep.begin(i); }
local_iterator end(size_type i) { return rep.end(i); }
const_local_iterator begin(size_type i) const { return rep.begin(i); }
const_local_iterator end(size_type i) const { return rep.end(i); }
// Accessor functions
allocator_type get_allocator() const { return rep.get_allocator(); }
hasher hash_funct() const { return rep.hash_funct(); }
hasher hash_function() const { return hash_funct(); }
key_equal key_eq() const { return rep.key_eq(); }
// Constructors
explicit sparse_hash_map(size_type expected_max_items_in_table = 0,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type())
: rep(expected_max_items_in_table, hf, eql, SelectKey(), SetKey(), alloc) {
}
template <class InputIterator>
sparse_hash_map(InputIterator f, InputIterator l,
size_type expected_max_items_in_table = 0,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type())
: rep(expected_max_items_in_table, hf, eql, SelectKey(), SetKey(), alloc) {
rep.insert(f, l);
}
// We use the default copy constructor
// We use the default operator=()
// We use the default destructor
void clear() { rep.clear(); }
void swap(sparse_hash_map& hs) { rep.swap(hs.rep); }
// Functions concerning size
size_type size() const { return rep.size(); }
size_type max_size() const { return rep.max_size(); }
bool empty() const { return rep.empty(); }
size_type bucket_count() const { return rep.bucket_count(); }
size_type max_bucket_count() const { return rep.max_bucket_count(); }
// These are tr1 methods. bucket() is the bucket the key is or would be in.
size_type bucket_size(size_type i) const { return rep.bucket_size(i); }
size_type bucket(const key_type& key) const { return rep.bucket(key); }
float load_factor() const {
return size() * 1.0f / bucket_count();
}
float max_load_factor() const {
float shrink, grow;
rep.get_resizing_parameters(&shrink, &grow);
return grow;
}
void max_load_factor(float new_grow) {
float shrink, grow;
rep.get_resizing_parameters(&shrink, &grow);
rep.set_resizing_parameters(shrink, new_grow);
}
// These aren't tr1 methods but perhaps ought to be.
float min_load_factor() const {
float shrink, grow;
rep.get_resizing_parameters(&shrink, &grow);
return shrink;
}
void min_load_factor(float new_shrink) {
float shrink, grow;
rep.get_resizing_parameters(&shrink, &grow);
rep.set_resizing_parameters(new_shrink, grow);
}
// Deprecated; use min_load_factor() or max_load_factor() instead.
void set_resizing_parameters(float shrink, float grow) {
rep.set_resizing_parameters(shrink, grow);
}
void resize(size_type hint) { rep.resize(hint); }
void rehash(size_type hint) { resize(hint); } // the tr1 name
// Lookup routines
iterator find(const key_type& key) { return rep.find(key); }
const_iterator find(const key_type& key) const { return rep.find(key); }
data_type& operator[](const key_type& key) { // This is our value-add!
// If key is in the hashtable, returns find(key)->second,
// otherwise returns insert(value_type(key, T()).first->second.
// Note it does not create an empty T unless the find fails.
return rep.template find_or_insert<DefaultValue>(key).second;
}
size_type count(const key_type& key) const { return rep.count(key); }
pair<iterator, iterator> equal_range(const key_type& key) {
return rep.equal_range(key);
}
pair<const_iterator, const_iterator> equal_range(const key_type& key) const {
return rep.equal_range(key);
}
// Insertion routines
pair<iterator, bool> insert(const value_type& obj) { return rep.insert(obj); }
template <class InputIterator>
void insert(InputIterator f, InputIterator l) { rep.insert(f, l); }
void insert(const_iterator f, const_iterator l) { rep.insert(f, l); }
// required for std::insert_iterator; the passed-in iterator is ignored
iterator insert(iterator, const value_type& obj) { return insert(obj).first; }
// Deletion routines
// THESE ARE NON-STANDARD! I make you specify an "impossible" key
// value to identify deleted buckets. You can change the key as
// time goes on, or get rid of it entirely to be insert-only.
void set_deleted_key(const key_type& key) {
rep.set_deleted_key(key);
}
void clear_deleted_key() { rep.clear_deleted_key(); }
key_type deleted_key() const { return rep.deleted_key(); }
// These are standard
size_type erase(const key_type& key) { return rep.erase(key); }
void erase(iterator it) { rep.erase(it); }
void erase(iterator f, iterator l) { rep.erase(f, l); }
// Comparison
bool operator==(const sparse_hash_map& hs) const { return rep == hs.rep; }
bool operator!=(const sparse_hash_map& hs) const { return rep != hs.rep; }
// I/O -- this is an add-on for writing metainformation to disk
bool write_metadata(FILE *fp) { return rep.write_metadata(fp); }
bool read_metadata(FILE *fp) { return rep.read_metadata(fp); }
bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); }
bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); }
};
// We need a global swap as well
template <class Key, class T, class HashFcn, class EqualKey, class Alloc>
inline void swap(sparse_hash_map<Key, T, HashFcn, EqualKey, Alloc>& hm1,
sparse_hash_map<Key, T, HashFcn, EqualKey, Alloc>& hm2) {
hm1.swap(hm2);
}
_END_GOOGLE_NAMESPACE_
#endif /* _SPARSE_HASH_MAP_H_ */

285
external/google/sparse_hash_set vendored Normal file
View file

@ -0,0 +1,285 @@
// Copyright (c) 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * 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.
// * Neither the name of Google Inc. 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
// OWNER 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.
// ---
// Author: Craig Silverstein
//
// This is just a very thin wrapper over sparsehashtable.h, just
// like sgi stl's stl_hash_set is a very thin wrapper over
// stl_hashtable. The major thing we define is operator[], because
// we have a concept of a data_type which stl_hashtable doesn't
// (it only has a key and a value).
//
// This is more different from sparse_hash_map than you might think,
// because all iterators for sets are const (you obviously can't
// change the key, and for sets there is no value).
//
// We adhere mostly to the STL semantics for hash-map. One important
// exception is that insert() may invalidate iterators entirely -- STL
// semantics are that insert() may reorder iterators, but they all
// still refer to something valid in the hashtable. Not so for us.
// Likewise, insert() may invalidate pointers into the hashtable.
// (Whether insert invalidates iterators and pointers depends on
// whether it results in a hashtable resize). On the plus side,
// delete() doesn't invalidate iterators or pointers at all, or even
// change the ordering of elements.
//
// Here are a few "power user" tips:
//
// 1) set_deleted_key():
// Unlike STL's hash_map, if you want to use erase() you
// *must* call set_deleted_key() after construction.
//
// 2) resize(0):
// When an item is deleted, its memory isn't freed right
// away. This allows you to iterate over a hashtable,
// and call erase(), without invalidating the iterator.
// To force the memory to be freed, call resize(0).
// For tr1 compatibility, this can also be called as rehash(0).
//
// 3) min_load_factor(0.0)
// Setting the minimum load factor to 0.0 guarantees that
// the hash table will never shrink.
//
// Roughly speaking:
// (1) dense_hash_set: fastest, uses the most memory unless entries are small
// (2) sparse_hash_set: slowest, uses the least memory
// (3) hash_set / unordered_set (STL): in the middle
//
// Typically I use sparse_hash_set when I care about space and/or when
// I need to save the hashtable on disk. I use hash_set otherwise. I
// don't personally use dense_hash_set ever; some people use it for
// small sets with lots of lookups.
//
// - dense_hash_set has, typically, about 78% memory overhead (if your
// data takes up X bytes, the hash_set uses .78X more bytes in overhead).
// - sparse_hash_set has about 4 bits overhead per entry.
// - sparse_hash_set can be 3-7 times slower than the others for lookup and,
// especially, inserts. See time_hash_map.cc for details.
//
// See /usr/(local/)?doc/sparsehash-*/sparse_hash_set.html
// for information about how to use this class.
#ifndef _SPARSE_HASH_SET_H_
#define _SPARSE_HASH_SET_H_
#include <google/sparsehash/sparseconfig.h>
#include <stdio.h> // for FILE * in read()/write()
#include <algorithm> // for the default template args
#include <functional> // for equal_to
#include <memory> // for alloc<>
#include <utility> // for pair<>
#include HASH_FUN_H // defined in config.h
#include <google/sparsehash/libc_allocator_with_realloc.h>
#include <google/sparsehash/sparsehashtable.h>
_START_GOOGLE_NAMESPACE_
using STL_NAMESPACE::pair;
template <class Value,
class HashFcn = SPARSEHASH_HASH<Value>, // defined in sparseconfig.h
class EqualKey = STL_NAMESPACE::equal_to<Value>,
class Alloc = libc_allocator_with_realloc<Value> >
class sparse_hash_set {
private:
// Apparently identity is not stl-standard, so we define our own
struct Identity {
typedef const Value& result_type;
const Value& operator()(const Value& v) const { return v; }
};
struct SetKey {
void operator()(Value* value, const Value& new_key) const {
*value = new_key;
}
};
typedef sparse_hashtable<Value, Value, HashFcn, Identity, SetKey,
EqualKey, Alloc> ht;
ht rep;
public:
typedef typename ht::key_type key_type;
typedef typename ht::value_type value_type;
typedef typename ht::hasher hasher;
typedef typename ht::key_equal key_equal;
typedef Alloc allocator_type;
typedef typename ht::size_type size_type;
typedef typename ht::difference_type difference_type;
typedef typename ht::const_pointer pointer;
typedef typename ht::const_pointer const_pointer;
typedef typename ht::const_reference reference;
typedef typename ht::const_reference const_reference;
typedef typename ht::const_iterator iterator;
typedef typename ht::const_iterator const_iterator;
typedef typename ht::const_local_iterator local_iterator;
typedef typename ht::const_local_iterator const_local_iterator;
// Iterator functions -- recall all iterators are const
iterator begin() const { return rep.begin(); }
iterator end() const { return rep.end(); }
// These come from tr1's unordered_set. For us, a bucket has 0 or 1 elements.
local_iterator begin(size_type i) const { return rep.begin(i); }
local_iterator end(size_type i) const { return rep.end(i); }
// Accessor functions
allocator_type get_allocator() const { return rep.get_allocator(); }
hasher hash_funct() const { return rep.hash_funct(); }
hasher hash_function() const { return hash_funct(); } // tr1 name
key_equal key_eq() const { return rep.key_eq(); }
// Constructors
explicit sparse_hash_set(size_type expected_max_items_in_table = 0,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type())
: rep(expected_max_items_in_table, hf, eql, Identity(), SetKey(), alloc) {
}
template <class InputIterator>
sparse_hash_set(InputIterator f, InputIterator l,
size_type expected_max_items_in_table = 0,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type())
: rep(expected_max_items_in_table, hf, eql, Identity(), SetKey(), alloc) {
rep.insert(f, l);
}
// We use the default copy constructor
// We use the default operator=()
// We use the default destructor
void clear() { rep.clear(); }
void swap(sparse_hash_set& hs) { rep.swap(hs.rep); }
// Functions concerning size
size_type size() const { return rep.size(); }
size_type max_size() const { return rep.max_size(); }
bool empty() const { return rep.empty(); }
size_type bucket_count() const { return rep.bucket_count(); }
size_type max_bucket_count() const { return rep.max_bucket_count(); }
// These are tr1 methods. bucket() is the bucket the key is or would be in.
size_type bucket_size(size_type i) const { return rep.bucket_size(i); }
size_type bucket(const key_type& key) const { return rep.bucket(key); }
float load_factor() const {
return size() * 1.0f / bucket_count();
}
float max_load_factor() const {
float shrink, grow;
rep.get_resizing_parameters(&shrink, &grow);
return grow;
}
void max_load_factor(float new_grow) {
float shrink, grow;
rep.get_resizing_parameters(&shrink, &grow);
rep.set_resizing_parameters(shrink, new_grow);
}
// These aren't tr1 methods but perhaps ought to be.
float min_load_factor() const {
float shrink, grow;
rep.get_resizing_parameters(&shrink, &grow);
return shrink;
}
void min_load_factor(float new_shrink) {
float shrink, grow;
rep.get_resizing_parameters(&shrink, &grow);
rep.set_resizing_parameters(new_shrink, grow);
}
// Deprecated; use min_load_factor() or max_load_factor() instead.
void set_resizing_parameters(float shrink, float grow) {
rep.set_resizing_parameters(shrink, grow);
}
void resize(size_type hint) { rep.resize(hint); }
void rehash(size_type hint) { resize(hint); } // the tr1 name
// Lookup routines
iterator find(const key_type& key) const { return rep.find(key); }
size_type count(const key_type& key) const { return rep.count(key); }
pair<iterator, iterator> equal_range(const key_type& key) const {
return rep.equal_range(key);
}
// Insertion routines
pair<iterator, bool> insert(const value_type& obj) {
pair<typename ht::iterator, bool> p = rep.insert(obj);
return pair<iterator, bool>(p.first, p.second); // const to non-const
}
template <class InputIterator>
void insert(InputIterator f, InputIterator l) { rep.insert(f, l); }
void insert(const_iterator f, const_iterator l) { rep.insert(f, l); }
// required for std::insert_iterator; the passed-in iterator is ignored
iterator insert(iterator, const value_type& obj) { return insert(obj).first; }
// Deletion routines
// THESE ARE NON-STANDARD! I make you specify an "impossible" key
// value to identify deleted buckets. You can change the key as
// time goes on, or get rid of it entirely to be insert-only.
void set_deleted_key(const key_type& key) { rep.set_deleted_key(key); }
void clear_deleted_key() { rep.clear_deleted_key(); }
key_type deleted_key() const { return rep.deleted_key(); }
// These are standard
size_type erase(const key_type& key) { return rep.erase(key); }
void erase(iterator it) { rep.erase(it); }
void erase(iterator f, iterator l) { rep.erase(f, l); }
// Comparison
bool operator==(const sparse_hash_set& hs) const { return rep == hs.rep; }
bool operator!=(const sparse_hash_set& hs) const { return rep != hs.rep; }
// I/O -- this is an add-on for writing metainformation to disk
bool write_metadata(FILE *fp) { return rep.write_metadata(fp); }
bool read_metadata(FILE *fp) { return rep.read_metadata(fp); }
bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); }
bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); }
};
template <class Val, class HashFcn, class EqualKey, class Alloc>
inline void swap(sparse_hash_set<Val, HashFcn, EqualKey, Alloc>& hs1,
sparse_hash_set<Val, HashFcn, EqualKey, Alloc>& hs2) {
hs1.swap(hs2);
}
_END_GOOGLE_NAMESPACE_
#endif /* _SPARSE_HASH_SET_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,178 @@
// Copyright (c) 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * 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.
// * Neither the name of Google Inc. 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
// OWNER 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.
// ---
// Author: Giao Nguyen
#ifndef UTIL_GTL_HASHTABLE_COMMON_H_
#define UTIL_GTL_HASHTABLE_COMMON_H_
#include <assert.h>
// Settings contains parameters for growing and shrinking the table.
// It also packages zero-size functor (ie. hasher).
template<typename Key, typename HashFunc,
typename SizeType, int HT_MIN_BUCKETS>
class sh_hashtable_settings : public HashFunc {
public:
typedef Key key_type;
typedef HashFunc hasher;
typedef SizeType size_type;
public:
sh_hashtable_settings(const hasher& hf,
const float ht_occupancy_flt,
const float ht_empty_flt)
: hasher(hf),
enlarge_threshold_(0),
shrink_threshold_(0),
consider_shrink_(false),
use_empty_(false),
use_deleted_(false),
num_ht_copies_(0) {
set_enlarge_factor(ht_occupancy_flt);
set_shrink_factor(ht_empty_flt);
}
size_type hash(const key_type& v) const {
return hasher::operator()(v);
}
float enlarge_factor() const {
return enlarge_factor_;
}
void set_enlarge_factor(float f) {
enlarge_factor_ = f;
}
float shrink_factor() const {
return shrink_factor_;
}
void set_shrink_factor(float f) {
shrink_factor_ = f;
}
size_type enlarge_threshold() const {
return enlarge_threshold_;
}
void set_enlarge_threshold(size_type t) {
enlarge_threshold_ = t;
}
size_type shrink_threshold() const {
return shrink_threshold_;
}
void set_shrink_threshold(size_type t) {
shrink_threshold_ = t;
}
size_type enlarge_size(size_type x) const {
return static_cast<size_type>(x * enlarge_factor_);
}
size_type shrink_size(size_type x) const {
return static_cast<size_type>(x * shrink_factor_);
}
bool consider_shrink() const {
return consider_shrink_;
}
void set_consider_shrink(bool t) {
consider_shrink_ = t;
}
bool use_empty() const {
return use_empty_;
}
void set_use_empty(bool t) {
use_empty_ = t;
}
bool use_deleted() const {
return use_deleted_;
}
void set_use_deleted(bool t) {
use_deleted_ = t;
}
size_type num_ht_copies() const {
return static_cast<size_type>(num_ht_copies_);
}
void inc_num_ht_copies() {
++num_ht_copies_;
}
// Reset the enlarge and shrink thresholds
void reset_thresholds(size_type num_buckets) {
set_enlarge_threshold(enlarge_size(num_buckets));
set_shrink_threshold(shrink_size(num_buckets));
// whatever caused us to reset already considered
set_consider_shrink(false);
}
// Caller is resposible for calling reset_threshold right after
// set_resizing_parameters.
void set_resizing_parameters(float shrink, float grow) {
assert(shrink >= 0.0);
assert(grow <= 1.0);
if (shrink > grow/2.0f)
shrink = grow / 2.0f; // otherwise we thrash hashtable size
set_shrink_factor(shrink);
set_enlarge_factor(grow);
}
// This is the smallest size a hashtable can be without being too crowded
// If you like, you can give a min #buckets as well as a min #elts
size_type min_buckets(size_type num_elts, size_type min_buckets_wanted) {
float enlarge = enlarge_factor();
size_type sz = HT_MIN_BUCKETS; // min buckets allowed
while ( sz < min_buckets_wanted ||
num_elts >= static_cast<size_type>(sz * enlarge) ) {
// This just prevents overflowing size_type, since sz can exceed
// max_size() here.
if (static_cast<size_type>(sz * 2) < sz) {
throw std::length_error("resize overflow"); // protect against overflow
}
sz *= 2;
}
return sz;
}
private:
size_type enlarge_threshold_; // table.size() * enlarge_factor
size_type shrink_threshold_; // table.size() * shrink_factor
float enlarge_factor_; // how full before resize
float shrink_factor_; // how empty before resize
// consider_shrink=true if we should try to shrink before next insert
bool consider_shrink_;
bool use_empty_; // used only by densehashtable, not sparsehashtable
bool use_deleted_; // false until delkey has been set
// num_ht_copies is a counter incremented every Copy/Move
unsigned int num_ht_copies_;
};
#endif // UTIL_GTL_HASHTABLE_COMMON_H_

View file

@ -0,0 +1,121 @@
// Copyright (c) 2010, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * 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.
// * Neither the name of Google Inc. 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
// OWNER 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.
// ---
// Author: Guilin Chen
#ifndef UTIL_GTL_LIBC_ALLOCATOR_WITH_REALLOC_H_
#define UTIL_GTL_LIBC_ALLOCATOR_WITH_REALLOC_H_
#include <google/sparsehash/sparseconfig.h>
#include <stdlib.h> // for malloc/realloc/free
#include <stddef.h> // for ptrdiff_t
_START_GOOGLE_NAMESPACE_
template<class T>
class libc_allocator_with_realloc {
public:
typedef T value_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
libc_allocator_with_realloc() {}
libc_allocator_with_realloc(const libc_allocator_with_realloc&) {}
~libc_allocator_with_realloc() {}
pointer address(reference r) const { return &r; }
const_pointer address(const_reference r) const { return &r; }
pointer allocate(size_type n, const_pointer = 0) {
return static_cast<pointer>(malloc(n * sizeof(value_type)));
}
void deallocate(pointer p, size_type) {
free(p);
}
pointer reallocate(pointer p, size_type n) {
return static_cast<pointer>(realloc(p, n * sizeof(value_type)));
}
size_type max_size() const {
return static_cast<size_type>(-1) / sizeof(value_type);
}
void construct(pointer p, const value_type& val) {
new(p) value_type(val);
}
void destroy(pointer p) { p->~value_type(); }
template <class U>
libc_allocator_with_realloc(const libc_allocator_with_realloc<U>&) {}
template<class U>
struct rebind {
typedef libc_allocator_with_realloc<U> other;
};
};
// libc_allocator_with_realloc<void> specialization.
template<>
class libc_allocator_with_realloc<void> {
public:
typedef void value_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef void* pointer;
typedef const void* const_pointer;
template<class U>
struct rebind {
typedef libc_allocator_with_realloc<U> other;
};
};
template<class T>
inline bool operator==(const libc_allocator_with_realloc<T>&,
const libc_allocator_with_realloc<T>&) {
return true;
}
template<class T>
inline bool operator!=(const libc_allocator_with_realloc<T>&,
const libc_allocator_with_realloc<T>&) {
return false;
}
_END_GOOGLE_NAMESPACE_
#endif // UTIL_GTL_LIBC_ALLOCATOR_WITH_REALLOC_H_

37
external/google/sparsehash/os_config.h vendored Normal file
View file

@ -0,0 +1,37 @@
#ifndef _MSC_VER
//non-win version
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if the system has the type `uint16_t'. */
#define HAVE_UINT16_T 1
/* Define to 1 if the system has the type `u_int16_t'. */
#define HAVE_U_INT16_T 1
/* Define to 1 if the system has the type `__uint16'. */
/* #undef HAVE___UINT16 */
#else
//win version
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if the system has the type `uint16_t'. */
#undef HAVE_UINT16_T
/* Define to 1 if the system has the type `u_int16_t'. */
#undef HAVE_U_INT16_T
/* Define to 1 if the system has the type `__uint16'. */
#define HAVE___UINT16 1
#endif

View file

@ -0,0 +1,39 @@
/*
* NOTE: This file is for internal use only.
* Do not use these #defines in your own program!
*/
/* Namespace for Google classes */
#define GOOGLE_NAMESPACE ::google
/* the location of the header defining hash functions */
#define HASH_FUN_H <functional>
/* the namespace of the hash<> function */
#define HASH_NAMESPACE std
/* Define to 1 if the system has the type `long long'. */
#define HAVE_LONG_LONG 1
/* Define to 1 if you have the `memcpy' function. */
#define HAVE_MEMCPY 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* The system-provided hash function including the namespace. */
#define SPARSEHASH_HASH HASH_NAMESPACE::hash
/* The system-provided hash function, in namespace HASH_NAMESPACE. */
#define SPARSEHASH_HASH_NO_NAMESPACE hash
/* the namespace where STL code like vector<> is defined */
#define STL_NAMESPACE std
/* Stops putting the code inside the Google namespace */
#define _END_GOOGLE_NAMESPACE_ }
/* Puts following code inside the Google namespace */
#define _START_GOOGLE_NAMESPACE_ namespace google {
#include "os_config.h"

View file

@ -0,0 +1,37 @@
/*
* NOTE: This file is for internal use only.
* Do not use these #defines in your own program!
*/
/* Namespace for Google classes */
#define GOOGLE_NAMESPACE ::google
/* the location of the header defining hash functions */
#define HASH_FUN_H <functional>
/* the namespace of the hash<> function */
#define HASH_NAMESPACE std
/* Define to 1 if the system has the type `long long'. */
#define HAVE_LONG_LONG 1
/* Define to 1 if you have the `memcpy' function. */
#define HAVE_MEMCPY 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* The system-provided hash function including the namespace. */
#define SPARSEHASH_HASH HASH_NAMESPACE::hash
/* The system-provided hash function, in namespace HASH_NAMESPACE. */
#define SPARSEHASH_HASH_NO_NAMESPACE hash
/* the namespace where STL code like vector<> is defined */
#define STL_NAMESPACE std
/* Stops putting the code inside the Google namespace */
#define _END_GOOGLE_NAMESPACE_ }
/* Puts following code inside the Google namespace */
#define _START_GOOGLE_NAMESPACE_ namespace google {

File diff suppressed because it is too large Load diff

1598
external/google/sparsetable vendored Normal file

File diff suppressed because it is too large Load diff

336
external/google/type_traits.h vendored Normal file
View file

@ -0,0 +1,336 @@
// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * 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.
// * Neither the name of Google Inc. 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
// OWNER 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.
// ----
// Author: Matt Austern
//
// Define a small subset of tr1 type traits. The traits we define are:
// is_integral
// is_floating_point
// is_pointer
// is_enum
// is_reference
// is_pod
// has_trivial_constructor
// has_trivial_copy
// has_trivial_assign
// has_trivial_destructor
// remove_const
// remove_volatile
// remove_cv
// remove_reference
// add_reference
// remove_pointer
// is_same
// is_convertible
// We can add more type traits as required.
#ifndef BASE_TYPE_TRAITS_H_
#define BASE_TYPE_TRAITS_H_
#include <google/sparsehash/sparseconfig.h>
#include <utility> // For pair
_START_GOOGLE_NAMESPACE_
// integral_constant, defined in tr1, is a wrapper for an integer
// value. We don't really need this generality; we could get away
// with hardcoding the integer type to bool. We use the fully
// general integer_constant for compatibility with tr1.
template<class T, T v>
struct integral_constant {
static const T value = v;
typedef T value_type;
typedef integral_constant<T, v> type;
};
template <class T, T v> const T integral_constant<T, v>::value;
// Abbreviations: true_type and false_type are structs that represent
// boolean true and false values.
typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;
// Types small_ and big_ are guaranteed such that sizeof(small_) <
// sizeof(big_)
typedef char small_;
struct big_ {
char dummy[2];
};
template <class T> struct is_integral;
template <class T> struct is_floating_point;
template <class T> struct is_pointer;
// MSVC can't compile this correctly, and neither can gcc 3.3.5 (at least)
#if !defined(_MSC_VER) && !(defined(__GNUC__) && __GNUC__ <= 3)
// is_enum uses is_convertible, which is not available on MSVC.
template <class T> struct is_enum;
#endif
template <class T> struct is_reference;
template <class T> struct is_pod;
template <class T> struct has_trivial_constructor;
template <class T> struct has_trivial_copy;
template <class T> struct has_trivial_assign;
template <class T> struct has_trivial_destructor;
template <class T> struct remove_const;
template <class T> struct remove_volatile;
template <class T> struct remove_cv;
template <class T> struct remove_reference;
template <class T> struct add_reference;
template <class T> struct remove_pointer;
template <class T, class U> struct is_same;
#if !defined(_MSC_VER) && !(defined(__GNUC__) && __GNUC__ <= 3)
template <class From, class To> struct is_convertible;
#endif
// is_integral is false except for the built-in integer types.
template <class T> struct is_integral : false_type { };
template<> struct is_integral<bool> : true_type { };
template<> struct is_integral<char> : true_type { };
template<> struct is_integral<unsigned char> : true_type { };
template<> struct is_integral<signed char> : true_type { };
#if defined(_MSC_VER)
// wchar_t is not by default a distinct type from unsigned short in
// Microsoft C.
// See http://msdn2.microsoft.com/en-us/library/dh8che7s(VS.80).aspx
template<> struct is_integral<__wchar_t> : true_type { };
#else
template<> struct is_integral<wchar_t> : true_type { };
#endif
template<> struct is_integral<short> : true_type { };
template<> struct is_integral<unsigned short> : true_type { };
template<> struct is_integral<int> : true_type { };
template<> struct is_integral<unsigned int> : true_type { };
template<> struct is_integral<long> : true_type { };
template<> struct is_integral<unsigned long> : true_type { };
#ifdef HAVE_LONG_LONG
template<> struct is_integral<long long> : true_type { };
template<> struct is_integral<unsigned long long> : true_type { };
#endif
// is_floating_point is false except for the built-in floating-point types.
template <class T> struct is_floating_point : false_type { };
template<> struct is_floating_point<float> : true_type { };
template<> struct is_floating_point<double> : true_type { };
template<> struct is_floating_point<long double> : true_type { };
// is_pointer is false except for pointer types.
template <class T> struct is_pointer : false_type { };
template <class T> struct is_pointer<T*> : true_type { };
#if !defined(_MSC_VER) && !(defined(__GNUC__) && __GNUC__ <= 3)
namespace internal {
template <class T> struct is_class_or_union {
template <class U> static small_ tester(void (U::*)());
template <class U> static big_ tester(...);
static const bool value = sizeof(tester<T>(0)) == sizeof(small_);
};
// is_convertible chokes if the first argument is an array. That's why
// we use add_reference here.
template <bool NotUnum, class T> struct is_enum_impl
: is_convertible<typename add_reference<T>::type, int> { };
template <class T> struct is_enum_impl<true, T> : false_type { };
} // namespace internal
// Specified by TR1 [4.5.1] primary type categories.
// Implementation note:
//
// Each type is either void, integral, floating point, array, pointer,
// reference, member object pointer, member function pointer, enum,
// union or class. Out of these, only integral, floating point, reference,
// class and enum types are potentially convertible to int. Therefore,
// if a type is not a reference, integral, floating point or class and
// is convertible to int, it's a enum.
//
// Is-convertible-to-int check is done only if all other checks pass,
// because it can't be used with some types (e.g. void or classes with
// inaccessible conversion operators).
template <class T> struct is_enum
: internal::is_enum_impl<
is_same<T, void>::value ||
is_integral<T>::value ||
is_floating_point<T>::value ||
is_reference<T>::value ||
internal::is_class_or_union<T>::value,
T> { };
template <class T> struct is_enum<const T> : is_enum<T> { };
template <class T> struct is_enum<volatile T> : is_enum<T> { };
template <class T> struct is_enum<const volatile T> : is_enum<T> { };
#endif
// is_reference is false except for reference types.
template<typename T> struct is_reference : false_type {};
template<typename T> struct is_reference<T&> : true_type {};
// We can't get is_pod right without compiler help, so fail conservatively.
// We will assume it's false except for arithmetic types, enumerations,
// pointers and const versions thereof. Note that std::pair is not a POD.
template <class T> struct is_pod
: integral_constant<bool, (is_integral<T>::value ||
is_floating_point<T>::value ||
#if !defined(_MSC_VER) && !(defined(__GNUC__) && __GNUC__ <= 3)
// is_enum is not available on MSVC.
is_enum<T>::value ||
#endif
is_pointer<T>::value)> { };
template <class T> struct is_pod<const T> : is_pod<T> { };
// We can't get has_trivial_constructor right without compiler help, so
// fail conservatively. We will assume it's false except for: (1) types
// for which is_pod is true. (2) std::pair of types with trivial
// constructors. (3) array of a type with a trivial constructor.
// (4) const versions thereof.
template <class T> struct has_trivial_constructor : is_pod<T> { };
template <class T, class U> struct has_trivial_constructor<std::pair<T, U> >
: integral_constant<bool,
(has_trivial_constructor<T>::value &&
has_trivial_constructor<U>::value)> { };
template <class A, int N> struct has_trivial_constructor<A[N]>
: has_trivial_constructor<A> { };
template <class T> struct has_trivial_constructor<const T>
: has_trivial_constructor<T> { };
// We can't get has_trivial_copy right without compiler help, so fail
// conservatively. We will assume it's false except for: (1) types
// for which is_pod is true. (2) std::pair of types with trivial copy
// constructors. (3) array of a type with a trivial copy constructor.
// (4) const versions thereof.
template <class T> struct has_trivial_copy : is_pod<T> { };
template <class T, class U> struct has_trivial_copy<std::pair<T, U> >
: integral_constant<bool,
(has_trivial_copy<T>::value &&
has_trivial_copy<U>::value)> { };
template <class A, int N> struct has_trivial_copy<A[N]>
: has_trivial_copy<A> { };
template <class T> struct has_trivial_copy<const T> : has_trivial_copy<T> { };
// We can't get has_trivial_assign right without compiler help, so fail
// conservatively. We will assume it's false except for: (1) types
// for which is_pod is true. (2) std::pair of types with trivial copy
// constructors. (3) array of a type with a trivial assign constructor.
template <class T> struct has_trivial_assign : is_pod<T> { };
template <class T, class U> struct has_trivial_assign<std::pair<T, U> >
: integral_constant<bool,
(has_trivial_assign<T>::value &&
has_trivial_assign<U>::value)> { };
template <class A, int N> struct has_trivial_assign<A[N]>
: has_trivial_assign<A> { };
// We can't get has_trivial_destructor right without compiler help, so
// fail conservatively. We will assume it's false except for: (1) types
// for which is_pod is true. (2) std::pair of types with trivial
// destructors. (3) array of a type with a trivial destructor.
// (4) const versions thereof.
template <class T> struct has_trivial_destructor : is_pod<T> { };
template <class T, class U> struct has_trivial_destructor<std::pair<T, U> >
: integral_constant<bool,
(has_trivial_destructor<T>::value &&
has_trivial_destructor<U>::value)> { };
template <class A, int N> struct has_trivial_destructor<A[N]>
: has_trivial_destructor<A> { };
template <class T> struct has_trivial_destructor<const T>
: has_trivial_destructor<T> { };
// Specified by TR1 [4.7.1]
template<typename T> struct remove_const { typedef T type; };
template<typename T> struct remove_const<T const> { typedef T type; };
template<typename T> struct remove_volatile { typedef T type; };
template<typename T> struct remove_volatile<T volatile> { typedef T type; };
template<typename T> struct remove_cv {
typedef typename remove_const<typename remove_volatile<T>::type>::type type;
};
// Specified by TR1 [4.7.2] Reference modifications.
template<typename T> struct remove_reference { typedef T type; };
template<typename T> struct remove_reference<T&> { typedef T type; };
template <typename T> struct add_reference { typedef T& type; };
template <typename T> struct add_reference<T&> { typedef T& type; };
// Specified by TR1 [4.7.4] Pointer modifications.
template<typename T> struct remove_pointer { typedef T type; };
template<typename T> struct remove_pointer<T*> { typedef T type; };
template<typename T> struct remove_pointer<T* const> { typedef T type; };
template<typename T> struct remove_pointer<T* volatile> { typedef T type; };
template<typename T> struct remove_pointer<T* const volatile> {
typedef T type; };
// Specified by TR1 [4.6] Relationships between types
template<typename T, typename U> struct is_same : public false_type { };
template<typename T> struct is_same<T, T> : public true_type { };
// Specified by TR1 [4.6] Relationships between types
#if !defined(_MSC_VER) && !(defined(__GNUC__) && __GNUC__ <= 3)
namespace internal {
// This class is an implementation detail for is_convertible, and you
// don't need to know how it works to use is_convertible. For those
// who care: we declare two different functions, one whose argument is
// of type To and one with a variadic argument list. We give them
// return types of different size, so we can use sizeof to trick the
// compiler into telling us which function it would have chosen if we
// had called it with an argument of type From. See Alexandrescu's
// _Modern C++ Design_ for more details on this sort of trick.
template <typename From, typename To>
struct ConvertHelper {
static small_ Test(To);
static big_ Test(...);
static From Create();
};
} // namespace internal
// Inherits from true_type if From is convertible to To, false_type otherwise.
template <typename From, typename To>
struct is_convertible
: integral_constant<bool,
sizeof(internal::ConvertHelper<From, To>::Test(
internal::ConvertHelper<From, To>::Create()))
== sizeof(small_)> {
};
#endif
_END_GOOGLE_NAMESPACE_
#endif // BASE_TYPE_TRAITS_H_

View file

@ -1,4 +1,4 @@
// Copyright (c) 2012-2013 The Cryptonote developers
// Copyright (c) 2011-2015 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@ -22,6 +22,7 @@ public:
virtual void peerCountUpdated(size_t count) {}
virtual void localBlockchainUpdated(uint64_t height) {}
virtual void lastKnownBlockHeightUpdated(uint64_t height) {}
virtual void poolChanged() {}
};
struct OutEntry {
@ -34,6 +35,12 @@ struct OutsForAmount {
std::vector<OutEntry> outs;
};
struct BlockCompleteEntry {
crypto::hash blockHash;
cryptonote::blobdata block;
std::list<cryptonote::blobdata> txs;
};
class INode {
public:
typedef std::function<void(std::error_code)> Callback;
@ -48,11 +55,14 @@ public:
virtual size_t getPeerCount() const = 0;
virtual uint64_t getLastLocalBlockHeight() const = 0;
virtual uint64_t getLastKnownBlockHeight() const = 0;
virtual uint64_t getLastLocalBlockTimestamp() const = 0;
virtual void relayTransaction(const cryptonote::transaction& transaction, const Callback& callback) = 0;
virtual void relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) = 0;
virtual void getRandomOutsByAmounts(std::vector<uint64_t>&& amounts, uint64_t outsCount, std::vector<cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount>& result, const Callback& callback) = 0;
virtual void getNewBlocks(std::list<crypto::hash>&& knownBlockIds, std::list<cryptonote::block_complete_entry>& newBlocks, uint64_t& startHeight, const Callback& callback) = 0;
virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector<uint64_t>& outsGlobalIndices, const Callback& callback) = 0;
virtual void queryBlocks(std::list<crypto::hash>&& knownBlockIds, uint64_t timestamp, std::list<BlockCompleteEntry>& newBlocks, uint64_t& startHeight, const Callback& callback) = 0;
virtual void getPoolSymmetricDifference(std::vector<crypto::hash>&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector<cryptonote::Transaction>& new_txs, std::vector<crypto::hash>& deleted_tx_ids, const Callback& callback) = 0;
};
}

16
include/IObservable.h Normal file
View file

@ -0,0 +1,16 @@
// Copyright (c) 2011-2015 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
namespace CryptoNote {
template <typename T>
class IObservable {
public:
virtual void addObserver(T* observer) = 0;
virtual void removeObserver(T* observer) = 0;
};
}

View file

@ -0,0 +1,17 @@
// Copyright (c) 2011-2015 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 <sstream>
namespace CryptoNote {
class IStreamSerializable {
public:
virtual void save(std::ostream& os) = 0;
virtual void load(std::istream& in) = 0;
};
}

167
include/ITransaction.h Normal file
View file

@ -0,0 +1,167 @@
// Copyright (c) 2011-2015 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 <array>
#include <cstdint>
#include <vector>
namespace CryptoNote {
typedef std::array<uint8_t, 32> PublicKey;
typedef std::array<uint8_t, 32> SecretKey;
typedef std::array<uint8_t, 32> KeyImage;
typedef std::array<uint8_t, 32> Hash;
typedef std::vector<uint8_t> Blob;
struct AccountAddress {
PublicKey spendPublicKey;
PublicKey viewPublicKey;
};
struct AccountKeys {
AccountAddress address;
SecretKey spendSecretKey;
SecretKey viewSecretKey;
};
struct KeyPair {
PublicKey publicKey;
SecretKey secretKey;
};
namespace TransactionTypes {
enum class InputType : uint8_t { Invalid, Key, Multisignature, Generating };
enum class OutputType : uint8_t { Invalid, Key, Multisignature };
struct InputKey {
uint64_t amount;
std::vector<uint64_t> keyOffsets;
KeyImage keyImage; // double spending protection
};
struct InputMultisignature {
uint64_t amount;
uint32_t signatures;
uint64_t outputIndex;
};
struct OutputKey {
uint64_t amount;
PublicKey key;
};
struct OutputMultisignature {
uint64_t amount;
std::vector<PublicKey> keys;
uint32_t requiredSignatures;
};
struct GlobalOutput {
PublicKey targetKey;
uint64_t outputIndex;
};
typedef std::vector<GlobalOutput> GlobalOutputsContainer;
struct OutputKeyInfo {
PublicKey transactionPublicKey;
size_t transactionIndex;
size_t outputInTransaction;
};
struct InputKeyInfo {
uint64_t amount;
GlobalOutputsContainer outputs;
OutputKeyInfo realOutput;
};
}
//
// ITransactionReader
//
class ITransactionReader {
public:
virtual ~ITransactionReader() { }
virtual Hash getTransactionHash() const = 0;
virtual Hash getTransactionPrefixHash() const = 0;
virtual PublicKey getTransactionPublicKey() const = 0;
virtual uint64_t getUnlockTime() const = 0;
// extra
virtual bool getPaymentId(Hash& paymentId) const = 0;
virtual bool getExtraNonce(std::string& nonce) const = 0;
// inputs
virtual size_t getInputCount() const = 0;
virtual uint64_t getInputTotalAmount() const = 0;
virtual TransactionTypes::InputType getInputType(size_t index) const = 0;
virtual void getInput(size_t index, TransactionTypes::InputKey& input) const = 0;
virtual void getInput(size_t index, TransactionTypes::InputMultisignature& input) const = 0;
// outputs
virtual size_t getOutputCount() const = 0;
virtual uint64_t getOutputTotalAmount() const = 0;
virtual TransactionTypes::OutputType getOutputType(size_t index) const = 0;
virtual void getOutput(size_t index, TransactionTypes::OutputKey& output) const = 0;
virtual void getOutput(size_t index, TransactionTypes::OutputMultisignature& output) const = 0;
// signatures
virtual size_t getRequiredSignaturesCount(size_t inputIndex) const = 0;
virtual bool findOutputsToAccount(const AccountAddress& addr, const SecretKey& viewSecretKey, std::vector<uint32_t>& outs, uint64_t& outputAmount) const = 0;
// various checks
virtual bool validateInputs() const = 0;
virtual bool validateOutputs() const = 0;
virtual bool validateSignatures() const = 0;
// serialized transaction
virtual Blob getTransactionData() const = 0;
};
//
// ITransactionWriter
//
class ITransactionWriter {
public:
virtual ~ITransactionWriter() { }
// transaction parameters
virtual void setUnlockTime(uint64_t unlockTime) = 0;
// extra
virtual void setPaymentId(const Hash& paymentId) = 0;
virtual void setExtraNonce(const std::string& nonce) = 0;
// Inputs/Outputs
virtual size_t addInput(const TransactionTypes::InputKey& input) = 0;
virtual size_t addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, KeyPair& ephKeys) = 0;
virtual size_t addInput(const TransactionTypes::InputMultisignature& input) = 0;
virtual size_t addOutput(uint64_t amount, const AccountAddress& to) = 0;
virtual size_t addOutput(uint64_t amount, const std::vector<AccountAddress>& to, uint32_t requiredSignatures) = 0;
// transaction info
virtual bool getTransactionSecretKey(SecretKey& key) const = 0;
virtual void setTransactionSecretKey(const SecretKey& key) = 0;
// signing
virtual void signInputKey(size_t input, const TransactionTypes::InputKeyInfo& info, const KeyPair& ephKeys) = 0;
virtual void signInputMultisignature(size_t input, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) = 0;
};
class ITransaction :
public ITransactionReader,
public ITransactionWriter {
public:
virtual ~ITransaction() { }
};
}

View file

@ -0,0 +1,92 @@
// Copyright (c) 2011-2015 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 <limits>
#include <vector>
#include "crypto/hash.h"
#include "ITransaction.h"
#include "IObservable.h"
#include "IStreamSerializable.h"
namespace CryptoNote {
const uint64_t UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX = std::numeric_limits<uint64_t>::max();
struct TransactionInformation {
// transaction info
Hash transactionHash;
PublicKey publicKey;
uint64_t blockHeight;
uint64_t timestamp;
uint64_t unlockTime;
uint64_t totalAmountIn;
uint64_t totalAmountOut;
std::vector<uint8_t> extra;
Hash paymentId;
};
struct TransactionOutputInformation {
// output info
TransactionTypes::OutputType type;
uint64_t amount;
uint64_t globalOutputIndex;
uint32_t outputInTransaction;
// transaction info
Hash transactionHash;
PublicKey transactionPublicKey;
union {
PublicKey outputKey; // Type: Key
uint32_t requiredSignatures; // Type: Multisignature
};
};
struct TransactionSpentOutputInformation: public TransactionOutputInformation {
uint64_t spendingBlockHeight;
uint64_t timestamp;
Hash spendingTransactionHash;
KeyImage keyImage; //!< \attention Used only for TransactionTypes::OutputType::Key
uint32_t inputInTransaction;
};
class ITransfersContainer : public IStreamSerializable {
public:
enum Flags : uint32_t {
// state
IncludeStateUnlocked = 0x01,
IncludeStateLocked = 0x02,
IncludeStateSoftLocked = 0x04,
// output type
IncludeTypeKey = 0x100,
IncludeTypeMultisignature = 0x200,
// combinations
IncludeStateAll = 0xff,
IncludeTypeAll = 0xff00,
IncludeKeyUnlocked = IncludeTypeKey | IncludeStateUnlocked,
IncludeKeyNotUnlocked = IncludeTypeKey | IncludeStateLocked | IncludeStateSoftLocked,
IncludeAllLocked = IncludeTypeAll | IncludeStateLocked | IncludeStateSoftLocked,
IncludeAllUnlocked = IncludeTypeAll | IncludeStateUnlocked,
IncludeAll = IncludeTypeAll | IncludeStateAll,
IncludeDefault = IncludeKeyUnlocked
};
virtual size_t transfersCount() = 0;
virtual size_t transactionsCount() = 0;
virtual uint64_t balance(uint32_t flags = IncludeDefault) = 0;
virtual void getOutputs(std::vector<TransactionOutputInformation>& transfers, uint32_t flags = IncludeDefault) = 0;
virtual bool getTransactionInformation(const Hash& transactionHash, TransactionInformation& info, int64_t& txBalance) = 0;
virtual std::vector<TransactionOutputInformation> getTransactionOutputs(const Hash& transactionHash, uint32_t flags = IncludeDefault) = 0;
virtual void getUnconfirmedTransactions(std::vector<crypto::hash>& transactions) = 0;
virtual std::vector<TransactionSpentOutputInformation> getSpentOutputs() = 0;
};
}

View file

@ -0,0 +1,62 @@
// Copyright (c) 2011-2015 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 <system_error>
#include "ITransaction.h"
#include "ITransfersContainer.h"
#include "IStreamSerializable.h"
namespace CryptoNote {
struct SynchronizationStart {
uint64_t timestamp;
uint64_t height;
};
struct AccountSubscription {
AccountKeys keys;
SynchronizationStart syncStart;
size_t transactionSpendableAge;
};
class ITransfersSubscription;
class ITransfersObserver {
public:
virtual void onError(ITransfersSubscription* object,
uint64_t height, std::error_code ec) {}
virtual void onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) {}
/**
* \note The sender must guarantee that onTransactionDeleted() is called only after onTransactionUpdated() is called
* for the same \a transactionHash.
*/
virtual void onTransactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) { }
};
class ITransfersSubscription : public IObservable < ITransfersObserver > {
public:
virtual ~ITransfersSubscription() {}
virtual AccountAddress getAddress() = 0;
virtual ITransfersContainer& getContainer() = 0;
};
class ITransfersSynchronizer : public IStreamSerializable {
public:
virtual ~ITransfersSynchronizer() {}
virtual ITransfersSubscription& addSubscription(const AccountSubscription& acc) = 0;
virtual bool removeSubscription(const AccountAddress& acc) = 0;
virtual void getSubscriptions(std::vector<AccountAddress>& subscriptions) = 0;
// returns nullptr if address is not found
virtual ITransfersSubscription* getSubscription(const AccountAddress& acc) = 0;
};
}

View file

@ -1,4 +1,4 @@
// Copyright (c) 2012-2013 The Cryptonote developers
// Copyright (c) 2011-2015 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@ -28,23 +28,48 @@ const TransactionId INVALID_TRANSACTION_ID = std::numeric_limits<TransactionI
const TransferId INVALID_TRANSFER_ID = std::numeric_limits<TransferId>::max();
const uint64_t UNCONFIRMED_TRANSACTION_HEIGHT = std::numeric_limits<uint64_t>::max();
struct Transaction {
TransferId firstTransferId;
size_t transferCount;
int64_t totalAmount;
uint64_t fee;
TransactionHash hash;
bool isCoinbase;
uint64_t blockHeight;
uint64_t timestamp;
std::string extra;
enum class TransactionState : uint8_t {
Active, // --> {Deleted}
Deleted, // --> {Active}
Sending, // --> {Active, Cancelled, Failed}
Cancelled, // --> {}
Failed // --> {}
};
struct TransactionInfo {
TransferId firstTransferId;
size_t transferCount;
int64_t totalAmount;
uint64_t fee;
uint64_t sentTime;
uint64_t unlockTime;
TransactionHash hash;
bool isCoinbase;
uint64_t blockHeight;
uint64_t timestamp;
std::string extra;
TransactionState state;
};
typedef std::array<uint8_t, 32> WalletPublicKey;
typedef std::array<uint8_t, 32> WalletSecretKey;
struct WalletAccountKeys {
WalletPublicKey viewPublicKey;
WalletSecretKey viewSecretKey;
WalletPublicKey spendPublicKey;
WalletSecretKey spendSecretKey;
};
class IWalletObserver {
public:
virtual ~IWalletObserver() {}
virtual void initCompleted(std::error_code result) {}
virtual void saveCompleted(std::error_code result) {}
virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total, std::error_code result) {}
virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total) {}
virtual void synchronizationCompleted(std::error_code result) {}
virtual void actualBalanceUpdated(uint64_t actualBalance) {}
virtual void pendingBalanceUpdated(uint64_t pendingBalance) {}
virtual void externalTransactionCreated(TransactionId transactionId) {}
@ -60,7 +85,9 @@ public:
virtual void initAndGenerate(const std::string& password) = 0;
virtual void initAndLoad(std::istream& source, const std::string& password) = 0;
virtual void initWithKeys(const WalletAccountKeys& accountKeys, const std::string& password) = 0;
virtual void shutdown() = 0;
virtual void reset() = 0;
virtual void save(std::ostream& destination, bool saveDetailed = true, bool saveCache = true) = 0;
@ -76,12 +103,14 @@ public:
virtual TransactionId findTransactionByTransferId(TransferId transferId) = 0;
virtual bool getTransaction(TransactionId transactionId, Transaction& transaction) = 0;
virtual bool getTransaction(TransactionId transactionId, TransactionInfo& transaction) = 0;
virtual bool getTransfer(TransferId transferId, Transfer& transfer) = 0;
virtual TransactionId sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) = 0;
virtual TransactionId sendTransaction(const std::vector<Transfer>& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) = 0;
virtual std::error_code cancelTransaction(size_t transferId) = 0;
virtual void getAccountKeys(WalletAccountKeys& keys) = 0;
};
}

41
src/CMakeLists.txt Normal file → Executable file
View file

@ -2,7 +2,7 @@ add_definitions(-DSTATICLIB)
file(GLOB_RECURSE COMMON common/*)
file(GLOB_RECURSE CRYPTO crypto/*)
file(GLOB_RECURSE CRYPTONOTE_CORE cryptonote_core/*)
file(GLOB_RECURSE CRYPTONOTE_CORE cryptonote_core/* cryptonote_config.h)
file(GLOB_RECURSE CRYPTONOTE_PROTOCOL cryptonote_protocol/*)
file(GLOB_RECURSE DAEMON daemon/*)
file(GLOB_RECURSE P2P p2p/*)
@ -12,6 +12,19 @@ file(GLOB_RECURSE CONN_TOOL connectivity_tool/*)
file(GLOB_RECURSE WALLET wallet/*)
file(GLOB_RECURSE MINER miner/*)
file(GLOB_RECURSE NODE_RPC_PROXY node_rpc_proxy/*)
file(GLOB_RECURSE TRANSFERS transfers/*)
if(MSVC)
file(GLOB_RECURSE SYSTEM Platform/Windows/System/*)
elseif(APPLE)
file(GLOB_RECURSE SYSTEM Platform/OSX/System/*)
else()
file(GLOB_RECURSE SYSTEM Platform/Linux/System/*)
endif()
file(GLOB_RECURSE SERIALIZATION serialization/*)
file(GLOB_RECURSE LOGGER logger/*)
file(GLOB_RECURSE INPROCESS_NODE inprocess_node/*)
file(GLOB_RECURSE HTTP HTTP/*)
source_group(common FILES ${COMMON})
source_group(crypto FILES ${CRYPTO})
@ -20,33 +33,43 @@ source_group(cryptonote_protocol FILES ${CRYPTONOTE_PROTOCOL})
source_group(daemon FILES ${DAEMON})
source_group(p2p FILES ${P2P})
source_group(rpc FILES ${RPC})
source_group(System FILES ${SYSTEM} ${HTTP})
source_group(simplewallet FILES ${SIMPLEWALLET})
source_group(connectivity-tool FILES ${CONN_TOOL})
source_group(wallet FILES ${WALLET})
source_group(simpleminer FILES ${MINER})
source_group(node_rpc_proxy FILES ${NODE_RPC_PROXY})
source_group(transfers FILES ${TRANSFERS})
source_group(logger FILES ${LOGGER})
source_group(inprocess_node FILES ${INPROCESS_NODE})
add_library(common ${COMMON})
add_library(crypto ${CRYPTO})
add_library(serialization ${SERIALIZATION})
add_library(cryptonote_core ${CRYPTONOTE_CORE})
add_library(node_rpc_proxy ${NODE_RPC_PROXY})
add_library(inprocess_node ${INPROCESS_NODE})
add_executable(daemon ${DAEMON} ${P2P} ${CRYPTONOTE_PROTOCOL})
add_executable(connectivity_tool ${CONN_TOOL})
add_executable(simpleminer ${MINER})
target_link_libraries(daemon rpc cryptonote_core crypto common upnpc-static ${Boost_LIBRARIES})
target_link_libraries(connectivity_tool cryptonote_core crypto common ${Boost_LIBRARIES})
target_link_libraries(simpleminer cryptonote_core crypto common ${Boost_LIBRARIES})
target_link_libraries(daemon epee rpc cryptonote_core crypto common upnpc-static serialization ${Boost_LIBRARIES})
target_link_libraries(connectivity_tool epee rpc cryptonote_core crypto common serialization ${Boost_LIBRARIES})
target_link_libraries(simpleminer epee cryptonote_core crypto common serialization ${Boost_LIBRARIES})
add_library(rpc ${RPC})
add_library(System ${SYSTEM} ${HTTP} System/TcpStream.cpp System/TcpStream.h)
add_library(wallet ${WALLET})
add_executable(simplewallet ${SIMPLEWALLET} )
target_link_libraries(simplewallet wallet rpc cryptonote_core crypto common upnpc-static ${Boost_LIBRARIES})
add_library(node_rpc_proxy ${NODE_RPC_PROXY})
target_link_libraries(simplewallet epee wallet transfers rpc cryptonote_core crypto common upnpc-static node_rpc_proxy serialization ${Boost_LIBRARIES})
add_library(logger ${LOGGER})
add_library(transfers ${TRANSFERS})
add_dependencies(connectivity_tool version)
add_dependencies(daemon version)
add_dependencies(rpc version)
add_dependencies(simplewallet version)
set_property(TARGET common crypto cryptonote_core rpc wallet node_rpc_proxy PROPERTY FOLDER "libs")
set_property(TARGET common crypto cryptonote_core rpc System wallet node_rpc_proxy serialization logger transfers inprocess_node PROPERTY FOLDER "libs")
set_property(TARGET daemon simplewallet connectivity_tool simpleminer PROPERTY FOLDER "prog")
#TODO Specify the name of daemon for your currency
#set_property(TARGET daemon PROPERTY OUTPUT_NAME "cryptonoted")
set_property(TARGET daemon PROPERTY OUTPUT_NAME "cryptonoted")

202
src/HTTP/HttpParser.cpp Normal file
View file

@ -0,0 +1,202 @@
// Copyright (c) 2011-2015 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 "HttpParser.h"
#include <stdexcept>
namespace cryptonote {
HttpResponse::HTTP_STATUS HttpParser::parseResponseStatusFromString(const std::string& status) {
if (status == "200 OK" || status == "200 Ok") return cryptonote::HttpResponse::STATUS_200;
else if (status == "404 Not Found") return cryptonote::HttpResponse::STATUS_404;
else if (status == "500 Internal Server Error") return cryptonote::HttpResponse::STATUS_500;
else throw std::runtime_error("Unknown HTTP status code is given");
return cryptonote::HttpResponse::STATUS_200; //unaccessible
}
void HttpParser::receiveRequest(std::istream& stream, HttpRequest& request) {
readWord(stream, request.method);
readWord(stream, request.url);
std::string httpVersion;
readWord(stream, httpVersion);
readHeaders(stream, request.headers);
std::string body;
size_t bodyLen = getBodyLen(request.headers);
if (bodyLen) {
readBody(stream, request.body, bodyLen);
}
}
void HttpParser::receiveResponse(std::istream& stream, HttpResponse& response) {
std::string httpVersion;
readWord(stream, httpVersion);
std::string status;
char c;
stream.get(c);
while (stream.good() && c != '\r') { //Till the end
status += c;
stream.get(c);
}
if (!stream.good()) {
throw std::runtime_error("Parser error: stream is not good");
}
if (c == '\r') {
stream.get(c);
if (c != '\n') {
throw std::runtime_error("Parser error: '\\n' symbol is expected");
}
}
response.setStatus(parseResponseStatusFromString(status));
std::string name;
std::string value;
while (readHeader(stream, name, value)) {
response.addHeader(name, value);
name.clear();
value.clear();
}
response.addHeader(name, value);
auto headers = response.getHeaders();
size_t length = 0;
auto it = headers.find("Content-Length");
if (it != headers.end()) {
length = std::stoul(it->second);
}
std::string body;
if (length) {
readBody(stream, body, length);
}
response.setBody(body);
}
void HttpParser::readWord(std::istream& stream, std::string& word) {
char c;
stream.get(c);
while (stream.good() && c != ' ' && c != '\r') {
word += c;
stream.get(c);
}
if (!stream.good()) {
throw std::runtime_error("Parser error: stream is not good");
}
if (c == '\r') {
stream.get(c);
if (c != '\n') {
throw std::runtime_error("Parser error: '\\n' symbol is expected");
}
}
}
void HttpParser::readHeaders(std::istream& stream, HttpRequest::Headers& headers) {
std::string name;
std::string value;
while (readHeader(stream, name, value)) {
headers[name] = value; //use insert
name.clear();
value.clear();
}
headers[name] = value; //use insert
}
bool HttpParser::readHeader(std::istream& stream, std::string& name, std::string& value) {
char c;
bool isName = true;
stream.get(c);
while (stream.good() && c != '\r') {
if (c == ':') {
if (stream.peek() == ' ') {
stream.get(c);
}
if (name.empty()) {
throw std::runtime_error("Header name must be not empty");
}
if (isName) {
isName = false;
stream.get(c);
continue;
}
}
if (isName) {
name += c;
stream.get(c);
} else {
value += c;
stream.get(c);
}
}
if (!stream.good()) {
throw std::runtime_error("Parser error: stream is not good");
}
stream.get(c);
if (c != '\n') {
throw std::runtime_error("Parser error: '\\n' symbol is expected");
}
c = stream.peek();
if (c == '\r') {
stream.get(c).get(c);
if (c != '\n') {
throw std::runtime_error("Parser error: '\\n' symbol is expected");
}
return false; //no more headers
}
return true;
}
size_t HttpParser::getBodyLen(const HttpRequest::Headers& headers) {
auto it = headers.find("Content-Length");
if (it != headers.end()) {
size_t bytes = std::stoul(it->second);
return bytes;
}
return 0;
}
void HttpParser::readBody(std::istream& stream, std::string& body, const size_t bodyLen) {
size_t read = 0;
while (stream.good() && read < bodyLen) {
body += stream.get();
++read;
}
if (!stream.good()) {
throw std::runtime_error("stream is not good");
}
}
}

34
src/HTTP/HttpParser.h Normal file
View file

@ -0,0 +1,34 @@
// Copyright (c) 2011-2015 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef HTTPPARSER_H_
#define HTTPPARSER_H_
#include <iostream>
#include <map>
#include <string>
#include "HttpRequest.h"
#include "HttpResponse.h"
namespace cryptonote {
//Blocking HttpParser
class HttpParser {
public:
HttpParser() {};
void receiveRequest(std::istream& stream, HttpRequest& request);
void receiveResponse(std::istream& stream, HttpResponse& response);
static HttpResponse::HTTP_STATUS parseResponseStatusFromString(const std::string& status);
private:
void readWord(std::istream& stream, std::string& word);
void readHeaders(std::istream& stream, HttpRequest::Headers &headers);
bool readHeader(std::istream& stream, std::string& name, std::string& value);
size_t getBodyLen(const HttpRequest::Headers& headers);
void readBody(std::istream& stream, std::string& body, const size_t bodyLen);
};
} //namespace cryptonote
#endif /* HTTPPARSER_H_ */

60
src/HTTP/HttpRequest.cpp Normal file
View file

@ -0,0 +1,60 @@
// Copyright (c) 2011-2015 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 "HttpRequest.h"
namespace cryptonote {
const std::string& HttpRequest::getMethod() const {
return method;
}
const std::string& HttpRequest::getUrl() const {
return url;
}
const HttpRequest::Headers& HttpRequest::getHeaders() const {
return headers;
}
const std::string& HttpRequest::getBody() const {
return body;
}
void HttpRequest::addHeader(const std::string& name, const std::string& value) {
headers[name] = value;
}
void HttpRequest::setBody(const std::string& b) {
body = b;
if (!body.empty()) {
headers["Content-Length"] = std::to_string(body.size());
}
else {
headers.erase("Content-Length");
}
}
void HttpRequest::setUrl(const std::string& u) {
url = u;
}
std::ostream& HttpRequest::printHttpRequest(std::ostream& os) const {
os << "POST " << url << " HTTP/1.1\r\n";
auto host = headers.find("Host");
if (host == headers.end()) {
os << "Host: " << "127.0.0.1" << "\r\n";
}
for (auto pair : headers) {
os << pair.first << ": " << pair.second << "\r\n";
}
os << "\r\n";
if (!body.empty()) {
os << body;
}
return os;
}
}

40
src/HTTP/HttpRequest.h Normal file
View file

@ -0,0 +1,40 @@
// Copyright (c) 2011-2015 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 <map>
#include <string>
namespace cryptonote {
class HttpRequest {
public:
typedef std::map<std::string, std::string> Headers;
const std::string& getMethod() const;
const std::string& getUrl() const;
const Headers& getHeaders() const;
const std::string& getBody() const;
void addHeader(const std::string& name, const std::string& value);
void setBody(const std::string& b);
void setUrl(const std::string& uri);
private:
friend class HttpParser;
std::string method;
std::string url;
Headers headers;
std::string body;
friend std::ostream& operator<<(std::ostream& os, const HttpRequest& resp);
std::ostream& printHttpRequest(std::ostream& os) const;
};
inline std::ostream& operator<<(std::ostream& os, const HttpRequest& resp) {
return resp.printHttpRequest(os);
}
}

70
src/HTTP/HttpResponse.cpp Normal file
View file

@ -0,0 +1,70 @@
// Copyright (c) 2011-2015 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 "HttpResponse.h"
#include <stdexcept>
namespace {
const char* getStatusString(cryptonote::HttpResponse::HTTP_STATUS status) {
switch (status) {
case cryptonote::HttpResponse::STATUS_200:
return "200 OK";
case cryptonote::HttpResponse::STATUS_404:
return "404 Not Found";
case cryptonote::HttpResponse::STATUS_500:
return "500 Internal Server Error";
default:
throw std::runtime_error("Unknown HTTP status code is given");
}
return ""; //unaccessible
}
} //namespace
namespace cryptonote {
HttpResponse::HttpResponse() {
status = STATUS_200;
headers["Server"] = "Cryptonote-based HTTP server";
}
void HttpResponse::setStatus(HTTP_STATUS s) {
status = s;
}
void HttpResponse::addHeader(const std::string& name, const std::string& value) {
headers[name] = value;
}
void HttpResponse::setBody(const std::string& b) {
body = b;
if (!body.empty()) {
headers["Content-Length"] = std::to_string(body.size());
} else {
headers.erase("Content-Length");
}
}
std::ostream& HttpResponse::printHttpResponse(std::ostream& os) const {
os << "HTTP/1.1 " << getStatusString(status) << "\r\n";
for (auto pair: headers) {
os << pair.first << ": " << pair.second << "\r\n";
}
os << "\r\n";
if (!body.empty()) {
os << body;
}
return os;
}
} //namespace cryptonote

44
src/HTTP/HttpResponse.h Normal file
View file

@ -0,0 +1,44 @@
// Copyright (c) 2011-2015 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 <ostream>
#include <string>
#include <map>
namespace cryptonote {
class HttpResponse {
public:
enum HTTP_STATUS {
STATUS_200,
STATUS_404,
STATUS_500
};
HttpResponse();
void setStatus(HTTP_STATUS s);
void addHeader(const std::string& name, const std::string& value);
void setBody(const std::string& b);
const std::map<std::string, std::string>& getHeaders() const { return headers; }
HTTP_STATUS getStatus() const { return status; }
const std::string& getBody() const { return body; }
private:
friend std::ostream& operator<<(std::ostream& os, const HttpResponse& resp);
std::ostream& printHttpResponse(std::ostream& os) const;
HTTP_STATUS status;
std::map<std::string, std::string> headers;
std::string body;
};
inline std::ostream& operator<<(std::ostream& os, const HttpResponse& resp) {
return resp.printHttpResponse(os);
}
} //namespace cryptonote

View file

@ -0,0 +1,147 @@
// Copyright (c) 2011-2015 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 "Dispatcher.h"
#include <iostream>
#include <ucontext.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <assert.h>
#include <sys/time.h>
#include <errno.h>
#include <stdexcept>
using namespace System;
void Dispatcher::contextProcedureStatic(void *context) {
reinterpret_cast<Dispatcher*>(context)->contextProcedure();
}
Dispatcher::Dispatcher() {
epoll = ::epoll_create1(0);
if (epoll == -1) {
std::cerr << "kqueue() fail errno=" << errno << std::endl;
} else {
currentContext = new ucontext_t;
if (getcontext(reinterpret_cast<ucontext_t*>(currentContext)) == -1) {
std::cerr << "getcontext() fail errno=" << errno << std::endl;
} else {
contextCount = 0;
return;
}
}
throw std::runtime_error("Dispatcher::Dispatcher");
}
Dispatcher::~Dispatcher() {
assert(resumingContexts.empty());
assert(reusableContexts.size() == contextCount);
assert(spawningProcedures.empty());
assert(reusableContexts.size() == allocatedStacks.size());
while (!reusableContexts.empty()) {
delete[] allocatedStacks.top();
allocatedStacks.pop();
delete static_cast<ucontext_t*>(reusableContexts.top());
reusableContexts.pop();
}
while (!timers.empty()) {
timers.pop();
}
if (-1 == close(epoll)) {
std::cerr << "close() fail errno=" << errno << std::endl;
}
}
void* Dispatcher::getCurrentContext() const {
return currentContext;
}
int Dispatcher::getEpoll() const {
return epoll;
}
void Dispatcher::pushContext(void* context) {
resumingContexts.push(context);
}
void Dispatcher::spawn(std::function<void()>&& procedure) {
ucontext_t *context;
if (reusableContexts.empty()) {
context = new ucontext_t;
if (getcontext(context) == -1) { //makecontext precondition
std::cerr << "getcontext() fail errno=" << errno << std::endl;
throw std::runtime_error("Dispatcher::spawn()");
}
auto stackPointer = new uint8_t[64 * 1024];
context->uc_stack.ss_sp = stackPointer;
allocatedStacks.push(stackPointer);
context->uc_stack.ss_size = 64 * 1024;
makecontext(context, (void(*)())contextProcedureStatic, 1, reinterpret_cast<int*>(this));
++contextCount;
} else {
context = static_cast<ucontext_t*>(reusableContexts.top());
reusableContexts.pop();
}
resumingContexts.push(context);
spawningProcedures.emplace(std::move(procedure));
}
void Dispatcher::clear() {
//TODO
}
void Dispatcher::yield() {
void* context;
for (;;) {
if (!resumingContexts.empty()) {
context = resumingContexts.front();
resumingContexts.pop();
assert(context);
break;
}
epoll_event event;
int count = epoll_wait(epoll, &event, 1, -1);
if (count == 1) {
if ((event.events & EPOLLOUT) != 0) {
context = static_cast<ContextExt *>(event.data.ptr)->writeContext;
} else {
context = static_cast<ContextExt *>(event.data.ptr)->context;
}
assert(context);
break;
}
if (errno != EINTR) {
std::cerr << "epoll_wait() failed, errno=" << errno << std::endl;
throw std::runtime_error("Dispatcher::yield()");
}
}
if (context != currentContext) {
ucontext_t* oldContext = static_cast<ucontext_t*>(currentContext);
currentContext = context;
if (-1 == swapcontext(oldContext, static_cast<ucontext_t *>(context))) {
std::cerr << "swapcontext() failed, errno=" << errno << std::endl;
throw std::runtime_error("Dispatcher::yield()");
}
}
}
void Dispatcher::contextProcedure() {
void* context = currentContext;
for (;;) {
assert(!spawningProcedures.empty());
std::function<void()> procedure = std::move(spawningProcedures.front());
spawningProcedures.pop();
procedure();
reusableContexts.push(context);
yield();
}
}

View file

@ -0,0 +1,51 @@
// Copyright (c) 2011-2015 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 <functional>
#include <queue>
#include <stack>
namespace System {
class Dispatcher {
public:
Dispatcher();
Dispatcher(const Dispatcher&) = delete;
~Dispatcher();
Dispatcher& operator=(const Dispatcher&) = delete;
void spawn(std::function<void()>&& procedure);
void yield();
void clear();
struct ContextExt {
void *context;
void *writeContext; //required workaround
};
private:
friend class Event;
friend class DispatcherAccessor;
friend class TcpConnection;
friend class TcpConnector;
friend class TcpListener;
friend class Timer;
int epoll;
void* currentContext;
std::size_t contextCount;
std::queue<void*> resumingContexts;
std::stack<void*> reusableContexts;
std::stack<uint8_t *> allocatedStacks;
std::queue<std::function<void()>> spawningProcedures;
std::stack<int> timers;
int getEpoll() const;
void pushContext(void* context);
void* getCurrentContext() const;
void contextProcedure();
static void contextProcedureStatic(void* context);
};
}

View file

@ -0,0 +1,93 @@
// Copyright (c) 2011-2015 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 "Event.h"
#include <cassert>
#include <iostream>
#include "Dispatcher.h"
using namespace System;
namespace {
struct Waiter {
Waiter* next;
void* context;
};
}
Event::Event() : dispatcher(nullptr) {
}
Event::Event(Dispatcher& dispatcher) : dispatcher(&dispatcher), first(nullptr), state(false) {
}
Event::Event(Event&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
first = other.first;
if (other.first != nullptr) {
last = other.last;
}
state = other.state;
other.dispatcher = nullptr;
}
}
Event::~Event() {
assert(first == nullptr);
}
Event& Event::operator=(Event&& other) {
assert(first == nullptr);
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
first = other.first;
if (other.first != nullptr) {
last = other.last;
}
state = other.state;
other.dispatcher = nullptr;
}
return *this;
}
bool Event::get() const {
assert(dispatcher != nullptr);
return state;
}
void Event::clear() {
assert(dispatcher != nullptr);
state = false;
}
void Event::set() {
assert(dispatcher != nullptr);
state = true;
for (Waiter* waiter = static_cast<Waiter*>(first); waiter != nullptr; waiter = waiter->next) {
dispatcher->pushContext(waiter->context);
}
first = nullptr;
}
void Event::wait() {
assert(dispatcher != nullptr);
if (!state) {
Waiter waiter = { nullptr, dispatcher->getCurrentContext() };
if (first != nullptr) {
static_cast<Waiter*>(last)->next = &waiter;
} else {
first = &waiter;
}
last = &waiter;
dispatcher->yield();
assert(dispatcher != nullptr);
}
}

View file

@ -0,0 +1,32 @@
// Copyright (c) 2011-2015 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
namespace System {
class Dispatcher;
class Event {
public:
Event();
explicit Event(Dispatcher& dispatcher);
Event(const Event&) = delete;
Event(Event&& other);
~Event();
Event& operator=(const Event&) = delete;
Event& operator=(Event&& other);
bool get() const;
void clear();
void set();
void wait();
private:
Dispatcher* dispatcher;
void* first;
void* last;
bool state;
};
}

View file

@ -1,7 +1,5 @@
// Copyright (c) 2011-2014 The Cryptonote developers
// Copyright (c) 2011-2015 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
bool transactions_generation_from_blockchain(std::string& blockchain_path);
#include "InterruptedException.h"

View file

@ -0,0 +1,10 @@
// Copyright (c) 2011-2015 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 <exception>
class InterruptedException : public std::exception {
};

View file

@ -0,0 +1,284 @@
// Copyright (c) 2011-2015 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 "TcpConnection.h"
#include <iostream>
#include <sys/epoll.h>
#include <errno.h>
#include <unistd.h>
#include <assert.h>
#include <stdexcept>
#include <sys/socket.h>
#include "Dispatcher.h"
#include "InterruptedException.h"
using namespace System;
namespace {
struct ConnectionContext : public Dispatcher::ContextExt {
bool interrupted;
};
}
TcpConnection::TcpConnection() : dispatcher(nullptr) {
}
TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), stopped(false), context(nullptr) {
epoll_event connectionEvent;
connectionEvent.data.fd = connection;
connectionEvent.events = 0;
connectionEvent.data.ptr = nullptr;
if (epoll_ctl(dispatcher.getEpoll(), EPOLL_CTL_ADD, socket, &connectionEvent) == -1) {
std::cerr << errno << std::endl;
throw std::runtime_error("epoll_ctl() fail");
}
}
TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
connection = other.connection;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
TcpConnection::~TcpConnection() {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (close(connection) == -1) {
std::cerr << "close() failed, errno=" << errno << '.' << std::endl;
}
}
}
TcpConnection& TcpConnection::operator=(TcpConnection&& other) {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (close(connection) == -1) {
std::cerr << "close() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpConnection::operator=");
}
}
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
connection = other.connection;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void TcpConnection::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
void TcpConnection::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
ConnectionContext *context2 = static_cast<ConnectionContext *>(context);
if (!context2->interrupted) {
epoll_event connectionEvent;
connectionEvent.data.fd = connection;
connectionEvent.events = 0;
connectionEvent.data.ptr = nullptr;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) {
std::cerr << errno << std::endl;
throw std::runtime_error("epoll_ctl() fail");
}
context2->interrupted = true;
if (context2->context != nullptr) {
dispatcher->pushContext(context2->context);
}
if (context2->writeContext != nullptr) {
dispatcher->pushContext(context2->writeContext);
}
}
}
stopped = true;
}
size_t TcpConnection::read(uint8_t* data, size_t size) {
assert(dispatcher != nullptr);
assert(context == nullptr || static_cast<Dispatcher::ContextExt*>(context)->context == nullptr);
if (stopped) {
throw InterruptedException();
}
ssize_t transferred = ::recv(connection, (void *)data, size, 0);
if (transferred == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
std::cerr << "recv failed, result=" << errno << '.' << std::endl;
} else {
epoll_event connectionEvent;
connectionEvent.data.fd = connection;
ConnectionContext context2;
if (context == nullptr) {
context2.writeContext = nullptr;
context2.interrupted = false;
context2.context = dispatcher->getCurrentContext();
context = &context2;
connectionEvent.events = EPOLLIN | EPOLLONESHOT;
} else {
assert(static_cast<Dispatcher::ContextExt*>(context)->writeContext != nullptr);
connectionEvent.events = EPOLLIN | EPOLLOUT | EPOLLONESHOT;
}
connectionEvent.data.ptr = context;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
} else {
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == dispatcher->getCurrentContext());
if (static_cast<ConnectionContext*>(context)->interrupted) {
context = nullptr;
throw InterruptedException();
}
assert(static_cast<Dispatcher::ContextExt*>(context)->context == context2.context);
if (static_cast<Dispatcher::ContextExt*>(context)->writeContext != nullptr) { //write is presented, rearm
static_cast<Dispatcher::ContextExt*>(context)->context = nullptr;
epoll_event connectionEvent;
connectionEvent.data.fd = connection;
connectionEvent.events = EPOLLOUT | EPOLLONESHOT;
connectionEvent.data.ptr = context;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpConnection::read");
}
} else {
context = nullptr;
}
ssize_t transferred = ::recv(connection, (void *)data, size, 0);
if (transferred == -1) {
std::cerr << "recv failed, errno=" << errno << '.' << std::endl;
} else {
if (transferred == 0) {
std::cerr << "recv return after yield with 0 bytes" << std::endl;
int retval = -1;
socklen_t retValLen = sizeof(retval);
int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen);
if (s == -1) {
std::cerr << "getsockopt() failed, errno=" << errno << '.' << std::endl;
} else {
std::cerr << "recv getsockopt retval = " << retval << std::endl;
}
}
assert(transferred <= size);
return transferred;
}
}
}
throw std::runtime_error("TcpConnection::read");
}
assert(transferred <= size);
return transferred;
}
void TcpConnection::write(const uint8_t* data, size_t size) {
assert(dispatcher != nullptr);
assert(context == nullptr || static_cast<Dispatcher::ContextExt*>(context)->writeContext == nullptr);
if (stopped) {
throw InterruptedException();
}
if (size == 0) {
if (shutdown(connection, SHUT_WR) == -1) {
std::cerr << "shutdown failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpConnection::write");
}
return;
}
ssize_t transferred = ::send(connection, (void *)data, size, 0);
if (transferred == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
std::cerr << "send failed, result=" << errno << '.' << std::endl;
} else {
epoll_event connectionEvent;
connectionEvent.data.fd = connection;
ConnectionContext context2;
if (context == nullptr) {
context2.context = nullptr;
context2.interrupted = false;
context2.writeContext = dispatcher->getCurrentContext();
context = &context2;
connectionEvent.events = EPOLLOUT | EPOLLONESHOT;
} else {
assert(static_cast<Dispatcher::ContextExt*>(context)->context != nullptr);
connectionEvent.events = EPOLLIN | EPOLLOUT | EPOLLONESHOT;
}
connectionEvent.data.ptr = context;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
} else {
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.writeContext == dispatcher->getCurrentContext());
if (static_cast<ConnectionContext*>(context)->interrupted) {
context = nullptr;
throw InterruptedException();
}
assert(static_cast<Dispatcher::ContextExt*>(context)->writeContext == context2.writeContext);
if (static_cast<Dispatcher::ContextExt*>(context)->context != nullptr) { //read is presented, rearm
static_cast<Dispatcher::ContextExt*>(context)->writeContext = nullptr;
epoll_event connectionEvent;
connectionEvent.data.fd = connection;
connectionEvent.events = EPOLLIN | EPOLLONESHOT;
connectionEvent.data.ptr = context;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpConnection::write");
}
} else {
context = nullptr;
}
ssize_t transferred = ::send(connection, (void *)data, size, 0);
if (transferred == -1) {
std::cerr << "send failed, errno=" << errno << '.' << std::endl;
} else {
if (transferred == 0) {
throw std::runtime_error("send transferred 0 bytes.");
}
assert(transferred == size);
return;
}
}
}
throw std::runtime_error("TcpConnection::write");
}
}

View file

@ -0,0 +1,40 @@
// Copyright (c) 2011-2015 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 <cstddef>
#include <cstdint>
#include <stdint.h>
namespace System {
class Dispatcher;
class TcpConnection {
public:
TcpConnection();
TcpConnection(const TcpConnection&) = delete;
TcpConnection(TcpConnection&& other);
~TcpConnection();
TcpConnection& operator=(const TcpConnection&) = delete;
TcpConnection& operator=(TcpConnection&& other);
void start();
void stop();
std::size_t read(uint8_t* data, std::size_t size);
void write(const uint8_t* data, std::size_t size);
private:
friend class TcpConnector;
friend class TcpListener;
explicit TcpConnection(Dispatcher& dispatcher, int socket);
Dispatcher* dispatcher;
int connection;
bool stopped;
void* context;
};
}

View file

@ -0,0 +1,200 @@
// Copyright (c) 2011-2015 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 "TcpConnector.h"
#include <stdexcept>
#include <iostream>
#include <random>
#include <sstream>
#include <cassert>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include "Dispatcher.h"
#include "TcpConnection.h"
#include "InterruptedException.h"
using namespace System;
namespace {
struct ConnectorContext : public Dispatcher::ContextExt {
int connection;
bool interrupted;
};
}
TcpConnector::TcpConnector() : dispatcher(nullptr) {
}
TcpConnector::TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher), address(address), port(port), stopped(false), context(nullptr) {
}
TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
address = other.address;
port = other.port;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
TcpConnector::~TcpConnector() {
}
TcpConnector& TcpConnector::operator=(TcpConnector&& other) {
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
address = other.address;
port = other.port;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void TcpConnector::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
TcpConnection TcpConnector::connect() {
assert(dispatcher != nullptr);
assert(context == nullptr);
if (stopped) {
throw InterruptedException();
}
std::ostringstream portStream;
portStream << port;
addrinfo hints = { 0, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL };
addrinfo *addressInfos;
int result = getaddrinfo(address.c_str(), portStream.str().c_str(), &hints, &addressInfos);
if (result == -1) {
std::cerr << "getaddrinfo failed, errno=" << errno << '.' << std::endl;
} else {
std::size_t count = 0;
for (addrinfo* addressInfo = addressInfos; addressInfo != nullptr; addressInfo = addressInfo->ai_next) {
++count;
}
std::random_device randomDevice;
std::mt19937 generator(randomDevice());
std::uniform_int_distribution<std::size_t> distribution(0, count - 1);
std::size_t index = distribution(generator);
addrinfo* addressInfo = addressInfos;
for (std::size_t i = 0; i < index; ++i) {
addressInfo = addressInfo->ai_next;
}
sockaddr_in addressData = *reinterpret_cast<sockaddr_in*>(addressInfo->ai_addr);
freeaddrinfo(addressInfo);
int connection = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (connection == -1) {
std::cerr << "socket failed, errno=" << errno << '.' << std::endl;
} else {
sockaddr_in bindAddress;
bindAddress.sin_family = AF_INET;
bindAddress.sin_port = 0;
bindAddress.sin_addr.s_addr = INADDR_ANY;
if (bind(connection, reinterpret_cast<sockaddr*>(&bindAddress), sizeof bindAddress) != 0) {
std::cerr << "bind failed, errno=" << errno << '.' << std::endl;
} else {
int flags = fcntl(connection, F_GETFL, 0);
if (flags == -1 || fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1) {
std::cerr << "fcntl() failed errno=" << errno << std::endl;
} else {
int result = ::connect(connection, reinterpret_cast<sockaddr *>(&addressData), sizeof addressData);
if (result == -1) {
if (errno == EINPROGRESS) {
ConnectorContext context2;
context2.writeContext = dispatcher->getCurrentContext();
context2.context = nullptr;
context2.interrupted = false;
context2.connection = connection;
context = &context2;
epoll_event connectEvent;
connectEvent.data.fd = connection;
connectEvent.events = EPOLLOUT | EPOLLRDHUP | EPOLLERR | EPOLLONESHOT;
connectEvent.data.ptr = context;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_ADD, connection, &connectEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
} else {
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.writeContext == dispatcher->getCurrentContext());
assert(context == &context2);
context = nullptr;
context2.writeContext = nullptr;
if (context2.interrupted) {
if (close(connection) == -1) {
std::cerr << "close failed, errno=" << errno << std::endl;
}
throw InterruptedException();
}
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_DEL, connection, NULL) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
} else {
int retval = -1;
socklen_t retValLen = sizeof(retval);
int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen);
if (s == -1) {
std::cerr << "getsockopt() failed, errno=" << errno << '.' << std::endl;
} else {
if (retval != 0) {
std::cerr << "connect failed; getsockopt retval = " << retval << std::endl;
} else {
return TcpConnection(*dispatcher, connection);
}
}
}
}
}
} else {
return TcpConnection(*dispatcher, connection);
}
}
}
if (close(connection) == -1) {
std::cerr << "close failed, errno=" << errno << std::endl;
}
}
}
throw std::runtime_error("TcpConnector::connect");
}
void TcpConnector::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
ConnectorContext* context2 = static_cast<ConnectorContext*>(context);
if (!context2->interrupted) {
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_DEL, context2->connection, NULL) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpConnector::stop");
}
dispatcher->pushContext(context2->writeContext);
context2->interrupted = true;
}
}
stopped = true;
}

View file

@ -0,0 +1,37 @@
// Copyright (c) 2011-2015 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>
#include <stdint.h>
namespace System {
class Dispatcher;
class TcpConnection;
class TcpConnector {
public:
TcpConnector();
TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port);
TcpConnector(const TcpConnector&) = delete;
TcpConnector(TcpConnector&& other);
~TcpConnector();
TcpConnector& operator=(const TcpConnector&) = delete;
TcpConnector& operator=(TcpConnector&& other);
void start();
void stop();
TcpConnection connect();
private:
Dispatcher* dispatcher;
std::string address;
uint16_t port;
bool stopped;
void* context;
};
}

View file

@ -0,0 +1,194 @@
// Copyright (c) 2011-2015 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 "TcpListener.h"
#include <sys/epoll.h>
#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#include <assert.h>
#include <iostream>
#include <cassert>
#include <errno.h>
#include <stdexcept>
#include "Dispatcher.h"
#include "TcpConnection.h"
#include "InterruptedException.h"
using namespace System;
namespace {
struct ListenerContext : public Dispatcher::ContextExt {
bool interrupted;
};
}
TcpListener::TcpListener() : dispatcher(nullptr) {
}
TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
listener = other.listener;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
TcpListener::~TcpListener() {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (close(listener) == -1) {
std::cerr << "close() failed, errno=" << errno << '.' << std::endl;
}
}
}
TcpListener& TcpListener::operator=(TcpListener&& other) {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (close(listener) == -1) {
std::cerr << "close() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpListener::operator=");
}
}
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
listener = other.listener;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void TcpListener::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
TcpListener::TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher) {
listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listener == -1) {
std::cerr << "socket failed, errno=" << errno << std::endl;
} else {
int flags = fcntl(listener, F_GETFL, 0);
if (flags == -1 || fcntl(listener, F_SETFL, flags | O_NONBLOCK) == -1) {
std::cerr << "fcntl() failed errno=" << errno << std::endl;
} else {
sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = INADDR_ANY;
if (bind(listener, reinterpret_cast<sockaddr*>(&address), sizeof address) != 0) {
std::cerr << "bind failed, errno=" << errno << std::endl;
} else if (listen(listener, SOMAXCONN) != 0) {
std::cerr << "listen failed, errno=" << errno << std::endl;
} else {
epoll_event listenEvent;
listenEvent.data.fd = listener;
listenEvent.events = 0;
listenEvent.data.ptr = nullptr;
if (epoll_ctl(dispatcher.getEpoll(), EPOLL_CTL_ADD, listener, &listenEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
} else {
stopped = false;
context = nullptr;
return;
}
}
}
if (close(listener) == -1) {
std::cerr << "close failed, errno=" << errno << std::endl;
}
}
throw std::runtime_error("TcpListener::TcpListener");
}
TcpConnection TcpListener::accept() {
assert(dispatcher != nullptr);
assert(context == nullptr);
if (stopped) {
throw InterruptedException();
}
ListenerContext context2;
context2.context = dispatcher->getCurrentContext();
context2.writeContext = nullptr;
context2.interrupted = false;
epoll_event listenEvent;
listenEvent.data.fd = listener;
listenEvent.events = EPOLLIN | EPOLLONESHOT;
listenEvent.data.ptr = &context2;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, listener, &listenEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
} else {
context = &context2;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == dispatcher->getCurrentContext());
assert(context2.writeContext == nullptr);
assert(context == &context2);
context = nullptr;
context2.context = nullptr;
if (context2.interrupted) {
if (close(listener) == -1) {
std::cerr << "close failed, errno=" << errno << std::endl;
}
throw InterruptedException();
}
sockaddr inAddr;
socklen_t inLen = sizeof(inAddr);
int connection = ::accept(listener, &inAddr, &inLen);
if (connection == -1) {
std::cerr << "accept() failed, errno=" << errno << '.' << std::endl;
} else {
int flags = fcntl(connection, F_GETFL, 0);
if (flags == -1 || fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1) {
std::cerr << "fcntl() failed errno=" << errno << std::endl;
} else {
return TcpConnection(*dispatcher, connection);
}
}
}
throw std::runtime_error("TcpListener::accept");
}
void TcpListener::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
ListenerContext* context2 = static_cast<ListenerContext*>(context);
if (!context2->interrupted) {
context2->interrupted = true;
epoll_event listenEvent;
listenEvent.data.fd = listener;
listenEvent.events = 0;
listenEvent.data.ptr = nullptr;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, listener, &listenEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpListener::stop");
}
dispatcher->pushContext(context2->context);
}
}
stopped = true;
}

View file

@ -0,0 +1,36 @@
// Copyright (c) 2011-2015 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>
#include <stdint.h>
namespace System {
class Dispatcher;
class TcpConnection;
class TcpListener {
public:
TcpListener();
TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port);
TcpListener(const TcpListener&) = delete;
TcpListener(TcpListener&& other);
~TcpListener();
TcpListener& operator=(const TcpListener&) = delete;
TcpListener& operator=(TcpListener&& other);
void start();
void stop();
TcpConnection accept();
private:
Dispatcher* dispatcher;
int listener;
bool stopped;
void* context;
};
}

View file

@ -0,0 +1,147 @@
// Copyright (c) 2011-2015 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 "Timer.h"
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <assert.h>
#include <unistd.h>
#include <iostream>
#include <stdexcept>
#include <errno.h>
#include "Dispatcher.h"
#include "InterruptedException.h"
using namespace System;
namespace {
struct TimerContext : public Dispatcher::ContextExt {
Dispatcher* dispatcher;
bool interrupted;
};
}
Timer::Timer() : dispatcher(nullptr) {
}
Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) {
timer = timerfd_create(CLOCK_MONOTONIC, 0);
epoll_event timerEvent;
timerEvent.data.fd = timer;
timerEvent.events = 0;
timerEvent.data.ptr = nullptr;
if (epoll_ctl(this->dispatcher->getEpoll(), EPOLL_CTL_ADD, timer, &timerEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("Timer::Timer");
}
}
Timer::Timer(Timer&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
timer = other.timer;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
Timer::~Timer() {
if (dispatcher != nullptr) {
close(timer);
}
}
Timer& Timer::operator=(Timer&& other) {
if (dispatcher != nullptr) {
assert(context == nullptr);
close(timer);
}
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
timer = other.timer;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void Timer::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
void Timer::sleep(std::chrono::milliseconds duration) {
assert(dispatcher != nullptr);
assert(context == nullptr);
if (stopped) {
throw InterruptedException();
}
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration);
itimerspec expires;
expires.it_interval.tv_nsec = expires.it_interval.tv_sec = 0;
expires.it_value.tv_sec = seconds.count();
expires.it_value.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(duration - seconds).count();
timerfd_settime(timer, 0, &expires, NULL);
TimerContext context2;
context2.dispatcher = dispatcher;
context2.context = dispatcher->getCurrentContext();
context2.writeContext = nullptr;
context2.interrupted = false;
epoll_event timerEvent;
timerEvent.data.fd = timer;
timerEvent.events = EPOLLIN | EPOLLONESHOT;
timerEvent.data.ptr = &context2;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, timer, &timerEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("Timer::sleep");
}
context = &context2;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == dispatcher->getCurrentContext());
assert(context2.writeContext == nullptr);
assert(context == &context2);
context = nullptr;
context2.context = nullptr;
if (context2.interrupted) {
throw InterruptedException();
}
}
void Timer::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
TimerContext* context2 = reinterpret_cast<TimerContext*>(context);
if (context2->context != nullptr) {
epoll_event timerEvent;
timerEvent.data.fd = timer;
timerEvent.events = 0;
timerEvent.data.ptr = nullptr;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, timer, &timerEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("Timer::sleep");
}
dispatcher->pushContext(context2->context);
context2->interrupted = true;
}
}
stopped = true;
}

View file

@ -0,0 +1,33 @@
// Copyright (c) 2011-2015 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 <chrono>
namespace System {
class Dispatcher;
class Timer {
public:
Timer();
explicit Timer(Dispatcher& dispatcher);
Timer(const Timer&) = delete;
Timer(Timer&& other);
~Timer();
Timer& operator=(const Timer&) = delete;
Timer& operator=(Timer&& other);
void start();
void stop();
void sleep(std::chrono::milliseconds duration);
private:
Dispatcher* dispatcher;
int timer;
bool stopped;
void* context;
};
}

View file

@ -0,0 +1,156 @@
// Copyright (c) 2011-2015 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 "Dispatcher.h"
#include <iostream>
#define _XOPEN_SOURCE
#include <ucontext.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/event.h>
#include <assert.h>
#include <sys/time.h>
using namespace System;
void Dispatcher::contextProcedureStatic(void *context) {
reinterpret_cast<Dispatcher*>(context)->contextProcedure();
}
Dispatcher::Dispatcher() : lastCreatedTimer(0) {
kqueue = ::kqueue();
if (kqueue == -1) {
std::cerr << "kqueue() fail errno=" << errno << std::endl;
} else {
currentContext = new ucontext_t;
if (getcontext(reinterpret_cast<ucontext_t*>(currentContext)) == -1) {
std::cerr << "getcontext() fail errno=" << errno << std::endl;
} else {
contextCount = 0;
return;
}
}
throw std::runtime_error("Dispatcher::Dispatcher");
}
Dispatcher::~Dispatcher() {
assert(resumingContexts.empty());
assert(reusableContexts.size() == contextCount);
assert(spawningProcedures.empty());
assert(reusableContexts.size() == allocatedStacks.size());
while (!reusableContexts.empty()) {
delete[] allocatedStacks.top();
allocatedStacks.pop();
delete static_cast<ucontext_t*>(reusableContexts.top());
reusableContexts.pop();
}
while (!timers.empty()) {
timers.pop();
}
if (-1 == close(kqueue)) {
std::cerr << "close() fail errno=" << errno << std::endl;
}
}
void* Dispatcher::getCurrentContext() const {
return currentContext;
}
int Dispatcher::getKqueue() const {
return kqueue;
}
void Dispatcher::pushContext(void* context) {
resumingContexts.push(context);
}
void Dispatcher::spawn(std::function<void()>&& procedure) {
void* context;
if (reusableContexts.empty()) {
context = new ucontext_t;
if (-1 == getcontext(reinterpret_cast<ucontext_t *>(context))) { //makecontext precondition
std::cerr << "getcontext() fail errno=" << errno << std::endl;
throw std::runtime_error("Dispatcher::spawn()");
}
auto stackPointer = new uint8_t[64 * 1024];
reinterpret_cast<ucontext_t *>(context)->uc_stack.ss_sp = stackPointer;
allocatedStacks.push(stackPointer);
reinterpret_cast<ucontext_t *>(context)->uc_stack.ss_size = 64 * 1024;
makecontext(reinterpret_cast<ucontext_t *>(context), (void(*)())contextProcedureStatic, 1, reinterpret_cast<int*>(this));
++contextCount;
} else {
context = reusableContexts.top();
reusableContexts.pop();
}
resumingContexts.push(context);
spawningProcedures.emplace(std::move(procedure));
}
int Dispatcher::getTimer() {
int timer;
if (timers.empty()) {
timer = ++lastCreatedTimer;
} else {
timer = timers.top();
timers.pop();
}
return timer;
}
void Dispatcher::pushTimer(int timer) {
timers.push(timer);
}
void Dispatcher::clear() {
//TODO
}
void Dispatcher::yield() {
void* context;
for (;;) {
if (!resumingContexts.empty()) {
context = resumingContexts.front();
resumingContexts.pop();
break;
}
struct kevent event;
int count = kevent(kqueue, NULL, 0, &event, 1, NULL);
if (count == 1) {
context = static_cast<ContextExt*>(event.udata)->context;
break;
}
if (errno != EINTR) {
std::cerr << "kevent() failed, errno=" << errno << std::endl;
throw std::runtime_error("Dispatcher::yield()");
}
}
if (context != currentContext) {
ucontext_t* oldContext = static_cast<ucontext_t*>(currentContext);
currentContext = context;
if (-1 == swapcontext(oldContext, static_cast<ucontext_t *>(context))) {
std::cerr << "setcontext() failed, errno=" << errno << std::endl;
throw std::runtime_error("Dispatcher::yield()");
}
}
}
void Dispatcher::contextProcedure() {
void* context = currentContext;
for (;;) {
assert(!spawningProcedures.empty());
std::function<void()> procedure = std::move(spawningProcedures.front());
spawningProcedures.pop();
procedure();
reusableContexts.push(context);
yield();
}
}

View file

@ -0,0 +1,53 @@
// Copyright (c) 2011-2015 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 <functional>
#include <queue>
#include <stack>
namespace System {
class Dispatcher {
public:
Dispatcher();
Dispatcher(const Dispatcher&) = delete;
~Dispatcher();
Dispatcher& operator=(const Dispatcher&) = delete;
void spawn(std::function<void()>&& procedure);
void yield();
void clear();
struct ContextExt {
void *context;
};
private:
friend class Event;
friend class DispatcherAccessor;
friend class TcpConnection;
friend class TcpConnector;
friend class TcpListener;
friend class Timer;
int kqueue;
void* currentContext;
int lastCreatedTimer;
std::size_t contextCount;
std::queue<void*> resumingContexts;
std::stack<void*> reusableContexts;
std::stack<uint8_t *> allocatedStacks;
std::queue<std::function<void()>> spawningProcedures;
std::stack<int> timers;
int getKqueue() const;
int getTimer();
void pushTimer(int timer);
void pushContext(void* context);
void* getCurrentContext() const;
void contextProcedure();
static void contextProcedureStatic(void* context);
};
}

View file

@ -0,0 +1,93 @@
// Copyright (c) 2011-2015 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 "Event.h"
#include <cassert>
#include <iostream>
#include "Dispatcher.h"
using namespace System;
namespace {
struct Waiter {
Waiter* next;
void* context;
};
}
Event::Event() : dispatcher(nullptr) {
}
Event::Event(Dispatcher& dispatcher) : dispatcher(&dispatcher), first(nullptr), state(false) {
}
Event::Event(Event&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
first = other.first;
if (other.first != nullptr) {
last = other.last;
}
state = other.state;
other.dispatcher = nullptr;
}
}
Event::~Event() {
assert(first == nullptr);
}
Event& Event::operator=(Event&& other) {
assert(first == nullptr);
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
first = other.first;
if (other.first != nullptr) {
last = other.last;
}
state = other.state;
other.dispatcher = nullptr;
}
return *this;
}
bool Event::get() const {
assert(dispatcher != nullptr);
return state;
}
void Event::clear() {
assert(dispatcher != nullptr);
state = false;
}
void Event::set() {
assert(dispatcher != nullptr);
state = true;
for (Waiter* waiter = static_cast<Waiter*>(first); waiter != nullptr; waiter = waiter->next) {
dispatcher->pushContext(waiter->context);
}
first = nullptr;
}
void Event::wait() {
assert(dispatcher != nullptr);
if (!state) {
Waiter waiter = { nullptr, dispatcher->getCurrentContext() };
if (first != nullptr) {
static_cast<Waiter*>(last)->next = &waiter;
} else {
first = &waiter;
}
last = &waiter;
dispatcher->yield();
assert(dispatcher != nullptr);
}
}

View file

@ -0,0 +1,32 @@
// Copyright (c) 2011-2015 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
namespace System {
class Dispatcher;
class Event {
public:
Event();
explicit Event(Dispatcher& dispatcher);
Event(const Event&) = delete;
Event(Event&& other);
~Event();
Event& operator=(const Event&) = delete;
Event& operator=(Event&& other);
bool get() const;
void clear();
void set();
void wait();
private:
Dispatcher* dispatcher;
void* first;
void* last;
bool state;
};
}

View file

@ -0,0 +1,5 @@
// Copyright (c) 2011-2015 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 "InterruptedException.h"

View file

@ -0,0 +1,14 @@
// Copyright (c) 2011-2015 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 <exception>
namespace System {
class InterruptedException : public std::exception {
};
}

View file

@ -0,0 +1,234 @@
// Copyright (c) 2011-2015 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 "TcpConnection.h"
#include <unistd.h>
#include <assert.h>
#include <iostream>
#include <sys/socket.h>
#include <sys/event.h>
#include <sys/socket.h>
#include "Dispatcher.h"
#include "InterruptedException.h"
using namespace System;
namespace {
struct ConnectionContext : public Dispatcher::ContextExt {
bool interrupted;
};
}
TcpConnection::TcpConnection() : dispatcher(nullptr) {
}
TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), stopped(false), readContext(nullptr), writeContext(nullptr) {
}
TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
connection = other.connection;
stopped = other.stopped;
readContext = other.readContext;
writeContext = other.writeContext;
other.dispatcher = nullptr;
}
}
TcpConnection::~TcpConnection() {
if (dispatcher != nullptr) {
assert(readContext == nullptr);
assert(writeContext == nullptr);
if (close(connection) == -1) {
std::cerr << "close() failed, errno=" << errno << '.' << std::endl;
}
}
}
TcpConnection& TcpConnection::operator=(TcpConnection&& other) {
if (dispatcher != nullptr) {
assert(readContext == nullptr);
assert(writeContext == nullptr);
if (close(connection) == -1) {
std::cerr << "close() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpConnection::operator=");
}
}
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
connection = other.connection;
stopped = other.stopped;
readContext = other.readContext;
writeContext = other.writeContext;
other.dispatcher = nullptr;
}
return *this;
}
void TcpConnection::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
size_t TcpConnection::read(uint8_t* data, size_t size) {
assert(dispatcher != nullptr);
assert(readContext == nullptr);
if (stopped) {
throw InterruptedException();
}
ssize_t transferred = ::recv(connection, (void *)data, size, 0);
if (transferred == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
std::cerr << "recv failed, result=" << errno << '.' << std::endl;
} else {
ConnectionContext context2;
context2.context = dispatcher->getCurrentContext();
context2.interrupted = false;
struct kevent event;
EV_SET(&event, connection, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR | EV_ONESHOT, 0, 0, &context2);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
} else {
readContext = &context2;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == dispatcher->getCurrentContext());
assert(readContext == &context2);
readContext = nullptr;
context2.context = nullptr;
if (context2.interrupted) {
throw InterruptedException();
}
ssize_t transferred = ::recv(connection, (void *)data, size, 0);
if (transferred == -1) {
std::cerr << "recv failed, errno=" << errno << '.' << std::endl;
} else {
if (transferred == 0) {
std::cerr << "recv return after yield with 0 bytes" << std::endl;
int retval = -1;
socklen_t retValLen = sizeof(retval);
int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen);
if (s == -1) {
std::cerr << "getsockopt() failed, errno=" << errno << '.' << std::endl;
} else {
std::cerr << "recv getsockopt retval = " << retval << std::endl;
}
}
assert(transferred <= size);
return transferred;
}
}
}
throw std::runtime_error("TcpConnection::read");
}
assert(transferred <= size);
return transferred;
}
void TcpConnection::write(const uint8_t* data, size_t size) {
assert(dispatcher != nullptr);
assert(writeContext == nullptr);
if (stopped) {
throw InterruptedException();
}
if (size == 0) {
if (shutdown(connection, SHUT_WR) == -1) {
std::cerr << "shutdown failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpConnection::write");
}
return;
}
ssize_t transferred = ::send(connection, (void *)data, size, 0);
if (transferred == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
std::cerr << "send failed, result=" << errno << '.' << std::endl;
} else {
ConnectionContext context2;
context2.context = dispatcher->getCurrentContext();
context2.interrupted = false;
struct kevent event;
EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, 0, &context2);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
} else {
writeContext = &context2;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == dispatcher->getCurrentContext());
assert(writeContext == &context2);
writeContext = nullptr;
context2.context = nullptr;
if (context2.interrupted) {
throw InterruptedException();
}
ssize_t transferred = ::send(connection, (void *)data, size, 0);
if (transferred == -1) {
std::cerr << "recv failed, errno=" << errno << '.' << std::endl;
} else {
if (transferred == 0) {
throw std::runtime_error("send transferred 0 bytes.");
}
assert(transferred == size);
return;
}
}
}
throw std::runtime_error("TcpConnection::write");
}
}
void TcpConnection::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (writeContext != nullptr && static_cast<ConnectionContext*>(writeContext)->context != nullptr) {
ConnectionContext* context2 = static_cast<ConnectionContext*>(writeContext);
if (!context2->interrupted) {
struct kevent event;
EV_SET(&event, connection, EVFILT_WRITE, EV_DELETE | EV_DISABLE, 0, 0, NULL);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpListener::stop");
}
context2->interrupted = true;
dispatcher->pushContext(context2->context);
}
}
if (readContext != nullptr && static_cast<ConnectionContext*>(readContext)->context != nullptr) {
ConnectionContext* context2 = static_cast<ConnectionContext*>(readContext);
if (!context2->interrupted) {
struct kevent event;
EV_SET(&event, connection, EVFILT_READ, EV_DELETE | EV_DISABLE, 0, 0, NULL);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpListener::stop");
}
context2->interrupted = true;
dispatcher->pushContext(context2->context);
}
}
stopped = true;
}

View file

@ -0,0 +1,40 @@
// Copyright (c) 2011-2015 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 <cstddef>
#include <cstdint>
namespace System {
class Dispatcher;
class TcpConnection {
public:
TcpConnection();
TcpConnection(const TcpConnection&) = delete;
TcpConnection(TcpConnection&& other);
~TcpConnection();
TcpConnection& operator=(const TcpConnection&) = delete;
TcpConnection& operator=(TcpConnection&& other);
void start();
void stop();
std::size_t read(uint8_t* data, std::size_t size);
void write(const uint8_t* data, std::size_t size);
private:
friend class TcpConnector;
friend class TcpListener;
explicit TcpConnection(Dispatcher& dispatcher, int socket);
Dispatcher* dispatcher;
int connection;
bool stopped;
void* readContext;
void* writeContext;
};
}

View file

@ -0,0 +1,200 @@
// Copyright (c) 2011-2015 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 "TcpConnector.h"
#include <cassert>
#include <iostream>
#include <random>
#include <sstream>
#include <sys/event.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
#include "InterruptedException.h"
#include "Dispatcher.h"
#include "TcpConnection.h"
using namespace System;
namespace {
struct ConnectorContext : public Dispatcher::ContextExt {
int connection;
bool interrupted;
};
}
TcpConnector::TcpConnector() : dispatcher(nullptr) {
}
TcpConnector::TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher), address(address), port(port), stopped(false), context(nullptr) {
}
TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
address = other.address;
port = other.port;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
TcpConnector::~TcpConnector() {
}
TcpConnector& TcpConnector::operator=(TcpConnector&& other) {
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
address = other.address;
port = other.port;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void TcpConnector::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
void TcpConnector::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
ConnectorContext* context2 = static_cast<ConnectorContext*>(context);
if (!context2->interrupted) {
struct kevent event;
EV_SET(&event, context2->connection, EVFILT_WRITE, EV_DELETE | EV_DISABLE, 0, 0, NULL);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpConnector::stop");
}
dispatcher->pushContext(context2->context);
context2->interrupted = true;
}
}
stopped = true;
}
TcpConnection TcpConnector::connect() {
assert(dispatcher != nullptr);
assert(context == nullptr);
if (stopped) {
throw InterruptedException();
}
std::ostringstream portStream;
portStream << port;
addrinfo hints = { 0, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL };
addrinfo *addressInfos;
int result = getaddrinfo(address.c_str(), portStream.str().c_str(), &hints, &addressInfos);
if (result == -1) {
std::cerr << "getaddrinfo failed, errno=" << errno << '.' << std::endl;
} else {
std::size_t count = 0;
for (addrinfo* addressInfo = addressInfos; addressInfo != nullptr; addressInfo = addressInfo->ai_next) {
++count;
}
std::random_device randomDevice;
std::mt19937 generator(randomDevice());
std::uniform_int_distribution<std::size_t> distribution(0, count - 1);
std::size_t index = distribution(generator);
addrinfo* addressInfo = addressInfos;
for (std::size_t i = 0; i < index; ++i) {
addressInfo = addressInfo->ai_next;
}
sockaddr_in addressData = *reinterpret_cast<sockaddr_in*>(addressInfo->ai_addr);
freeaddrinfo(addressInfo);
int connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (connection == -1) {
std::cerr << "socket failed, errno=" << errno << '.' << std::endl;
} else {
sockaddr_in bindAddress;
bindAddress.sin_family = AF_INET;
bindAddress.sin_port = 0;
bindAddress.sin_addr.s_addr = INADDR_ANY;
if (bind(connection, reinterpret_cast<sockaddr*>(&bindAddress), sizeof bindAddress) != 0) {
std::cerr << "bind failed, errno=" << errno << '.' << std::endl;
} else {
int flags = fcntl(connection, F_GETFL, 0);
if (flags == -1 || (fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1)) {
std::cerr << "fcntl() failed errno=" << errno << std::endl;
} else {
int result = ::connect(connection, reinterpret_cast<sockaddr *>(&addressData), sizeof addressData);
if (result == -1) {
if (errno == EINPROGRESS) {
ConnectorContext context2;
context2.context = dispatcher->getCurrentContext();
context2.interrupted = false;
context2.connection = connection;
struct kevent event;
EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_ENABLE | EV_ONESHOT | EV_CLEAR, 0, 0, &context2);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
} else {
context = &context2;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == dispatcher->getCurrentContext());
assert(context == &context2);
context = nullptr;
context2.context = nullptr;
if (context2.interrupted) {
if (close(connection) == -1) {
std::cerr << "close failed, errno=" << errno << std::endl;
}
throw InterruptedException();
}
struct kevent event;
EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_DISABLE, 0, 0, NULL);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
} else {
int retval = -1;
socklen_t retValLen = sizeof(retval);
int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen);
if (s == -1) {
std::cerr << "getsockopt() failed, errno=" << errno << '.' << std::endl;
} else {
if (retval != 0) {
std::cerr << "connect failed; getsockopt retval = " << retval << std::endl;
} else {
return TcpConnection(*dispatcher, connection);
}
}
}
}
}
} else {
return TcpConnection(*dispatcher, connection);
}
}
}
if (close(connection) == -1) {
std::cerr << "close failed, errno=" << errno << std::endl;
}
}
}
throw std::runtime_error("TcpConnector::connect");
}

View file

@ -0,0 +1,36 @@
// Copyright (c) 2011-2015 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 System {
class Dispatcher;
class TcpConnection;
class TcpConnector {
public:
TcpConnector();
TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port);
TcpConnector(const TcpConnector&) = delete;
TcpConnector(TcpConnector&& other);
~TcpConnector();
TcpConnector& operator=(const TcpConnector&) = delete;
TcpConnector& operator=(TcpConnector&& other);
void start();
void stop();
TcpConnection connect();
private:
Dispatcher* dispatcher;
std::string address;
uint16_t port;
bool stopped;
void* context;
};
}

View file

@ -0,0 +1,189 @@
// Copyright (c) 2011-2015 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 "TcpListener.h"
#include <cassert>
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/event.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "InterruptedException.h"
#include "Dispatcher.h"
#include "TcpConnection.h"
using namespace System;
namespace {
struct ListenerContext : public Dispatcher::ContextExt {
bool interrupted;
};
}
TcpListener::TcpListener() : dispatcher(nullptr) {
}
TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
listener = other.listener;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
TcpListener::~TcpListener() {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (close(listener) == -1) {
std::cerr << "close() failed, errno=" << errno << '.' << std::endl;
}
}
}
TcpListener& TcpListener::operator=(TcpListener&& other) {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (close(listener) == -1) {
std::cerr << "close() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpListener::operator=");
}
}
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
listener = other.listener;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void TcpListener::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
TcpListener::TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher) {
listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listener == -1) {
std::cerr << "socket failed, errno=" << errno << std::endl;
} else {
int flags = fcntl(listener, F_GETFL, 0);
if (flags == -1 || (fcntl(listener, F_SETFL, flags | O_NONBLOCK) == -1)) {
std::cerr << "fcntl() failed errno=" << errno << std::endl;
} else {
sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = INADDR_ANY;
if (bind(listener, reinterpret_cast<sockaddr*>(&address), sizeof address) != 0) {
std::cerr << "bind failed, errno=" << errno << std::endl;
} else if (listen(listener, SOMAXCONN) != 0) {
std::cerr << "listen failed, errno=" << errno << std::endl;
} else {
struct kevent event;
EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_DISABLE, 0, SOMAXCONN, NULL);
if (kevent(dispatcher.getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
} else {
stopped = false;
context = nullptr;
return;
}
}
}
if (close(listener) == -1) {
std::cerr << "close failed, errno=" << errno << std::endl;
}
}
throw std::runtime_error("TcpListener::TcpListener");
}
TcpConnection TcpListener::accept() {
assert(dispatcher != nullptr);
assert(context == nullptr);
if (stopped) {
throw InterruptedException();
}
ListenerContext context2;
context2.context = dispatcher->getCurrentContext();
context2.interrupted = false;
struct kevent event;
EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, SOMAXCONN, &context2);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
} else {
context = &context2;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == dispatcher->getCurrentContext());
assert(context == &context2);
context = nullptr;
context2.context = nullptr;
if (context2.interrupted) {
if (close(listener) == -1) {
std::cerr << "close failed, errno=" << errno << std::endl;
}
throw InterruptedException();
}
struct kevent event;
EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_DISABLE, 0, 0, NULL);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
} else {
sockaddr inAddr;
socklen_t inLen = sizeof(inAddr);
int connection = ::accept(listener, &inAddr, &inLen);
if (connection == -1) {
std::cerr << "accept() failed, errno=" << errno << '.' << std::endl;
} else {
int flags = fcntl(connection, F_GETFL, 0);
if (flags == -1 || (fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1)) {
std::cerr << "fcntl() failed errno=" << errno << std::endl;
} else {
return TcpConnection(*dispatcher, connection);
}
}
}
}
throw std::runtime_error("TcpListener::accept");
}
void TcpListener::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
ListenerContext* context2 = static_cast<ListenerContext*>(context);
if (!context2->interrupted) {
context2->interrupted = true;
struct kevent event;
EV_SET(&event, listener, EVFILT_READ, EV_DELETE | EV_DISABLE, 0, 0, NULL);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpListener::stop");
}
dispatcher->pushContext(context2->context);
}
}
stopped = true;
}

View file

@ -0,0 +1,35 @@
// Copyright (c) 2011-2015 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 System {
class Dispatcher;
class TcpConnection;
class TcpListener {
public:
TcpListener();
TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port);
TcpListener(const TcpListener&) = delete;
TcpListener(TcpListener&& other);
~TcpListener();
TcpListener& operator=(const TcpListener&) = delete;
TcpListener& operator=(TcpListener&& other);
void start();
void stop();
TcpConnection accept();
private:
Dispatcher* dispatcher;
int listener;
bool stopped;
void* context;
};
}

View file

@ -0,0 +1,124 @@
// Copyright (c) 2011-2015 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 "Timer.h"
#include <cassert>
#include <iostream>
#include <sys/event.h>
#include <sys/time.h>
#include <assert.h>
#include <unistd.h>
#include "Dispatcher.h"
#include "InterruptedException.h"
using namespace System;
namespace {
struct TimerContext : public Dispatcher::ContextExt {
Dispatcher* dispatcher;
bool interrupted;
};
}
Timer::Timer() : dispatcher(nullptr) {
}
Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) {
timer = dispatcher.getTimer();
}
Timer::~Timer() {
if (dispatcher != nullptr) {
assert(context == nullptr);
dispatcher->pushTimer(timer);
}
}
Timer::Timer(Timer&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
timer = other.timer;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
Timer& Timer::operator=(Timer&& other) {
if (dispatcher != nullptr) {
assert(context == nullptr);
dispatcher->pushTimer(timer);
}
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
timer = other.timer;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void Timer::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
void Timer::sleep(std::chrono::milliseconds duration) {
assert(dispatcher != nullptr);
assert(context == nullptr);
if (stopped) {
throw InterruptedException();
}
TimerContext context2;
context2.dispatcher = dispatcher;
context2.context = dispatcher->getCurrentContext();
context2.interrupted = false;
struct kevent event;
EV_SET(&event, timer, EVFILT_TIMER, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, duration.count(), &context2);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("Timer::sleep");
}
context = &context2;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == dispatcher->getCurrentContext());
assert(context == &context2);
context = nullptr;
context2.context = nullptr;
if (context2.interrupted) {
throw InterruptedException();
}
}
void Timer::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
TimerContext* context2 = reinterpret_cast<TimerContext*>(context);
if (context2->context != nullptr) {
struct kevent event;
EV_SET(&event, timer, EVFILT_TIMER, EV_DISABLE, 0, 0, NULL);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("Timer::stop");
}
dispatcher->pushContext(context2->context);
context2->interrupted = true;
}
}
stopped = true;
}

View file

@ -0,0 +1,33 @@
// Copyright (c) 2011-2015 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 <chrono>
namespace System {
class Dispatcher;
class Timer {
public:
Timer();
explicit Timer(Dispatcher& dispatcher);
Timer(const Timer&) = delete;
Timer(Timer&& other);
~Timer();
Timer& operator=(const Timer&) = delete;
Timer& operator=(Timer&& other);
void start();
void stop();
void sleep(std::chrono::milliseconds duration);
private:
Dispatcher* dispatcher;
int timer;
bool stopped;
void* context;
};
}

View file

@ -0,0 +1,173 @@
// Copyright (c) 2011-2015 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 "Dispatcher.h"
#include <cassert>
#include <iostream>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
using namespace System;
namespace {
struct OverlappedExt : public OVERLAPPED {
void* context;
};
}
Dispatcher::Dispatcher() {
completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (completionPort == NULL) {
std::cerr << "CreateIoCompletionPort failed, result=" << GetLastError() << '.' << std::endl;
} else {
if (ConvertThreadToFiberEx(NULL, 0) == NULL) {
std::cerr << "ConvertThreadToFiberEx failed, result=" << GetLastError() << '.' << std::endl;
} else {
WSADATA wsaData;
int result = WSAStartup(0x0202, &wsaData);
if (result != 0) {
std::cerr << "WSAStartup failed, result=" << result << '.' << std::endl;
} else {
contextCount = 0;
return;
}
if (ConvertFiberToThread() != TRUE) {
std::cerr << "ConvertFiberToThread failed, result=" << GetLastError() << '.' << std::endl;
}
}
if (CloseHandle(completionPort) != TRUE) {
std::cerr << "CloseHandle failed, result=" << GetLastError() << '.' << std::endl;
}
}
throw std::runtime_error("Dispatcher::Dispatcher");
}
Dispatcher::~Dispatcher() {
assert(resumingContexts.empty());
assert(reusableContexts.size() == contextCount);
assert(spawningProcedures.empty());
while (!reusableContexts.empty()) {
DeleteFiber(reusableContexts.top());
reusableContexts.pop();
}
while (!timers.empty()) {
if (CloseHandle(timers.top()) != TRUE) {
std::cerr << "CloseHandle failed, result=" << GetLastError() << '.' << std::endl;
}
timers.pop();
}
if (WSACleanup() != 0) {
std::cerr << "WSACleanup failed, result=" << WSAGetLastError() << '.' << std::endl;
}
if (ConvertFiberToThread() != TRUE) {
std::cerr << "ConvertFiberToThread failed, result=" << GetLastError() << '.' << std::endl;
}
if (CloseHandle(completionPort) != TRUE) {
std::cerr << "CloseHandle failed, result=" << GetLastError() << '.' << std::endl;
}
}
void* Dispatcher::getCompletionPort() const {
return completionPort;
}
void* Dispatcher::getTimer() {
void* timer;
if (timers.empty()) {
timer = CreateWaitableTimer(NULL, FALSE, NULL);
if (timer == NULL) {
std::cerr << "CreateWaitableTimer failed, result=" << GetLastError() << '.' << std::endl;
throw std::runtime_error("Dispatcher::getTimer");
}
} else {
timer = timers.top();
timers.pop();
}
return timer;
}
void Dispatcher::pushTimer(void* timer) {
timers.push(timer);
}
void Dispatcher::pushContext(void* context) {
resumingContexts.push(context);
}
void Dispatcher::spawn(std::function<void()>&& procedure) {
void* context;
if (reusableContexts.empty()) {
context = CreateFiberEx(16384, 65536, 0, contextProcedureStatic, this);
if (context == NULL) {
std::cerr << "CreateFiberEx failed, result=" << GetLastError() << '.' << std::endl;
throw std::runtime_error("Dispatcher::spawn");
}
++contextCount;
} else {
context = reusableContexts.top();
reusableContexts.pop();
}
resumingContexts.push(context);
spawningProcedures.emplace(std::move(procedure));
}
void Dispatcher::clear() {
//TODO
}
void Dispatcher::yield() {
void* context;
for (;;) {
if (!resumingContexts.empty()) {
context = resumingContexts.front();
resumingContexts.pop();
break;
}
OVERLAPPED_ENTRY entry;
ULONG actual = 0;
if (GetQueuedCompletionStatusEx(completionPort, &entry, 1, &actual, INFINITE, TRUE) == TRUE) {
context = reinterpret_cast<OverlappedExt*>(entry.lpOverlapped)->context;
break;
}
DWORD lastError = GetLastError();
if (lastError != WAIT_IO_COMPLETION) {
std::cerr << "GetQueuedCompletionStatusEx failed, result=" << lastError << '.' << std::endl;
throw std::runtime_error("Dispatcher::yield");
}
}
if (context != GetCurrentFiber()) {
SwitchToFiber(context);
}
}
void Dispatcher::contextProcedure() {
for (;;) {
assert(!spawningProcedures.empty());
std::function<void()> procedure = std::move(spawningProcedures.front());
spawningProcedures.pop();
procedure();
reusableContexts.push(GetCurrentFiber());
yield();
}
}
void __stdcall Dispatcher::contextProcedureStatic(void* context) {
static_cast<Dispatcher*>(context)->contextProcedure();
}

View file

@ -0,0 +1,47 @@
// Copyright (c) 2011-2015 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 <functional>
#include <queue>
#include <stack>
namespace System {
class Dispatcher {
public:
Dispatcher();
Dispatcher(const Dispatcher&) = delete;
~Dispatcher();
Dispatcher& operator=(const Dispatcher&) = delete;
void spawn(std::function<void()>&& procedure);
void yield();
void clear();
private:
friend class Event;
friend class DispatcherAccessor;
friend class TcpConnection;
friend class TcpConnector;
friend class TcpListener;
friend class Timer;
void* completionPort;
std::size_t contextCount;
std::queue<void*> resumingContexts;
std::stack<void*> reusableContexts;
std::queue<std::function<void()>> spawningProcedures;
std::stack<void*> timers;
void* getCompletionPort() const;
void* getTimer();
void pushTimer(void* timer);
void pushContext(void* context);
void contextProcedure();
static void __stdcall contextProcedureStatic(void* context);
};
}

View file

@ -0,0 +1,94 @@
// Copyright (c) 2011-2015 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 "Event.h"
#include <cassert>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "Dispatcher.h"
using namespace System;
namespace {
struct Waiter {
Waiter* next;
void* context;
};
}
Event::Event() : dispatcher(nullptr) {
}
Event::Event(Dispatcher& dispatcher) : dispatcher(&dispatcher), first(nullptr), state(false) {
}
Event::Event(Event&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
first = other.first;
if (other.first != nullptr) {
last = other.last;
}
state = other.state;
other.dispatcher = nullptr;
}
}
Event::~Event() {
assert(first == nullptr);
}
Event& Event::operator=(Event&& other) {
assert(first == nullptr);
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
first = other.first;
if (other.first != nullptr) {
last = other.last;
}
state = other.state;
other.dispatcher = nullptr;
}
return *this;
}
bool Event::get() const {
assert(dispatcher != nullptr);
return state;
}
void Event::clear() {
assert(dispatcher != nullptr);
state = false;
}
void Event::set() {
assert(dispatcher != nullptr);
state = true;
for (Waiter* waiter = static_cast<Waiter*>(first); waiter != nullptr; waiter = waiter->next) {
dispatcher->pushContext(waiter->context);
}
first = nullptr;
}
void Event::wait() {
assert(dispatcher != nullptr);
if (!state) {
Waiter waiter = {nullptr, GetCurrentFiber()};
if (first != nullptr) {
static_cast<Waiter*>(last)->next = &waiter;
} else {
first = &waiter;
}
last = &waiter;
dispatcher->yield();
assert(dispatcher != nullptr);
}
}

View file

@ -0,0 +1,32 @@
// Copyright (c) 2011-2015 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
namespace System {
class Dispatcher;
class Event {
public:
Event();
explicit Event(Dispatcher& dispatcher);
Event(const Event&) = delete;
Event(Event&& other);
~Event();
Event& operator=(const Event&) = delete;
Event& operator=(Event&& other);
bool get() const;
void clear();
void set();
void wait();
private:
Dispatcher* dispatcher;
void* first;
void* last;
bool state;
};
}

View file

@ -0,0 +1,5 @@
// Copyright (c) 2011-2015 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 "InterruptedException.h"

View file

@ -0,0 +1,14 @@
// Copyright (c) 2011-2015 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 <exception>
namespace System {
class InterruptedException : public std::exception {
};
}

View file

@ -0,0 +1,227 @@
// Copyright (c) 2011-2015 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 "TcpConnection.h"
#include <cassert>
#include <iostream>
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include "InterruptedException.h"
#include "Dispatcher.h"
using namespace System;
namespace {
struct OverlappedExt : public OVERLAPPED {
void* context;
bool interrupted;
};
struct Context {
Dispatcher* dispatcher;
OverlappedExt* read;
OverlappedExt* write;
};
}
TcpConnection::TcpConnection() : dispatcher(nullptr) {
}
TcpConnection::TcpConnection(Dispatcher& dispatcher, std::size_t connection) : dispatcher(&dispatcher), connection(connection), stopped(false), context(nullptr) {
}
TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
connection = other.connection;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
TcpConnection::~TcpConnection() {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (closesocket(connection) != 0) {
std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl;
}
}
}
TcpConnection& TcpConnection::operator=(TcpConnection&& other) {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (closesocket(connection) != 0) {
std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl;
throw std::runtime_error("TcpConnection::operator=");
}
}
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
connection = other.connection;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void TcpConnection::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
void TcpConnection::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
Context* context2 = static_cast<Context*>(context);
if (context2->read != nullptr && !context2->read->interrupted) {
if (CancelIoEx(reinterpret_cast<HANDLE>(connection), context2->read) != TRUE) {
std::cerr << "CancelIoEx failed, result=" << GetLastError() << '.' << std::endl;
throw std::runtime_error("TcpConnection::stop");
}
context2->read->interrupted = true;
}
if (context2->write != nullptr && !context2->write->interrupted) {
if (CancelIoEx(reinterpret_cast<HANDLE>(connection), context2->write) != TRUE) {
std::cerr << "CancelIoEx failed, result=" << GetLastError() << '.' << std::endl;
throw std::runtime_error("TcpConnection::stop");
}
context2->write->interrupted = true;
}
}
stopped = true;
}
size_t TcpConnection::read(uint8_t* data, size_t size) {
assert(dispatcher != nullptr);
assert(context == nullptr || static_cast<Context*>(context)->read == nullptr);
if (stopped) {
throw InterruptedException();
}
WSABUF buf{static_cast<ULONG>(size), reinterpret_cast<char*>(data)};
DWORD flags = 0;
OverlappedExt overlapped;
overlapped.hEvent = NULL;
if (WSARecv(connection, &buf, 1, NULL, &flags, &overlapped, NULL) != 0) {
int lastError = WSAGetLastError();
if (lastError != WSA_IO_PENDING) {
std::cerr << "WSARecv failed, result=" << lastError << '.' << std::endl;
throw std::runtime_error("TcpConnection::read");
}
}
assert(flags == 0);
Context context2;
if (context == nullptr) {
context2.dispatcher = dispatcher;
context2.write = nullptr;
context = &context2;
}
overlapped.context = GetCurrentFiber();
overlapped.interrupted = false;
static_cast<Context*>(context)->read = &overlapped;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(overlapped.context == GetCurrentFiber());
assert(static_cast<Context*>(context)->read == &overlapped);
if (static_cast<Context*>(context)->write != nullptr) {
static_cast<Context*>(context)->read = nullptr;
} else {
context = nullptr;
}
DWORD transferred;
if (WSAGetOverlappedResult(connection, &overlapped, &transferred, FALSE, &flags) != TRUE) {
int lastError = WSAGetLastError();
if (lastError == ERROR_OPERATION_ABORTED) {
assert(overlapped.interrupted);
throw InterruptedException();
}
std::cerr << "WSARecv failed, result=" << lastError << '.' << std::endl;
throw std::runtime_error("TcpConnection::read");
}
assert(transferred <= size);
assert(flags == 0);
return transferred;
}
void TcpConnection::write(const uint8_t* data, size_t size) {
assert(dispatcher != nullptr);
assert(context == nullptr || static_cast<Context*>(context)->write == nullptr);
if (stopped) {
throw InterruptedException();
}
if (size == 0) {
if (shutdown(connection, SD_SEND) != 0) {
std::cerr << "shutdown failed, result=" << WSAGetLastError() << '.' << std::endl;
throw std::runtime_error("TcpConnection::write");
}
return;
}
WSABUF buf{static_cast<ULONG>(size), reinterpret_cast<char*>(const_cast<uint8_t*>(data))};
OverlappedExt overlapped;
overlapped.hEvent = NULL;
if (WSASend(connection, &buf, 1, NULL, 0, &overlapped, NULL) != 0) {
int lastError = WSAGetLastError();
if (lastError != WSA_IO_PENDING) {
std::cerr << "WSASend failed, result=" << lastError << '.' << std::endl;
throw std::runtime_error("TcpConnection::write");
}
}
Context context2;
if (context == nullptr) {
context2.dispatcher = dispatcher;
context2.read = nullptr;
context = &context2;
}
overlapped.context = GetCurrentFiber();
overlapped.interrupted = false;
static_cast<Context*>(context)->write = &overlapped;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(overlapped.context == GetCurrentFiber());
assert(static_cast<Context*>(context)->write == &overlapped);
if (static_cast<Context*>(context)->read != nullptr) {
static_cast<Context*>(context)->write = nullptr;
} else {
context = nullptr;
}
DWORD transferred;
DWORD flags;
if (WSAGetOverlappedResult(connection, &overlapped, &transferred, FALSE, &flags) != TRUE) {
int lastError = WSAGetLastError();
if (lastError == ERROR_OPERATION_ABORTED) {
assert(overlapped.interrupted);
throw InterruptedException();
}
std::cerr << "WSSend failed, result=" << lastError << '.' << std::endl;
throw std::runtime_error("TcpConnection::write");
}
assert(transferred == size);
assert(flags == 0);
}

View file

@ -0,0 +1,39 @@
// Copyright (c) 2011-2015 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 <cstddef>
#include <cstdint>
namespace System {
class Dispatcher;
class TcpConnection {
public:
TcpConnection();
TcpConnection(const TcpConnection&) = delete;
TcpConnection(TcpConnection&& other);
~TcpConnection();
TcpConnection& operator=(const TcpConnection&) = delete;
TcpConnection& operator=(TcpConnection&& other);
void start();
void stop();
std::size_t read(uint8_t* data, std::size_t size);
void write(const uint8_t* data, std::size_t size);
private:
friend class TcpConnector;
friend class TcpListener;
explicit TcpConnection(Dispatcher& dispatcher, std::size_t connection);
Dispatcher* dispatcher;
std::size_t connection;
bool stopped;
void* context;
};
}

View file

@ -0,0 +1,191 @@
// Copyright (c) 2011-2015 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 "TcpConnector.h"
#include <cassert>
#include <iostream>
#include <random>
#include <sstream>
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
#include <mswsock.h>
#include "InterruptedException.h"
#include "Dispatcher.h"
#include "TcpConnection.h"
using namespace System;
namespace {
struct Context : public OVERLAPPED {
void* context;
std::size_t connection;
bool interrupted;
};
LPFN_CONNECTEX connectEx = nullptr;
}
TcpConnector::TcpConnector() : dispatcher(nullptr) {
}
TcpConnector::TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher), address(address), port(port), stopped(false), context(nullptr) {
}
TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
address = other.address;
port = other.port;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
TcpConnector::~TcpConnector() {
}
TcpConnector& TcpConnector::operator=(TcpConnector&& other) {
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
address = other.address;
port = other.port;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void TcpConnector::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
void TcpConnector::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
Context* context2 = static_cast<Context*>(context);
if (!context2->interrupted) {
if (CancelIoEx(reinterpret_cast<HANDLE>(context2->connection), context2) != TRUE) {
std::cerr << "CancelIoEx failed, result=" << GetLastError() << '.' << std::endl;
throw std::runtime_error("TcpConnector::stop");
}
context2->interrupted = true;
}
}
stopped = true;
}
TcpConnection TcpConnector::connect() {
assert(dispatcher != nullptr);
assert(context == nullptr);
if (stopped) {
throw InterruptedException();
}
std::ostringstream portStream;
portStream << port;
addrinfo hints = {0, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL};
addrinfo* addressInfos;
int result = getaddrinfo(address.c_str(), portStream.str().c_str(), &hints, &addressInfos);
if (result != 0) {
std::cerr << "getaddrinfo failed, result=" << result << '.' << std::endl;
} else {
std::size_t count = 0;
for (addrinfo* addressInfo = addressInfos; addressInfo != nullptr; addressInfo = addressInfo->ai_next) {
++count;
}
std::random_device randomDevice;
std::mt19937 generator(randomDevice());
std::uniform_int_distribution<std::size_t> distribution(0, count - 1);
std::size_t index = distribution(generator);
addrinfo* addressInfo = addressInfos;
for (std::size_t i = 0; i < index; ++i) {
addressInfo = addressInfo->ai_next;
}
sockaddr_in addressData = *reinterpret_cast<sockaddr_in*>(addressInfo->ai_addr);
freeaddrinfo(addressInfo);
SOCKET connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (connection == INVALID_SOCKET) {
std::cerr << "socket failed, result=" << WSAGetLastError() << '.' << std::endl;
} else {
sockaddr_in bindAddress;
bindAddress.sin_family = AF_INET;
bindAddress.sin_port = 0;
bindAddress.sin_addr.s_addr = INADDR_ANY;
if (bind(connection, reinterpret_cast<sockaddr*>(&bindAddress), sizeof bindAddress) != 0) {
std::cerr << "bind failed, result=" << WSAGetLastError() << '.' << std::endl;
} else {
GUID guidConnectEx = WSAID_CONNECTEX;
DWORD read = sizeof connectEx;
if (connectEx == nullptr && WSAIoctl(connection, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidConnectEx, sizeof guidConnectEx, &connectEx, sizeof connectEx, &read, NULL, NULL) != 0) {
std::cerr << "WSAIoctl failed, result=" << WSAGetLastError() << '.' << std::endl;
} else {
assert(read == sizeof connectEx);
if (CreateIoCompletionPort(reinterpret_cast<HANDLE>(connection), dispatcher->getCompletionPort(), 0, 0) != dispatcher->getCompletionPort()) {
std::cerr << "CreateIoCompletionPort failed, result=" << GetLastError() << '.' << std::endl;
} else {
addressData.sin_port = htons(port);
Context context2;
context2.hEvent = NULL;
if (connectEx(connection, reinterpret_cast<sockaddr*>(&addressData), sizeof addressData, NULL, 0, NULL, &context2) == TRUE) {
std::cerr << "ConnectEx returned immediately, which is not supported." << std::endl;
} else {
int lastError = WSAGetLastError();
if (lastError != WSA_IO_PENDING) {
std::cerr << "ConnectEx failed, result=" << lastError << '.' << std::endl;
} else {
context2.context = GetCurrentFiber();
context2.connection = connection;
context2.interrupted = false;
context = &context2;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == GetCurrentFiber());
assert(context2.connection == connection);
assert(context == &context2);
context = nullptr;
DWORD transferred;
DWORD flags;
if (WSAGetOverlappedResult(connection, &context2, &transferred, FALSE, &flags) != TRUE) {
lastError = WSAGetLastError();
if (lastError == ERROR_OPERATION_ABORTED) {
assert(context2.interrupted);
if (closesocket(connection) != 0) {
std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl;
}
throw InterruptedException();
}
std::cerr << "ConnectEx failed, result=" << lastError << '.' << std::endl;
} else {
assert(transferred == 0);
assert(flags == 0);
return TcpConnection(*dispatcher, connection);
}
}
}
}
}
}
if (closesocket(connection) != 0) {
std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl;
}
}
}
throw std::runtime_error("TcpConnector::connect");
}

View file

@ -0,0 +1,36 @@
// Copyright (c) 2011-2015 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 System {
class Dispatcher;
class TcpConnection;
class TcpConnector {
public:
TcpConnector();
TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port);
TcpConnector(const TcpConnector&) = delete;
TcpConnector(TcpConnector&& other);
~TcpConnector();
TcpConnector& operator=(const TcpConnector&) = delete;
TcpConnector& operator=(TcpConnector&& other);
void start();
void stop();
TcpConnection connect();
private:
Dispatcher* dispatcher;
std::string address;
uint16_t port;
bool stopped;
void* context;
};
}

View file

@ -0,0 +1,213 @@
// Copyright (c) 2011-2015 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 "TcpListener.h"
#include <cassert>
#include <iostream>
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <mswsock.h>
#include "InterruptedException.h"
#include "Dispatcher.h"
#include "TcpConnection.h"
using namespace System;
namespace {
struct Context : public OVERLAPPED {
void* context;
bool interrupted;
};
LPFN_ACCEPTEX acceptEx = nullptr;
LPFN_GETACCEPTEXSOCKADDRS getAcceptExSockaddrs = nullptr;
}
TcpListener::TcpListener() : dispatcher(nullptr) {
}
TcpListener::TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher) {
listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listener == INVALID_SOCKET) {
std::cerr << "socket failed, result=" << WSAGetLastError() << '.' << std::endl;
} else {
sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = INADDR_ANY;
if (bind(listener, reinterpret_cast<sockaddr*>(&address), sizeof address) != 0) {
std::cerr << "bind failed, result=" << WSAGetLastError() << '.' << std::endl;
} else if (listen(listener, SOMAXCONN) != 0) {
std::cerr << "listen failed, result=" << WSAGetLastError() << '.' << std::endl;
} else {
GUID guidAcceptEx = WSAID_ACCEPTEX;
DWORD read = sizeof acceptEx;
if (acceptEx == nullptr && WSAIoctl(listener, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidAcceptEx, sizeof guidAcceptEx, &acceptEx, sizeof acceptEx, &read, NULL, NULL) != 0) {
std::cerr << "WSAIoctl failed, result=" << WSAGetLastError() << '.' << std::endl;
} else {
assert(read == sizeof acceptEx);
GUID guidGetAcceptExSockaddrs = WSAID_GETACCEPTEXSOCKADDRS;
read = sizeof getAcceptExSockaddrs;
if (getAcceptExSockaddrs == nullptr && WSAIoctl(listener, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidGetAcceptExSockaddrs, sizeof guidGetAcceptExSockaddrs, &getAcceptExSockaddrs, sizeof getAcceptExSockaddrs, &read, NULL, NULL) != 0) {
std::cerr << "WSAIoctl failed, result=" << WSAGetLastError() << '.' << std::endl;
} else {
assert(read == sizeof getAcceptExSockaddrs);
if (CreateIoCompletionPort(reinterpret_cast<HANDLE>(listener), dispatcher.getCompletionPort(), 0, 0) != dispatcher.getCompletionPort()) {
std::cerr << "CreateIoCompletionPort failed, result=" << GetLastError() << '.' << std::endl;
} else {
stopped = false;
context = nullptr;
return;
}
}
}
}
if (closesocket(listener) != 0) {
std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl;
}
}
throw std::runtime_error("TcpListener::TcpListener");
}
TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
listener = other.listener;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
TcpListener::~TcpListener() {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (closesocket(listener) != 0) {
std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl;
}
}
}
TcpListener& TcpListener::operator=(TcpListener&& other) {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (closesocket(listener) != 0) {
std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl;
throw std::runtime_error("TcpListener::operator=");
}
}
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
listener = other.listener;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void TcpListener::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
void TcpListener::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
Context* context2 = static_cast<Context*>(context);
if (!context2->interrupted) {
if (CancelIoEx(reinterpret_cast<HANDLE>(listener), context2) != TRUE) {
std::cerr << "CancelIoEx failed, result=" << GetLastError() << '.' << std::endl;
throw std::runtime_error("TcpListener::stop");
}
context2->interrupted = true;
}
}
stopped = true;
}
TcpConnection TcpListener::accept() {
assert(dispatcher != nullptr);
assert(context == nullptr);
if (stopped) {
throw InterruptedException();
}
SOCKET connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (connection == INVALID_SOCKET) {
std::cerr << "socket failed, result=" << WSAGetLastError() << '.' << std::endl;
} else {
uint8_t addresses[sizeof sockaddr_in * 2 + 32];
DWORD received;
Context context2;
context2.hEvent = NULL;
if (acceptEx(listener, connection, addresses, 0, sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, &received, &context2) == TRUE) {
std::cerr << "AcceptEx returned immediately, which is not supported." << std::endl;
} else {
int lastError = WSAGetLastError();
if (lastError != WSA_IO_PENDING) {
std::cerr << "AcceptEx failed, result=" << lastError << '.' << std::endl;
} else {
context2.context = GetCurrentFiber();
context2.interrupted = false;
context = &context2;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == GetCurrentFiber());
assert(context == &context2);
context = nullptr;
DWORD transferred;
DWORD flags;
if (WSAGetOverlappedResult(listener, &context2, &transferred, FALSE, &flags) != TRUE) {
lastError = WSAGetLastError();
if (lastError == ERROR_OPERATION_ABORTED) {
assert(context2.interrupted);
if (closesocket(connection) != 0) {
std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl;
}
throw InterruptedException();
}
std::cerr << "AcceptEx failed, result=" << lastError << '.' << std::endl;
} else {
assert(transferred == 0);
assert(flags == 0);
if (setsockopt(connection, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, reinterpret_cast<char*>(&listener), sizeof listener) != 0) {
std::cerr << "setsockopt failed, result=" << WSAGetLastError() << '.' << std::endl;
} else {
if (CreateIoCompletionPort(reinterpret_cast<HANDLE>(connection), dispatcher->getCompletionPort(), 0, 0) != dispatcher->getCompletionPort()) {
std::cerr << "CreateIoCompletionPort failed, result=" << GetLastError() << '.' << std::endl;
} else {
//sockaddr_in* local;
//int localSize;
//sockaddr_in* remote;
//int remoteSize;
//static_cast<LPFN_GETACCEPTEXSOCKADDRS>(getAcceptExSockaddrs)(addresses, 0, sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, reinterpret_cast<sockaddr**>(&local), &localSize, reinterpret_cast<sockaddr**>(&remote), &remoteSize);
//assert(localSize == sizeof sockaddr_in);
//assert(remoteSize == sizeof sockaddr_in);
//std::cout << "Client connected from " << static_cast<unsigned int>(remote->sin_addr.S_un.S_un_b.s_b1) << '.' << static_cast<unsigned int>(remote->sin_addr.S_un.S_un_b.s_b2) << '.' << static_cast<unsigned int>(remote->sin_addr.S_un.S_un_b.s_b3) << '.' << static_cast<unsigned int>(remote->sin_addr.S_un.S_un_b.s_b4) << ':' << remote->sin_port << std::endl;
return TcpConnection(*dispatcher, connection);
}
}
}
}
}
if (closesocket(connection) != 0) {
std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl;
}
}
throw std::runtime_error("TcpListener::accept");
}

View file

@ -0,0 +1,35 @@
// Copyright (c) 2011-2015 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 System {
class Dispatcher;
class TcpConnection;
class TcpListener {
public:
TcpListener();
TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port);
TcpListener(const TcpListener&) = delete;
TcpListener(TcpListener&& other);
~TcpListener();
TcpListener& operator=(const TcpListener&) = delete;
TcpListener& operator=(TcpListener&& other);
void start();
void stop();
TcpConnection accept();
private:
Dispatcher* dispatcher;
std::size_t listener;
bool stopped;
void* context;
};
}

View file

@ -0,0 +1,133 @@
// Copyright (c) 2011-2015 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 "Timer.h"
#include <cassert>
#include <iostream>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "InterruptedException.h"
#include "Dispatcher.h"
using namespace System;
namespace System {
class DispatcherAccessor {
public:
DispatcherAccessor(Dispatcher* dispatcher, void* context) {
dispatcher->pushContext(context);
}
};
}
namespace {
struct Context {
Dispatcher* dispatcher;
void* context;
bool interrupted;
};
void __stdcall callbackProcedure(void* lpArgToCompletionRoutine, DWORD dwTimerLowValue, DWORD dwTimerHighValue) {
Context* context = static_cast<Context*>(lpArgToCompletionRoutine);
assert(context->context != nullptr);
DispatcherAccessor(context->dispatcher, context->context);
context->context = nullptr;
}
}
Timer::Timer() : dispatcher(nullptr) {
}
Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) {
timer = dispatcher.getTimer();
}
Timer::Timer(Timer&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
timer = other.timer;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
Timer::~Timer() {
if (dispatcher != nullptr) {
assert(context == nullptr);
dispatcher->pushTimer(timer);
}
}
Timer& Timer::operator=(Timer&& other) {
if (dispatcher != nullptr) {
assert(context == nullptr);
dispatcher->pushTimer(timer);
}
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
timer = other.timer;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void Timer::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
void Timer::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
Context* context2 = static_cast<Context*>(context);
if (context2->context != nullptr) {
if (CancelWaitableTimer(timer) != TRUE) {
std::cerr << "CancelWaitableTimer failed, result=" << GetLastError() << '.' << std::endl;
throw std::runtime_error("Timer::stop");
}
dispatcher->pushContext(context2->context);
context2->context = nullptr;
context2->interrupted = true;
}
}
stopped = true;
}
void Timer::sleep(std::chrono::nanoseconds duration) {
assert(dispatcher != nullptr);
assert(context == nullptr);
if (stopped) {
throw InterruptedException();
}
LARGE_INTEGER duration2;
duration2.QuadPart = static_cast<LONGLONG>(duration.count() / -100);
Context context2 = {dispatcher, GetCurrentFiber(), false};
if (SetWaitableTimer(timer, &duration2, 0, callbackProcedure, &context2, FALSE) != TRUE) {
std::cerr << "SetWaitableTimer failed, result=" << GetLastError() << '.' << std::endl;
throw std::runtime_error("Timer::sleep");
}
context = &context2;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == nullptr);
assert(context == &context2);
context = nullptr;
if (context2.interrupted) {
throw InterruptedException();
}
}

View file

@ -0,0 +1,33 @@
// Copyright (c) 2011-2015 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 <chrono>
namespace System {
class Dispatcher;
class Timer {
public:
Timer();
explicit Timer(Dispatcher& dispatcher);
Timer(const Timer&) = delete;
Timer(Timer&& other);
~Timer();
Timer& operator=(const Timer&) = delete;
Timer& operator=(Timer&& other);
void start();
void stop();
void sleep(std::chrono::nanoseconds duration);
private:
Dispatcher* dispatcher;
void* timer;
bool stopped;
void* context;
};
}

View file

@ -1,4 +1,4 @@
// Copyright (c) 2012-2013 The Cryptonote developers
// Copyright (c) 2011-2015 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

Some files were not shown because too many files have changed in this diff Show more