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