Paweł Bylica
8 years ago
8 changed files with 10 additions and 750 deletions
@ -1,182 +0,0 @@ |
|||||
/*
|
|
||||
This file is part of cpp-ethereum. |
|
||||
|
|
||||
cpp-ethereum is free software: you can redistribute it and/or modify |
|
||||
it under the terms of the GNU General Public License as published by |
|
||||
the Free Software Foundation, either version 3 of the License, or |
|
||||
(at your option) any later version. |
|
||||
|
|
||||
cpp-ethereum is distributed in the hope that it will be useful, |
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||
GNU General Public License for more details. |
|
||||
|
|
||||
You should have received a copy of the GNU General Public License |
|
||||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||
*/ |
|
||||
/** @file CommonIO.cpp
|
|
||||
* @author Gav Wood <i@gavwood.com> |
|
||||
* @date 2014 |
|
||||
*/ |
|
||||
|
|
||||
#include "CommonIO.h" |
|
||||
#include <iostream> |
|
||||
#include <cstdlib> |
|
||||
#include <fstream> |
|
||||
#include <stdio.h> |
|
||||
#ifdef _WIN32 |
|
||||
#include <windows.h> |
|
||||
#else |
|
||||
#include <termios.h> |
|
||||
#endif |
|
||||
#include <boost/filesystem.hpp> |
|
||||
#include "Exceptions.h" |
|
||||
using namespace std; |
|
||||
using namespace dev; |
|
||||
|
|
||||
string dev::memDump(bytes const& _bytes, unsigned _width, bool _html) |
|
||||
{ |
|
||||
stringstream ret; |
|
||||
if (_html) |
|
||||
ret << "<pre style=\"font-family: Monospace,Lucida Console,Courier,Courier New,sans-serif; font-size: small\">"; |
|
||||
for (unsigned i = 0; i < _bytes.size(); i += _width) |
|
||||
{ |
|
||||
ret << hex << setw(4) << setfill('0') << i << " "; |
|
||||
for (unsigned j = i; j < i + _width; ++j) |
|
||||
if (j < _bytes.size()) |
|
||||
if (_bytes[j] >= 32 && _bytes[j] < 127) |
|
||||
if ((char)_bytes[j] == '<' && _html) |
|
||||
ret << "<"; |
|
||||
else if ((char)_bytes[j] == '&' && _html) |
|
||||
ret << "&"; |
|
||||
else |
|
||||
ret << (char)_bytes[j]; |
|
||||
else |
|
||||
ret << '?'; |
|
||||
else |
|
||||
ret << ' '; |
|
||||
ret << " "; |
|
||||
for (unsigned j = i; j < i + _width && j < _bytes.size(); ++j) |
|
||||
ret << setfill('0') << setw(2) << hex << (unsigned)_bytes[j] << " "; |
|
||||
ret << "\n"; |
|
||||
} |
|
||||
if (_html) |
|
||||
ret << "</pre>"; |
|
||||
return ret.str(); |
|
||||
} |
|
||||
|
|
||||
template <typename _T> |
|
||||
inline _T contentsGeneric(std::string const& _file) |
|
||||
{ |
|
||||
_T ret; |
|
||||
size_t const c_elementSize = sizeof(typename _T::value_type); |
|
||||
std::ifstream is(_file, std::ifstream::binary); |
|
||||
if (!is) |
|
||||
return ret; |
|
||||
|
|
||||
// get length of file:
|
|
||||
is.seekg(0, is.end); |
|
||||
streamoff length = is.tellg(); |
|
||||
if (length == 0) |
|
||||
return ret; // do not read empty file (MSVC does not like it)
|
|
||||
is.seekg(0, is.beg); |
|
||||
|
|
||||
ret.resize((length + c_elementSize - 1) / c_elementSize); |
|
||||
is.read(const_cast<char*>(reinterpret_cast<char const*>(ret.data())), length); |
|
||||
return ret; |
|
||||
} |
|
||||
|
|
||||
bytes dev::contents(string const& _file) |
|
||||
{ |
|
||||
return contentsGeneric<bytes>(_file); |
|
||||
} |
|
||||
|
|
||||
bytesSec dev::contentsSec(string const& _file) |
|
||||
{ |
|
||||
bytes b = contentsGeneric<bytes>(_file); |
|
||||
bytesSec ret(b); |
|
||||
bytesRef(&b).cleanse(); |
|
||||
return ret; |
|
||||
} |
|
||||
|
|
||||
string dev::contentsString(string const& _file) |
|
||||
{ |
|
||||
return contentsGeneric<string>(_file); |
|
||||
} |
|
||||
|
|
||||
void dev::writeFile(std::string const& _file, bytesConstRef _data, bool _writeDeleteRename) |
|
||||
{ |
|
||||
namespace fs = boost::filesystem; |
|
||||
if (_writeDeleteRename) |
|
||||
{ |
|
||||
fs::path tempPath = fs::unique_path(_file + "-%%%%%%"); |
|
||||
writeFile(tempPath.string(), _data, false); |
|
||||
// will delete _file if it exists
|
|
||||
fs::rename(tempPath, _file); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// create directory if not existent
|
|
||||
fs::path p(_file); |
|
||||
if (!fs::exists(p.parent_path())) |
|
||||
{ |
|
||||
fs::create_directories(p.parent_path()); |
|
||||
DEV_IGNORE_EXCEPTIONS(fs::permissions(p.parent_path(), fs::owner_all)); |
|
||||
} |
|
||||
|
|
||||
ofstream s(_file, ios::trunc | ios::binary); |
|
||||
s.write(reinterpret_cast<char const*>(_data.data()), _data.size()); |
|
||||
if (!s) |
|
||||
BOOST_THROW_EXCEPTION(FileError() << errinfo_comment("Could not write to file: " + _file)); |
|
||||
DEV_IGNORE_EXCEPTIONS(fs::permissions(_file, fs::owner_read|fs::owner_write)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
std::string dev::getPassword(std::string const& _prompt) |
|
||||
{ |
|
||||
#if WIN32 |
|
||||
cout << _prompt << flush; |
|
||||
// Get current Console input flags
|
|
||||
HANDLE hStdin; |
|
||||
DWORD fdwSaveOldMode; |
|
||||
if ((hStdin = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE) |
|
||||
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("GetStdHandle")); |
|
||||
if (!GetConsoleMode(hStdin, &fdwSaveOldMode)) |
|
||||
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("GetConsoleMode")); |
|
||||
// Set console flags to no echo
|
|
||||
if (!SetConsoleMode(hStdin, fdwSaveOldMode & (~ENABLE_ECHO_INPUT))) |
|
||||
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("SetConsoleMode")); |
|
||||
// Read the string
|
|
||||
std::string ret; |
|
||||
std::getline(cin, ret); |
|
||||
// Restore old input mode
|
|
||||
if (!SetConsoleMode(hStdin, fdwSaveOldMode)) |
|
||||
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("SetConsoleMode")); |
|
||||
return ret; |
|
||||
#else |
|
||||
struct termios oflags; |
|
||||
struct termios nflags; |
|
||||
char password[256]; |
|
||||
|
|
||||
// disable echo in the terminal
|
|
||||
tcgetattr(fileno(stdin), &oflags); |
|
||||
nflags = oflags; |
|
||||
nflags.c_lflag &= ~ECHO; |
|
||||
nflags.c_lflag |= ECHONL; |
|
||||
|
|
||||
if (tcsetattr(fileno(stdin), TCSANOW, &nflags) != 0) |
|
||||
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("tcsetattr")); |
|
||||
|
|
||||
printf("%s", _prompt.c_str()); |
|
||||
if (!fgets(password, sizeof(password), stdin)) |
|
||||
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("fgets")); |
|
||||
password[strlen(password) - 1] = 0; |
|
||||
|
|
||||
// restore terminal
|
|
||||
if (tcsetattr(fileno(stdin), TCSANOW, &oflags) != 0) |
|
||||
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("tcsetattr")); |
|
||||
|
|
||||
|
|
||||
return password; |
|
||||
#endif |
|
||||
} |
|
@ -1,269 +0,0 @@ |
|||||
/*
|
|
||||
This file is part of cpp-ethereum. |
|
||||
|
|
||||
cpp-ethereum is free software: you can redistribute it and/or modify |
|
||||
it under the terms of the GNU General Public License as published by |
|
||||
the Free Software Foundation, either version 3 of the License, or |
|
||||
(at your option) any later version. |
|
||||
|
|
||||
cpp-ethereum is distributed in the hope that it will be useful, |
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||
GNU General Public License for more details. |
|
||||
|
|
||||
You should have received a copy of the GNU General Public License |
|
||||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||
*/ |
|
||||
/** @file CommonIO.h
|
|
||||
* @author Gav Wood <i@gavwood.com> |
|
||||
* @date 2014 |
|
||||
* |
|
||||
* File & stream I/O routines. |
|
||||
*/ |
|
||||
|
|
||||
#pragma once |
|
||||
|
|
||||
#include <map> |
|
||||
#include <set> |
|
||||
#include <unordered_map> |
|
||||
#include <unordered_set> |
|
||||
#include <array> |
|
||||
#include <list> |
|
||||
#include <memory> |
|
||||
#include <vector> |
|
||||
#include <array> |
|
||||
#include <sstream> |
|
||||
#include <string> |
|
||||
#include <iostream> |
|
||||
#include <chrono> |
|
||||
#include "Common.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
|
|
||||
/// Requests the user to enter a password on the console.
|
|
||||
std::string getPassword(std::string const& _prompt); |
|
||||
|
|
||||
/// Retrieve and returns the contents of the given file.
|
|
||||
/// If the file doesn't exist or isn't readable, returns an empty container / bytes.
|
|
||||
bytes contents(std::string const& _file); |
|
||||
/// Secure variation.
|
|
||||
bytesSec contentsSec(std::string const& _file); |
|
||||
/// Retrieve and returns the contents of the given file as a std::string.
|
|
||||
/// If the file doesn't exist or isn't readable, returns an empty container / bytes.
|
|
||||
std::string contentsString(std::string const& _file); |
|
||||
/// Retrieve and returns the allocated contents of the given file; if @_dest is given, don't allocate, use it directly.
|
|
||||
/// If the file doesn't exist or isn't readable, returns bytesRef(). Don't forget to delete [] the returned value's data when finished.
|
|
||||
bytesRef contentsNew(std::string const& _file, bytesRef _dest = bytesRef()); |
|
||||
|
|
||||
/// Write the given binary data into the given file, replacing the file if it pre-exists.
|
|
||||
/// Throws exception on error.
|
|
||||
/// @param _writeDeleteRename useful not to lose any data: If set, first writes to another file in
|
|
||||
/// the same directory and then moves that file.
|
|
||||
void writeFile(std::string const& _file, bytesConstRef _data, bool _writeDeleteRename = false); |
|
||||
/// Write the given binary data into the given file, replacing the file if it pre-exists.
|
|
||||
inline void writeFile(std::string const& _file, bytes const& _data, bool _writeDeleteRename = false) { writeFile(_file, bytesConstRef(&_data), _writeDeleteRename); } |
|
||||
inline void writeFile(std::string const& _file, std::string const& _data, bool _writeDeleteRename = false) { writeFile(_file, bytesConstRef(_data), _writeDeleteRename); } |
|
||||
|
|
||||
/// Nicely renders the given bytes to a string, optionally as HTML.
|
|
||||
/// @a _bytes: bytes array to be rendered as string. @a _width of a bytes line.
|
|
||||
std::string memDump(bytes const& _bytes, unsigned _width = 8, bool _html = false); |
|
||||
|
|
||||
// Stream I/O functions.
|
|
||||
// Provides templated stream I/O for all STL collections so they can be shifted on to any iostream-like interface.
|
|
||||
|
|
||||
template <class S, class T> struct StreamOut { static S& bypass(S& _out, T const& _t) { _out << _t; return _out; } }; |
|
||||
template <class S> struct StreamOut<S, uint8_t> { static S& bypass(S& _out, uint8_t const& _t) { _out << (int)_t; return _out; } }; |
|
||||
|
|
||||
template <class T> inline std::ostream& operator<<(std::ostream& _out, std::vector<T> const& _e); |
|
||||
template <class T, std::size_t Z> inline std::ostream& operator<<(std::ostream& _out, std::array<T, Z> const& _e); |
|
||||
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::pair<T, U> const& _e); |
|
||||
template <class T> inline std::ostream& operator<<(std::ostream& _out, std::list<T> const& _e); |
|
||||
template <class T1, class T2, class T3> inline std::ostream& operator<<(std::ostream& _out, std::tuple<T1, T2, T3> const& _e); |
|
||||
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::map<T, U> const& _e); |
|
||||
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::unordered_map<T, U> const& _e); |
|
||||
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::set<T, U> const& _e); |
|
||||
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::unordered_set<T, U> const& _e); |
|
||||
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::multimap<T, U> const& _e); |
|
||||
template <class _S, class _T> _S& operator<<(_S& _out, std::shared_ptr<_T> const& _p); |
|
||||
|
|
||||
#ifdef _WIN32 |
|
||||
template <class T> inline std::string toString(std::chrono::time_point<T> const& _e, std::string _format = "%Y-%m-%d %H:%M:%S") |
|
||||
#else |
|
||||
template <class T> inline std::string toString(std::chrono::time_point<T> const& _e, std::string _format = "%F %T") |
|
||||
#endif |
|
||||
{ |
|
||||
unsigned long milliSecondsSinceEpoch = std::chrono::duration_cast<std::chrono::milliseconds>(_e.time_since_epoch()).count(); |
|
||||
auto const durationSinceEpoch = std::chrono::milliseconds(milliSecondsSinceEpoch); |
|
||||
std::chrono::time_point<std::chrono::system_clock> const tpAfterDuration(durationSinceEpoch); |
|
||||
|
|
||||
tm timeValue; |
|
||||
auto time = std::chrono::system_clock::to_time_t(tpAfterDuration); |
|
||||
#ifdef _WIN32 |
|
||||
gmtime_s(&timeValue, &time); |
|
||||
#else |
|
||||
gmtime_r(&time, &timeValue); |
|
||||
#endif |
|
||||
|
|
||||
unsigned const millisRemainder = milliSecondsSinceEpoch % 1000; |
|
||||
char buffer[1024]; |
|
||||
if (strftime(buffer, sizeof(buffer), _format.c_str(), &timeValue)) |
|
||||
return std::string(buffer) + "." + (millisRemainder < 1 ? "000" : millisRemainder < 10 ? "00" : millisRemainder < 100 ? "0" : "") + std::to_string(millisRemainder) + "Z"; |
|
||||
return std::string(); |
|
||||
} |
|
||||
|
|
||||
template <class S, class T> |
|
||||
inline S& streamout(S& _out, std::vector<T> const& _e) |
|
||||
{ |
|
||||
_out << "["; |
|
||||
if (!_e.empty()) |
|
||||
{ |
|
||||
StreamOut<S, T>::bypass(_out, _e.front()); |
|
||||
for (auto i = ++_e.begin(); i != _e.end(); ++i) |
|
||||
StreamOut<S, T>::bypass(_out << ",", *i); |
|
||||
} |
|
||||
_out << "]"; |
|
||||
return _out; |
|
||||
} |
|
||||
|
|
||||
template <class T> inline std::ostream& operator<<(std::ostream& _out, std::vector<T> const& _e) { streamout(_out, _e); return _out; } |
|
||||
|
|
||||
template <class S, class T, std::size_t Z> |
|
||||
inline S& streamout(S& _out, std::array<T, Z> const& _e) |
|
||||
{ |
|
||||
_out << "["; |
|
||||
if (!_e.empty()) |
|
||||
{ |
|
||||
StreamOut<S, T>::bypass(_out, _e.front()); |
|
||||
auto i = _e.begin(); |
|
||||
for (++i; i != _e.end(); ++i) |
|
||||
StreamOut<S, T>::bypass(_out << ",", *i); |
|
||||
} |
|
||||
_out << "]"; |
|
||||
return _out; |
|
||||
} |
|
||||
template <class T, std::size_t Z> inline std::ostream& operator<<(std::ostream& _out, std::array<T, Z> const& _e) { streamout(_out, _e); return _out; } |
|
||||
|
|
||||
template <class S, class T> |
|
||||
inline S& streamout(S& _out, std::list<T> const& _e) |
|
||||
{ |
|
||||
_out << "["; |
|
||||
if (!_e.empty()) |
|
||||
{ |
|
||||
_out << _e.front(); |
|
||||
for (auto i = ++_e.begin(); i != _e.end(); ++i) |
|
||||
_out << "," << *i; |
|
||||
} |
|
||||
_out << "]"; |
|
||||
return _out; |
|
||||
} |
|
||||
template <class T> inline std::ostream& operator<<(std::ostream& _out, std::list<T> const& _e) { streamout(_out, _e); return _out; } |
|
||||
|
|
||||
template <class S, class T, class U> |
|
||||
inline S& streamout(S& _out, std::pair<T, U> const& _e) |
|
||||
{ |
|
||||
_out << "(" << _e.first << "," << _e.second << ")"; |
|
||||
return _out; |
|
||||
} |
|
||||
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::pair<T, U> const& _e) { streamout(_out, _e); return _out; } |
|
||||
|
|
||||
template <class S, class T1, class T2, class T3> |
|
||||
inline S& streamout(S& _out, std::tuple<T1, T2, T3> const& _t) |
|
||||
{ |
|
||||
_out << "(" << std::get<0>(_t) << "," << std::get<1>(_t) << "," << std::get<2>(_t) << ")"; |
|
||||
return _out; |
|
||||
} |
|
||||
template <class T1, class T2, class T3> inline std::ostream& operator<<(std::ostream& _out, std::tuple<T1, T2, T3> const& _e) { streamout(_out, _e); return _out; } |
|
||||
|
|
||||
template <class S, class T, class U> |
|
||||
S& streamout(S& _out, std::map<T, U> const& _v) |
|
||||
{ |
|
||||
if (_v.empty()) |
|
||||
return _out << "{}"; |
|
||||
int i = 0; |
|
||||
for (auto p: _v) |
|
||||
_out << (!(i++) ? "{ " : "; ") << p.first << " => " << p.second; |
|
||||
return _out << " }"; |
|
||||
} |
|
||||
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::map<T, U> const& _e) { streamout(_out, _e); return _out; } |
|
||||
|
|
||||
template <class S, class T, class U> |
|
||||
S& streamout(S& _out, std::unordered_map<T, U> const& _v) |
|
||||
{ |
|
||||
if (_v.empty()) |
|
||||
return _out << "{}"; |
|
||||
int i = 0; |
|
||||
for (auto p: _v) |
|
||||
_out << (!(i++) ? "{ " : "; ") << p.first << " => " << p.second; |
|
||||
return _out << " }"; |
|
||||
} |
|
||||
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::unordered_map<T, U> const& _e) { streamout(_out, _e); return _out; } |
|
||||
|
|
||||
template <class S, class T> |
|
||||
S& streamout(S& _out, std::set<T> const& _v) |
|
||||
{ |
|
||||
if (_v.empty()) |
|
||||
return _out << "{}"; |
|
||||
int i = 0; |
|
||||
for (auto p: _v) |
|
||||
_out << (!(i++) ? "{ " : ", ") << p; |
|
||||
return _out << " }"; |
|
||||
} |
|
||||
template <class T> inline std::ostream& operator<<(std::ostream& _out, std::set<T> const& _e) { streamout(_out, _e); return _out; } |
|
||||
|
|
||||
template <class S, class T> |
|
||||
S& streamout(S& _out, std::unordered_set<T> const& _v) |
|
||||
{ |
|
||||
if (_v.empty()) |
|
||||
return _out << "{}"; |
|
||||
int i = 0; |
|
||||
for (auto p: _v) |
|
||||
_out << (!(i++) ? "{ " : ", ") << p; |
|
||||
return _out << " }"; |
|
||||
} |
|
||||
template <class T> inline std::ostream& operator<<(std::ostream& _out, std::unordered_set<T> const& _e) { streamout(_out, _e); return _out; } |
|
||||
|
|
||||
template <class S, class T> |
|
||||
S& streamout(S& _out, std::multiset<T> const& _v) |
|
||||
{ |
|
||||
if (_v.empty()) |
|
||||
return _out << "{}"; |
|
||||
int i = 0; |
|
||||
for (auto p: _v) |
|
||||
_out << (!(i++) ? "{ " : ", ") << p; |
|
||||
return _out << " }"; |
|
||||
} |
|
||||
template <class T> inline std::ostream& operator<<(std::ostream& _out, std::multiset<T> const& _e) { streamout(_out, _e); return _out; } |
|
||||
|
|
||||
template <class S, class T, class U> |
|
||||
S& streamout(S& _out, std::multimap<T, U> const& _v) |
|
||||
{ |
|
||||
if (_v.empty()) |
|
||||
return _out << "{}"; |
|
||||
T l; |
|
||||
int i = 0; |
|
||||
for (auto p: _v) |
|
||||
if (!(i++)) |
|
||||
_out << "{ " << (l = p.first) << " => " << p.second; |
|
||||
else if (l == p.first) |
|
||||
_out << ", " << p.second; |
|
||||
else |
|
||||
_out << "; " << (l = p.first) << " => " << p.second; |
|
||||
return _out << " }"; |
|
||||
} |
|
||||
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::multimap<T, U> const& _e) { streamout(_out, _e); return _out; } |
|
||||
|
|
||||
template <class _S, class _T> _S& operator<<(_S& _out, std::shared_ptr<_T> const& _p) { if (_p) _out << "@" << (*_p); else _out << "nullptr"; return _out; } |
|
||||
|
|
||||
// Functions that use streaming stuff.
|
|
||||
|
|
||||
/// Converts arbitrary value to string representation using std::stringstream.
|
|
||||
template <class _T> |
|
||||
std::string toString(_T const& _t) |
|
||||
{ |
|
||||
std::ostringstream o; |
|
||||
o << _t; |
|
||||
return o.str(); |
|
||||
} |
|
||||
|
|
||||
} |
|
@ -1,294 +0,0 @@ |
|||||
/*
|
|
||||
This file is part of cpp-ethereum. |
|
||||
|
|
||||
cpp-ethereum is free software: you can redistribute it and/or modify |
|
||||
it under the terms of the GNU General Public License as published by |
|
||||
the Free Software Foundation, either version 3 of the License, or |
|
||||
(at your option) any later version. |
|
||||
|
|
||||
cpp-ethereum is distributed in the hope that it will be useful, |
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||
GNU General Public License for more details. |
|
||||
|
|
||||
You should have received a copy of the GNU General Public License |
|
||||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||
*/ |
|
||||
/** @file RangeMask.h
|
|
||||
* @author Gav Wood <i@gavwood.com> |
|
||||
* @date 2014 |
|
||||
*/ |
|
||||
|
|
||||
#pragma once |
|
||||
|
|
||||
#include <map> |
|
||||
#include <utility> |
|
||||
#include <vector> |
|
||||
#include <iterator> |
|
||||
#include <iostream> |
|
||||
#include <assert.h> |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
|
|
||||
class RLPStream; |
|
||||
|
|
||||
using UnsignedRange = std::pair<unsigned, unsigned>; |
|
||||
using UnsignedRanges = std::vector<UnsignedRange>; |
|
||||
|
|
||||
/**
|
|
||||
* Set of elements of a certain "ground range" representable by unions of ranges inside this |
|
||||
* ground range. |
|
||||
* Ranges are given as pairs (begin, end), denoting the interval [begin, end), i.e. end is excluded. |
|
||||
* Supports set-theoretic operators, size and iteration. |
|
||||
*/ |
|
||||
template <class T> |
|
||||
class RangeMask |
|
||||
{ |
|
||||
template <class U> friend std::ostream& operator<<(std::ostream& _out, RangeMask<U> const& _r); |
|
||||
|
|
||||
public: |
|
||||
using Range = std::pair<T, T>; |
|
||||
using Ranges = std::vector<Range>; |
|
||||
|
|
||||
/// Constructs an empty range mask with empty ground range.
|
|
||||
RangeMask(): m_all(0, 0) {} |
|
||||
/// Constructs an empty range mask with ground range [_begin, _end).
|
|
||||
RangeMask(T _begin, T _end): m_all(_begin, _end) {} |
|
||||
/// Constructs an empty range mask with ground range _c.
|
|
||||
RangeMask(Range const& _c): m_all(_c) {} |
|
||||
|
|
||||
/// @returns the union with the range mask _m, taking also the union of the ground ranges.
|
|
||||
RangeMask unionedWith(RangeMask const& _m) const { return operator+(_m); } |
|
||||
RangeMask operator+(RangeMask const& _m) const { return RangeMask(*this) += _m; } |
|
||||
|
|
||||
/// @returns a new range mask containing the smallest _items elements (not ranges).
|
|
||||
RangeMask lowest(decltype(T{} - T{}) _items) const |
|
||||
{ |
|
||||
RangeMask ret(m_all); |
|
||||
for (auto i = m_ranges.begin(); i != m_ranges.end() && _items; ++i) |
|
||||
_items -= (ret.m_ranges[i->first] = std::min(i->first + _items, i->second)) - i->first; |
|
||||
return ret; |
|
||||
} |
|
||||
|
|
||||
/// @returns the complement of the range mask relative to the ground range.
|
|
||||
RangeMask operator~() const { return inverted(); } |
|
||||
|
|
||||
/// @returns a copy of this range mask representing the complement relative to the ground range.
|
|
||||
RangeMask inverted() const |
|
||||
{ |
|
||||
RangeMask ret(m_all); |
|
||||
T last = m_all.first; |
|
||||
for (auto i: m_ranges) |
|
||||
{ |
|
||||
if (i.first != last) |
|
||||
ret.m_ranges[last] = i.first; |
|
||||
last = i.second; |
|
||||
} |
|
||||
if (last != m_all.second) |
|
||||
ret.m_ranges[last] = m_all.second; |
|
||||
return ret; |
|
||||
} |
|
||||
|
|
||||
/// Changes the range mask to its complement relative to the ground range and returns a
|
|
||||
/// reference to itself.
|
|
||||
RangeMask& invert() { return *this = inverted(); } |
|
||||
|
|
||||
template <class S> RangeMask operator-(S const& _m) const { auto ret = *this; return ret -= _m; } |
|
||||
template <class S> RangeMask& operator-=(S const& _m) { return invert().unionWith(_m).invert(); } |
|
||||
|
|
||||
RangeMask& operator+=(RangeMask const& _m) { return unionWith(_m); } |
|
||||
|
|
||||
RangeMask& unionWith(RangeMask const& _m) |
|
||||
{ |
|
||||
m_all.first = std::min(_m.m_all.first, m_all.first); |
|
||||
m_all.second = std::max(_m.m_all.second, m_all.second); |
|
||||
for (auto const& i: _m.m_ranges) |
|
||||
unionWith(i); |
|
||||
return *this; |
|
||||
} |
|
||||
RangeMask& operator+=(Range const& _m) { return unionWith(_m); } |
|
||||
/// Modifies this range mask to also include the range _m, which has to be a subset of
|
|
||||
/// the ground range.
|
|
||||
RangeMask& unionWith(Range const& _m); |
|
||||
|
|
||||
/// Adds the single element _i to the range mask.
|
|
||||
RangeMask& operator+=(T _m) { return unionWith(_m); } |
|
||||
/// Adds the single element _i to the range mask.
|
|
||||
RangeMask& unionWith(T _i) |
|
||||
{ |
|
||||
return operator+=(Range(_i, _i + 1)); |
|
||||
} |
|
||||
|
|
||||
bool contains(T _i) const |
|
||||
{ |
|
||||
auto it = m_ranges.upper_bound(_i); |
|
||||
if (it == m_ranges.begin()) |
|
||||
return false; |
|
||||
return (--it)->second > _i; |
|
||||
} |
|
||||
|
|
||||
bool empty() const |
|
||||
{ |
|
||||
return m_ranges.empty(); |
|
||||
} |
|
||||
|
|
||||
bool full() const |
|
||||
{ |
|
||||
return m_all.first == m_all.second || (m_ranges.size() == 1 && m_ranges.begin()->first == m_all.first && m_ranges.begin()->second == m_all.second); |
|
||||
} |
|
||||
|
|
||||
void clear() |
|
||||
{ |
|
||||
m_ranges.clear(); |
|
||||
} |
|
||||
|
|
||||
void reset() |
|
||||
{ |
|
||||
m_ranges.clear(); |
|
||||
m_all = std::make_pair(0, 0); |
|
||||
} |
|
||||
|
|
||||
/// @returns the ground range.
|
|
||||
std::pair<T, T> const& all() const { return m_all; } |
|
||||
/// Extends the ground range to include _i.
|
|
||||
void extendAll(T _i) { m_all = std::make_pair(std::min(m_all.first, _i), std::max(m_all.second, _i + 1)); } |
|
||||
|
|
||||
class const_iterator: public std::iterator<std::forward_iterator_tag, T> |
|
||||
{ |
|
||||
friend class RangeMask; |
|
||||
|
|
||||
public: |
|
||||
const_iterator() {} |
|
||||
|
|
||||
T operator*() const { return m_value; } |
|
||||
const_iterator& operator++() { if (m_owner) m_value = m_owner->next(m_value); return *this; } |
|
||||
const_iterator operator++(int) { auto ret = *this; if (m_owner) m_value = m_owner->next(m_value); return ret; } |
|
||||
|
|
||||
bool operator==(const_iterator const& _i) const { return m_owner == _i.m_owner && m_value == _i.m_value; } |
|
||||
bool operator!=(const_iterator const& _i) const { return !operator==(_i); } |
|
||||
bool operator<(const_iterator const& _i) const { return m_value < _i.m_value; } |
|
||||
|
|
||||
private: |
|
||||
const_iterator(RangeMask const& _m, bool _end): m_owner(&_m), m_value(_m.m_ranges.empty() || _end ? _m.m_all.second : _m.m_ranges.begin()->first) {} |
|
||||
|
|
||||
RangeMask const* m_owner = nullptr; |
|
||||
T m_value = 0; |
|
||||
}; |
|
||||
|
|
||||
const_iterator begin() const { return const_iterator(*this, false); } |
|
||||
const_iterator end() const { return const_iterator(*this, true); } |
|
||||
/// @returns the smallest element in the range mask that is larger than _t or the end of the
|
|
||||
/// base range if such an element does not exist.
|
|
||||
T next(T _t) const |
|
||||
{ |
|
||||
_t++; |
|
||||
// find next item in range at least _t
|
|
||||
auto uit = m_ranges.upper_bound(_t); // > _t
|
|
||||
auto it = uit == m_ranges.begin() ? m_ranges.end() : std::prev(uit); |
|
||||
if (it != m_ranges.end() && it->first <= _t && it->second > _t) |
|
||||
return _t; |
|
||||
return uit == m_ranges.end() ? m_all.second : uit->first; |
|
||||
} |
|
||||
|
|
||||
/// @returns the number of elements (not ranges) in the range mask.
|
|
||||
size_t size() const |
|
||||
{ |
|
||||
size_t c = 0; |
|
||||
for (auto const& r: this->m_ranges) |
|
||||
c += r.second - r.first; |
|
||||
return c; |
|
||||
} |
|
||||
|
|
||||
size_t firstOut() const |
|
||||
{ |
|
||||
if (m_ranges.empty() || !m_ranges.count(m_all.first)) |
|
||||
return m_all.first; |
|
||||
return m_ranges.at(m_all.first); |
|
||||
} |
|
||||
|
|
||||
size_t lastIn() const |
|
||||
{ |
|
||||
if (m_ranges.empty()) |
|
||||
return m_all.first; |
|
||||
return m_ranges.rbegin()->second - 1; |
|
||||
} |
|
||||
|
|
||||
private: |
|
||||
/// The ground range.
|
|
||||
UnsignedRange m_all; |
|
||||
/// Mapping begin -> end containing the ranges.
|
|
||||
std::map<T, T> m_ranges; |
|
||||
}; |
|
||||
|
|
||||
template <class T> inline std::ostream& operator<<(std::ostream& _out, RangeMask<T> const& _r) |
|
||||
{ |
|
||||
_out << _r.m_all.first << "{ "; |
|
||||
for (auto const& i: _r.m_ranges) |
|
||||
_out << "[" << i.first << ", " << i.second << ") "; |
|
||||
_out << "}" << _r.m_all.second; |
|
||||
return _out; |
|
||||
} |
|
||||
|
|
||||
template <class T> |
|
||||
RangeMask<T>& RangeMask<T>::unionWith(typename RangeMask<T>::Range const& _m) |
|
||||
{ |
|
||||
for (auto i = _m.first; i < _m.second;) |
|
||||
{ |
|
||||
assert(i >= m_all.first); |
|
||||
assert(i < m_all.second); |
|
||||
// For each number, we find the element equal or next lower. this, if any, must contain the value.
|
|
||||
// First range that starts after i.
|
|
||||
auto rangeAfter = m_ranges.upper_bound(i); |
|
||||
// Range before rangeAfter or "end" if the rangeAfter is the first ever...
|
|
||||
auto it = rangeAfter == m_ranges.begin() ? m_ranges.end() : std::prev(rangeAfter); |
|
||||
if (it == m_ranges.end() || it->second < i) |
|
||||
{ |
|
||||
// i is either before the first range or between two ranges (with some distance
|
|
||||
// so that we cannot merge it onto "it").
|
|
||||
// lower range is too low to merge.
|
|
||||
// if the next higher range is too high.
|
|
||||
if (rangeAfter == m_ranges.end() || rangeAfter->first > _m.second) |
|
||||
{ |
|
||||
// just create a new range
|
|
||||
m_ranges[i] = _m.second; |
|
||||
break; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
if (rangeAfter->first == i) |
|
||||
// move i to end of range
|
|
||||
i = rangeAfter->second; |
|
||||
else |
|
||||
{ |
|
||||
// merge with the next higher range
|
|
||||
// move i to end of range
|
|
||||
i = m_ranges[i] = rangeAfter->second; |
|
||||
m_ranges.erase(rangeAfter); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
else if (it->second == i) |
|
||||
{ |
|
||||
// The range before i ends with i.
|
|
||||
// if the next higher range is too high.
|
|
||||
if (rangeAfter == m_ranges.end() || rangeAfter->first > _m.second) |
|
||||
{ |
|
||||
// merge with the next lower range
|
|
||||
m_ranges[it->first] = _m.second; |
|
||||
break; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// merge with both next lower & next higher.
|
|
||||
i = m_ranges[it->first] = rangeAfter->second; |
|
||||
m_ranges.erase(rangeAfter); |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
i = it->second; |
|
||||
} |
|
||||
return *this; |
|
||||
} |
|
||||
|
|
||||
} |
|
Loading…
Reference in new issue