mirror of
https://github.com/boostorg/histogram.git
synced 2025-05-11 13:14:06 +00:00
increase coverage
* provide unsafe access to buffer * factored mp_int out into separate header and renamed to large_int * refined adding behavior * more warnings and comeback of coverage variant * better coverage reporting
This commit is contained in:
parent
ed4935cd93
commit
6b386c9b51
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,3 +15,4 @@ CMakeSettings.json
|
||||
.vs*
|
||||
*.info
|
||||
tools/lcov-*
|
||||
coverage-report
|
||||
|
11
.travis.yml
11
.travis.yml
@ -65,26 +65,25 @@ matrix:
|
||||
../../b2 -j2 -q toolset=clang-5 cxxstd=14
|
||||
variant=histogram_ubasan warnings-as-errors=on
|
||||
|
||||
- name: "gcc-8: c++17"
|
||||
- name: "gcc-8: c++latest"
|
||||
addons:
|
||||
apt:
|
||||
sources: ubuntu-toolchain-r-test
|
||||
packages: g++-8
|
||||
script:
|
||||
- ../../b2 -j2 -q toolset=gcc-8 cxxstd=17 warnings-as-errors=on
|
||||
test examples
|
||||
- ../../b2 -j2 -q toolset=gcc-8 cxxstd=latest warnings-as-errors=on
|
||||
|
||||
before_script:
|
||||
# Cloning minimal set of Boost libraries
|
||||
- cd ..
|
||||
- git clone -b $BRANCH --depth 10 https://github.com/boostorg/boost.git
|
||||
- git clone -b $BRANCH --depth 5 https://github.com/boostorg/boost.git
|
||||
- cd boost
|
||||
- git submodule update --init --depth 10 tools/build tools/boostdep
|
||||
- git submodule update --init --depth 5 tools/build tools/boostdep
|
||||
|
||||
# Replacing Boost module with this project and installing Boost dependencies
|
||||
- rm -rf libs/$LIBRARY_DIR || true
|
||||
- mv $TRAVIS_BUILD_DIR libs/$LIBRARY_DIR
|
||||
- python tools/boostdep/depinst/depinst.py --git_args "--depth 10 --jobs 2" $LIBRARY_DIR
|
||||
- python tools/boostdep/depinst/depinst.py --git_args "--depth 5 --jobs 3" $LIBRARY_DIR
|
||||
|
||||
# Adding missing toolsets and preparing Boost headers
|
||||
- ./bootstrap.sh
|
||||
|
@ -109,33 +109,12 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
||||
#
|
||||
# install(TARGETS boost_histogram
|
||||
# boost_assert
|
||||
# boost_bind
|
||||
# boost_callable_traits
|
||||
# boost_concept_check
|
||||
# boost_config
|
||||
# boost_container_hash
|
||||
# boost_conversion
|
||||
# boost_core
|
||||
# boost_detail
|
||||
# boost_function_types
|
||||
# boost_fusion
|
||||
# boost_integer
|
||||
# boost_iterator
|
||||
# boost_move
|
||||
# boost_mp11
|
||||
# boost_mpl
|
||||
# boost_optional
|
||||
# boost_predef
|
||||
# boost_preprocessor
|
||||
# boost_smart_ptr
|
||||
# boost_static_assert
|
||||
# boost_throw_exception
|
||||
# boost_tuple
|
||||
# boost_type_index
|
||||
# boost_type_traits
|
||||
# boost_typeof
|
||||
# boost_utility
|
||||
# boost_callable_traits
|
||||
# boost_variant
|
||||
# boost_throw_exception
|
||||
# EXPORT ${PROJECT_NAME}Targets)
|
||||
# install(EXPORT ${PROJECT_NAME}Targets
|
||||
# DESTINATION ${CONFIG_INSTALL_DIR}
|
||||
|
11
Jamfile
11
Jamfile
@ -15,12 +15,19 @@ variant histogram_ubasan : debug :
|
||||
<linkflags>"-fsanitize=address,leak,undefined"
|
||||
;
|
||||
|
||||
# only works with gcc-5
|
||||
variant histogram_coverage : debug :
|
||||
<cxxstd>latest
|
||||
<cxxflags>"--coverage"
|
||||
<linkflags>"--coverage"
|
||||
;
|
||||
|
||||
project
|
||||
: requirements
|
||||
<implicit-dependency>/boost//headers
|
||||
<include>$(BOOST_ROOT)
|
||||
<toolset>clang:<cxxflags>"-pedantic -Wextra -fstrict-aliasing"
|
||||
<toolset>gcc:<cxxflags>"-pedantic -Wextra -fstrict-aliasing"
|
||||
<toolset>clang:<cxxflags>"-pedantic -Wextra -Wsign-compare -Wstrict-aliasing -fstrict-aliasing"
|
||||
<toolset>gcc:<cxxflags>"-pedantic -Wextra -Wsign-compare -Wstrict-aliasing -fstrict-aliasing"
|
||||
: default-build
|
||||
<warnings>all
|
||||
#<warnings-as-errors>on
|
||||
|
263
include/boost/histogram/detail/large_int.hpp
Normal file
263
include/boost/histogram/detail/large_int.hpp
Normal file
@ -0,0 +1,263 @@
|
||||
// Copyright 2018-2019 Hans Dembinski
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_HISTOGRAM_DETAIL_LARGE_INT_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_LARGE_INT_HPP
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/histogram/detail/static_if.hpp>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <bool B, class T>
|
||||
struct is_unsigned_integral_impl : std::false_type {};
|
||||
|
||||
template <class T>
|
||||
struct is_unsigned_integral_impl<true, T> : std::is_unsigned<T> {};
|
||||
|
||||
template <class T>
|
||||
using is_unsigned_integral = is_unsigned_integral_impl<std::is_integral<T>::value, T>;
|
||||
|
||||
template <class T>
|
||||
bool safe_increment(T& t) {
|
||||
if (t < std::numeric_limits<T>::max()) {
|
||||
++t;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool safe_radd(T& t, const U& u) {
|
||||
static_assert(is_unsigned_integral<T>::value, "T must be unsigned integral type");
|
||||
static_assert(is_unsigned_integral<U>::value, "T must be unsigned integral type");
|
||||
if (static_cast<T>(std::numeric_limits<T>::max() - t) >= u) {
|
||||
t += static_cast<T>(u); // static_cast to suppress conversion warning
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// An integer type which can grow arbitrarily large (until memory is exhausted).
|
||||
// Use boost.multiprecision.cpp_int in your own code, it is much more sophisticated.
|
||||
// We use it only to reduce coupling between boost libs.
|
||||
template <class Allocator>
|
||||
struct large_int {
|
||||
explicit large_int(const Allocator& a = {}) : data(1, 0, a) {}
|
||||
explicit large_int(std::uint64_t v, const Allocator& a = {}) : data(1, v, a) {}
|
||||
|
||||
large_int(const large_int&) = default;
|
||||
large_int& operator=(const large_int&) = default;
|
||||
large_int(large_int&&) = default;
|
||||
large_int& operator=(large_int&&) = default;
|
||||
|
||||
large_int& operator=(std::uint64_t o) {
|
||||
data = decltype(data)(1, o);
|
||||
return *this;
|
||||
}
|
||||
|
||||
large_int& operator++() {
|
||||
BOOST_ASSERT(data.size() > 0u);
|
||||
std::size_t i = 0;
|
||||
while (!safe_increment(data[i])) {
|
||||
data[i] = 0;
|
||||
++i;
|
||||
if (i == data.size()) {
|
||||
data.push_back(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
large_int& operator+=(const large_int& o) {
|
||||
if (this == &o) {
|
||||
auto tmp{o};
|
||||
return operator+=(tmp);
|
||||
}
|
||||
bool carry = false;
|
||||
std::size_t i = 0;
|
||||
for (std::uint64_t oi : o.data) {
|
||||
auto& di = maybe_extend(i);
|
||||
if (carry) {
|
||||
if (safe_increment(oi))
|
||||
carry = false;
|
||||
else {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!safe_radd(di, oi)) {
|
||||
add_remainder(di, oi);
|
||||
carry = true;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
while (carry) {
|
||||
auto& di = maybe_extend(i);
|
||||
if (safe_increment(di)) break;
|
||||
di = 0;
|
||||
++i;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
large_int& operator+=(std::uint64_t o) {
|
||||
BOOST_ASSERT(data.size() > 0u);
|
||||
if (safe_radd(data[0], o)) return *this;
|
||||
add_remainder(data[0], o);
|
||||
// carry the one, data may grow several times
|
||||
std::size_t i = 1;
|
||||
while (true) {
|
||||
auto& di = maybe_extend(i);
|
||||
if (safe_increment(di)) break;
|
||||
di = 0;
|
||||
++i;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator double() const noexcept {
|
||||
BOOST_ASSERT(data.size() > 0u);
|
||||
double result = static_cast<double>(data[0]);
|
||||
std::size_t i = 0;
|
||||
while (++i < data.size())
|
||||
result += static_cast<double>(data[i]) * std::pow(2.0, i * 64);
|
||||
return result;
|
||||
}
|
||||
|
||||
// total ordering for large_int, large_int
|
||||
bool operator<(const large_int& o) const noexcept {
|
||||
BOOST_ASSERT(data.size() > 0u);
|
||||
BOOST_ASSERT(o.data.size() > 0u);
|
||||
// no leading zeros allowed
|
||||
BOOST_ASSERT(data.size() == 1 || data.back() > 0u);
|
||||
BOOST_ASSERT(o.data.size() == 1 || o.data.back() > 0u);
|
||||
if (data.size() < o.data.size()) return true;
|
||||
if (data.size() > o.data.size()) return false;
|
||||
auto s = data.size();
|
||||
while (s > 0u) {
|
||||
--s;
|
||||
if (data[s] < o.data[s]) return true;
|
||||
if (data[s] > o.data[s]) return false;
|
||||
}
|
||||
return false; // args are equal
|
||||
}
|
||||
|
||||
bool operator==(const large_int& o) const noexcept {
|
||||
BOOST_ASSERT(data.size() > 0u);
|
||||
BOOST_ASSERT(o.data.size() > 0u);
|
||||
// no leading zeros allowed
|
||||
BOOST_ASSERT(data.size() == 1 || data.back() > 0u);
|
||||
BOOST_ASSERT(o.data.size() == 1 || o.data.back() > 0u);
|
||||
if (data.size() != o.data.size()) return false;
|
||||
return std::equal(data.begin(), data.end(), o.data.begin());
|
||||
}
|
||||
|
||||
// copied from boost/operators.hpp
|
||||
friend bool operator>(const large_int& x, const large_int& y) { return y < x; }
|
||||
friend bool operator<=(const large_int& x, const large_int& y) { return !(y < x); }
|
||||
friend bool operator>=(const large_int& x, const large_int& y) { return !(x < y); }
|
||||
friend bool operator!=(const large_int& x, const large_int& y) { return !(x == y); }
|
||||
|
||||
// total ordering for large_int, uint64; partial ordering for large_int, double
|
||||
template <class U>
|
||||
bool operator<(const U& o) const noexcept {
|
||||
BOOST_ASSERT(data.size() > 0u);
|
||||
return static_if<is_unsigned_integral<U>>(
|
||||
[this](std::uint64_t o) { return data.size() == 1 && data[0] < o; },
|
||||
[this](double o) { return operator double() < o; }, o);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
bool operator>(const U& o) const noexcept {
|
||||
BOOST_ASSERT(data.size() > 0u);
|
||||
BOOST_ASSERT(data.size() == 1 || data.back() > 0u); // no leading zeros allowed
|
||||
return static_if<is_unsigned_integral<U>>(
|
||||
[this](std::uint64_t o) { return data.size() > 1 || data[0] > o; },
|
||||
[this](double o) { return operator double() > o; }, o);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
bool operator==(const U& o) const noexcept {
|
||||
BOOST_ASSERT(data.size() > 0u);
|
||||
return static_if<is_unsigned_integral<U>>(
|
||||
[this](std::uint64_t o) { return data.size() == 1 && data[0] == o; },
|
||||
[this](double o) { return operator double() == o; }, o);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
bool operator!=(const U& x) const noexcept {
|
||||
return !operator==(x);
|
||||
}
|
||||
|
||||
// adapted copy from boost/operators.hpp
|
||||
template <class U>
|
||||
friend bool operator<=(const large_int& x, const U& y) {
|
||||
if (is_unsigned_integral<U>::value) return !(x > y);
|
||||
return (x < y) || (x == y);
|
||||
}
|
||||
template <class U>
|
||||
friend bool operator>=(const large_int& x, const U& y) {
|
||||
if (is_unsigned_integral<U>::value) return !(x < y);
|
||||
return (x > y) || (x == y);
|
||||
}
|
||||
template <class U>
|
||||
friend bool operator>(const U& x, const large_int& y) {
|
||||
return y.operator<(x);
|
||||
}
|
||||
template <class U>
|
||||
friend bool operator<(const U& x, const large_int& y) {
|
||||
return y.operator>(x);
|
||||
}
|
||||
template <class U>
|
||||
friend bool operator<=(const U& x, const large_int& y) {
|
||||
return y >= x;
|
||||
}
|
||||
template <class U>
|
||||
friend bool operator>=(const U& x, const large_int& y) {
|
||||
return y <= x;
|
||||
}
|
||||
template <class U>
|
||||
friend bool operator==(const U& y, const large_int& x) {
|
||||
return x.operator==(y);
|
||||
}
|
||||
template <class U>
|
||||
friend bool operator!=(const U& y, const large_int& x) {
|
||||
return x.operator!=(y);
|
||||
}
|
||||
|
||||
std::uint64_t& maybe_extend(std::size_t i) {
|
||||
while (i >= data.size()) data.push_back(0);
|
||||
return data[i];
|
||||
}
|
||||
|
||||
static void add_remainder(std::uint64_t& d, const std::uint64_t o) noexcept {
|
||||
BOOST_ASSERT(d > 0u);
|
||||
// in decimal system it would look like this:
|
||||
// 8 + 8 = 6 = 8 - (9 - 8) - 1
|
||||
// 9 + 1 = 0 = 9 - (9 - 1) - 1
|
||||
auto tmp = std::numeric_limits<std::uint64_t>::max();
|
||||
tmp -= o;
|
||||
--d -= tmp;
|
||||
}
|
||||
|
||||
std::vector<std::uint64_t, Allocator> data;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -301,6 +301,9 @@ struct requires_axes {};
|
||||
template <class T, class U, class = std::enable_if_t<std::is_convertible<T, U>::value>>
|
||||
struct requires_convertible {};
|
||||
|
||||
template <class T, class = std::enable_if_t<std::is_arithmetic<T>::value>>
|
||||
struct requires_arithmetic {};
|
||||
|
||||
template <class T>
|
||||
auto make_default(const T& t) {
|
||||
return static_if<has_allocator<T>>([](const auto& t) { return T(t.get_allocator()); },
|
||||
@ -395,21 +398,6 @@ R get_scale(const T& t) {
|
||||
template <class T, class Default>
|
||||
using replace_default = mp11::mp_if<std::is_same<T, use_default>, Default, T>;
|
||||
|
||||
template <class T>
|
||||
auto make_unsigned_impl(std::true_type, const T t) noexcept {
|
||||
return static_cast<typename std::make_unsigned<T>::type>(t);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto make_unsigned_impl(std::false_type, const T t) noexcept {
|
||||
return t;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto make_unsigned(const T t) noexcept {
|
||||
return make_unsigned_impl(std::is_integral<T>{}, t);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
@ -165,7 +165,7 @@ void serialize(Archive& ar, map_impl<T>& impl, unsigned /* version */) {
|
||||
}
|
||||
|
||||
template <class Archive, class Allocator>
|
||||
void serialize(Archive& ar, mp_int<Allocator>& x, unsigned /* version */) {
|
||||
void serialize(Archive& ar, large_int<Allocator>& x, unsigned /* version */) {
|
||||
ar& serialization::make_nvp("data", x.data);
|
||||
}
|
||||
} // namespace detail
|
||||
@ -176,15 +176,16 @@ void serialize(Archive& ar, storage_adaptor<T>& x, unsigned /* version */) {
|
||||
ar& serialization::make_nvp("impl", static_cast<impl_t&>(x));
|
||||
}
|
||||
|
||||
template <class A>
|
||||
template <class Archive>
|
||||
void unlimited_storage<A>::serialize(Archive& ar, unsigned /* version */) {
|
||||
template <class Allocator, class Archive>
|
||||
void serialize(Archive& ar, unlimited_storage<Allocator>& s, unsigned /* version */) {
|
||||
auto& buffer = unsafe_access::unlimited_storage_buffer(s);
|
||||
using buffer_t = std::remove_reference_t<decltype(buffer)>;
|
||||
if (Archive::is_loading::value) {
|
||||
buffer_type dummy(buffer.alloc);
|
||||
buffer_t helper(buffer.alloc);
|
||||
std::size_t size;
|
||||
ar& serialization::make_nvp("type", dummy.type);
|
||||
ar& serialization::make_nvp("type", helper.type);
|
||||
ar& serialization::make_nvp("size", size);
|
||||
dummy.apply([this, size](auto* tp) {
|
||||
helper.visit([&buffer, size](auto* tp) {
|
||||
BOOST_ASSERT(tp == nullptr);
|
||||
using T = detail::remove_cvref_t<decltype(*tp)>;
|
||||
buffer.template make<T>(size);
|
||||
@ -193,7 +194,7 @@ void unlimited_storage<A>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& serialization::make_nvp("type", buffer.type);
|
||||
ar& serialization::make_nvp("size", buffer.size);
|
||||
}
|
||||
buffer.apply([this, &ar](auto* tp) {
|
||||
buffer.visit([&buffer, &ar](auto* tp) {
|
||||
using T = detail::remove_cvref_t<decltype(*tp)>;
|
||||
ar& serialization::make_nvp(
|
||||
"buffer",
|
||||
|
@ -24,24 +24,23 @@ namespace detail {
|
||||
|
||||
template <class T>
|
||||
struct vector_impl : T {
|
||||
using allocator_type = typename T::allocator_type;
|
||||
|
||||
static constexpr bool has_threading_support =
|
||||
accumulators::is_thread_safe<typename T::value_type>::value;
|
||||
|
||||
vector_impl() = default;
|
||||
vector_impl(const vector_impl& t) : T(t) {}
|
||||
vector_impl& operator=(const vector_impl& t) {
|
||||
T::operator=(t);
|
||||
return *this;
|
||||
}
|
||||
vector_impl(const allocator_type& a = {}) : T(a) {}
|
||||
vector_impl(const vector_impl&) = default;
|
||||
vector_impl& operator=(const vector_impl&) = default;
|
||||
vector_impl(vector_impl&&) = default;
|
||||
vector_impl& operator=(vector_impl&&) = default;
|
||||
|
||||
explicit vector_impl(typename T::allocator_type a) : T(a) {}
|
||||
explicit vector_impl(T&& t) : T(std::move(t)) {}
|
||||
explicit vector_impl(const T& t) : T(t) {}
|
||||
|
||||
template <class U, class = requires_iterable<U>>
|
||||
explicit vector_impl(const U& u) {
|
||||
T::reserve(u.size());
|
||||
for (auto&& x : u) T::emplace_back(x);
|
||||
}
|
||||
explicit vector_impl(const U& u, const allocator_type& a = {})
|
||||
: T(std::begin(u), std::end(u), a) {}
|
||||
|
||||
template <class U, class = requires_iterable<U>>
|
||||
vector_impl& operator=(const U& u) {
|
||||
@ -57,7 +56,7 @@ struct vector_impl : T {
|
||||
T::resize(n, value_type());
|
||||
std::fill_n(T::begin(), std::min(n, old_size), value_type());
|
||||
}
|
||||
};
|
||||
}; // namespace detail
|
||||
|
||||
template <class T>
|
||||
struct array_impl : T {
|
||||
@ -72,11 +71,14 @@ struct array_impl : T {
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit array_impl(T&& t) : T(std::move(t)) {}
|
||||
explicit array_impl(const T& t) : T(t) {}
|
||||
|
||||
template <class U, class = requires_iterable<U>>
|
||||
explicit array_impl(const U& u) : size_(u.size()) {
|
||||
std::size_t i = 0;
|
||||
for (auto&& x : u) T::operator[](i++) = x;
|
||||
using std::begin;
|
||||
using std::end;
|
||||
std::copy(begin(u), end(u), this->begin());
|
||||
}
|
||||
|
||||
template <class U, class = requires_iterable<U>>
|
||||
@ -124,6 +126,7 @@ struct map_impl : T {
|
||||
|
||||
struct reference {
|
||||
reference(map_impl* m, std::size_t i) noexcept : map(m), idx(i) {}
|
||||
|
||||
reference(const reference&) noexcept = default;
|
||||
reference& operator=(const reference& o) {
|
||||
if (this != &o) operator=(static_cast<const_reference>(o));
|
||||
@ -259,19 +262,20 @@ struct map_impl : T {
|
||||
using iterator = iterator_t<value_type, reference, map_impl*>;
|
||||
using const_iterator = iterator_t<const value_type, const_reference, const map_impl*>;
|
||||
|
||||
map_impl() = default;
|
||||
map_impl(const map_impl& t) : T(t), size_(t.size_) {}
|
||||
map_impl& operator=(const map_impl& t) {
|
||||
T::operator=(t);
|
||||
size_ = t.size_;
|
||||
return *this;
|
||||
}
|
||||
using allocator_type = typename T::allocator_type;
|
||||
|
||||
explicit map_impl(const T& t) : T(t) {}
|
||||
explicit map_impl(typename T::allocator_type a) : T(a) {}
|
||||
map_impl(const allocator_type& a = {}) : T(a) {}
|
||||
|
||||
map_impl(const map_impl&) = default;
|
||||
map_impl& operator=(const map_impl&) = default;
|
||||
map_impl(map_impl&&) = default;
|
||||
map_impl& operator=(map_impl&&) = default;
|
||||
|
||||
map_impl(const T& t) : T(t), size_(t.size()) {}
|
||||
map_impl(T&& t) : T(std::move(t)), size_(t.size()) {}
|
||||
|
||||
template <class U, class = requires_iterable<U>>
|
||||
explicit map_impl(const U& u) : size_(u.size()) {
|
||||
explicit map_impl(const U& u, const allocator_type& a = {}) : T(a), size_(u.size()) {
|
||||
using std::begin;
|
||||
using std::end;
|
||||
std::copy(begin(u), end(u), this->begin());
|
||||
@ -334,26 +338,20 @@ class storage_adaptor : public detail::storage_adaptor_impl<T> {
|
||||
public:
|
||||
using base_type = detail::storage_adaptor_impl<T>;
|
||||
|
||||
// standard default, copy, move, assign
|
||||
storage_adaptor() = default;
|
||||
// standard copy, move, assign
|
||||
storage_adaptor(storage_adaptor&&) = default;
|
||||
storage_adaptor(const storage_adaptor&) = default;
|
||||
storage_adaptor& operator=(storage_adaptor&&) = default;
|
||||
storage_adaptor& operator=(const storage_adaptor&) = default;
|
||||
|
||||
// allow move from adapted object
|
||||
storage_adaptor(T&& t) : base_type(std::move(t)) {}
|
||||
storage_adaptor& operator=(T&& t) {
|
||||
base_type::operator=(std::move(t));
|
||||
return *this;
|
||||
}
|
||||
// forwarding constructor
|
||||
template <class... Ts>
|
||||
storage_adaptor(Ts&&... ts) : base_type(std::forward<Ts>(ts)...) {}
|
||||
|
||||
// conversion ctor and assign
|
||||
// forwarding assign
|
||||
template <class U>
|
||||
storage_adaptor(const U& u) : base_type(u) {}
|
||||
template <class U>
|
||||
storage_adaptor& operator=(const U& u) {
|
||||
base_type::operator=(u);
|
||||
storage_adaptor& operator=(U&& u) {
|
||||
base_type::operator=(std::forward<U>(u));
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -13,7 +13,14 @@
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
|
||||
/// Unsafe read/write access to classes that potentially break consistency
|
||||
/** Unsafe read/write access to private data that potentially breaks consistency.
|
||||
|
||||
This struct enables access to private data of some classes. It is intended for library
|
||||
developers who need this to implement algorithms which require this, for example,
|
||||
serialization. Users should not use this. If you are a user who relies on
|
||||
unsafe_access to get a specific effect, please submit an issue on Github. In this case,
|
||||
the interface should be extended.
|
||||
*/
|
||||
struct unsafe_access {
|
||||
/**
|
||||
Get axes.
|
||||
@ -66,6 +73,15 @@ struct unsafe_access {
|
||||
static const auto& storage(const Histogram& hist) {
|
||||
return hist.storage_and_mutex_.first();
|
||||
}
|
||||
|
||||
/**
|
||||
Get buffer of unlimited_storage.
|
||||
@param storage instance of unlimited_storage.
|
||||
*/
|
||||
template <class Allocator>
|
||||
static constexpr auto& unlimited_storage_buffer(unlimited_storage<Allocator>& storage) {
|
||||
return storage.buffer_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace histogram
|
||||
|
@ -29,6 +29,8 @@ boost_test(TYPE run SOURCES detail_misc_test.cpp
|
||||
LIBRARIES Boost::histogram Boost::core)
|
||||
boost_test(TYPE run SOURCES detail_meta_test.cpp
|
||||
LIBRARIES Boost::histogram Boost::core)
|
||||
boost_test(TYPE run SOURCES detail_large_int_test.cpp
|
||||
LIBRARIES Boost::histogram Boost::core)
|
||||
boost_test(TYPE run SOURCES detail_linearize_test.cpp
|
||||
LIBRARIES Boost::histogram Boost::core)
|
||||
boost_test(TYPE run SOURCES detail_iterator_adaptor_test.cpp
|
||||
|
@ -48,6 +48,7 @@ alias cxx14 :
|
||||
[ run detail_meta_test.cpp ]
|
||||
[ run detail_misc_test.cpp ]
|
||||
[ run detail_iterator_adaptor_test.cpp ]
|
||||
[ run detail_large_int_test.cpp ]
|
||||
[ run detail_linearize_test.cpp ]
|
||||
[ run histogram_dynamic_test.cpp ]
|
||||
[ run histogram_growing_test.cpp ]
|
||||
|
158
test/detail_large_int_test.cpp
Normal file
158
test/detail_large_int_test.cpp
Normal file
@ -0,0 +1,158 @@
|
||||
// Copyright 2018-2019 Hans Dembinski
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/histogram/detail/large_int.hpp>
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
#include <limits>
|
||||
#include "utility_meta.hpp"
|
||||
|
||||
using namespace boost::histogram;
|
||||
|
||||
using large_int = detail::large_int<std::allocator<std::uint64_t>>;
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const large_int& x) {
|
||||
os << "large_int" << x.data;
|
||||
return os;
|
||||
}
|
||||
|
||||
template <class... Ts>
|
||||
auto make_mp_int(Ts... ts) {
|
||||
large_int r;
|
||||
r.data = {static_cast<uint64_t>(ts)...};
|
||||
return r;
|
||||
}
|
||||
|
||||
int main() {
|
||||
// low-level tools
|
||||
{
|
||||
uint8_t c = 0;
|
||||
BOOST_TEST_EQ(detail::safe_increment(c), true);
|
||||
BOOST_TEST_EQ(c, 1);
|
||||
c = 255;
|
||||
BOOST_TEST_EQ(detail::safe_increment(c), false);
|
||||
BOOST_TEST_EQ(c, 255);
|
||||
c = 0;
|
||||
BOOST_TEST_EQ(detail::safe_radd(c, 255u), true);
|
||||
BOOST_TEST_EQ(c, 255);
|
||||
c = 1;
|
||||
BOOST_TEST_EQ(detail::safe_radd(c, 255u), false);
|
||||
BOOST_TEST_EQ(c, 1);
|
||||
c = 255;
|
||||
BOOST_TEST_EQ(detail::safe_radd(c, 1u), false);
|
||||
BOOST_TEST_EQ(c, 255);
|
||||
}
|
||||
|
||||
const auto vmax = std::numeric_limits<std::uint64_t>::max();
|
||||
|
||||
BOOST_TEST_EQ(large_int(), 0u);
|
||||
BOOST_TEST_EQ(large_int(1u), 1u);
|
||||
BOOST_TEST_EQ(large_int(1u), 1.0);
|
||||
BOOST_TEST_EQ(large_int(1u), large_int(1u));
|
||||
BOOST_TEST_NE(large_int(1u), 2u);
|
||||
BOOST_TEST_NE(large_int(1u), 2.0);
|
||||
BOOST_TEST_NE(large_int(1u), large_int(2u));
|
||||
BOOST_TEST_LT(large_int(1u), 2u);
|
||||
BOOST_TEST_LT(large_int(1u), 2.0);
|
||||
BOOST_TEST_LT(large_int(1u), large_int(2u));
|
||||
BOOST_TEST_LE(large_int(1u), 2u);
|
||||
BOOST_TEST_LE(large_int(1u), 2.0);
|
||||
BOOST_TEST_LE(large_int(1u), large_int(2u));
|
||||
BOOST_TEST_LE(large_int(1u), 1u);
|
||||
BOOST_TEST_GT(large_int(1u), 0u);
|
||||
BOOST_TEST_GT(large_int(1u), 0.0);
|
||||
BOOST_TEST_GT(large_int(1u), large_int(0u));
|
||||
BOOST_TEST_GE(large_int(1u), 0u);
|
||||
BOOST_TEST_GE(large_int(1u), 0.0);
|
||||
BOOST_TEST_GE(large_int(1u), 1u);
|
||||
BOOST_TEST_GE(large_int(1u), large_int(0u));
|
||||
BOOST_TEST_NOT(large_int(1u) < large_int(1u));
|
||||
BOOST_TEST_NOT(large_int(1u) > large_int(1u));
|
||||
BOOST_TEST_GT(1, large_int());
|
||||
BOOST_TEST_LT(-1, large_int());
|
||||
BOOST_TEST_GE(1, large_int());
|
||||
BOOST_TEST_LE(-1, large_int());
|
||||
BOOST_TEST_NE(1, large_int());
|
||||
|
||||
auto a = large_int();
|
||||
++a;
|
||||
BOOST_TEST_EQ(a.data.size(), 1);
|
||||
BOOST_TEST_EQ(a.data[0], 1);
|
||||
++a;
|
||||
BOOST_TEST_EQ(a.data[0], 2);
|
||||
a = vmax;
|
||||
BOOST_TEST_EQ(a, vmax);
|
||||
BOOST_TEST_EQ(a, static_cast<double>(vmax));
|
||||
++a;
|
||||
BOOST_TEST_EQ(a, make_mp_int(0, 1));
|
||||
++a;
|
||||
BOOST_TEST_EQ(a, make_mp_int(1, 1));
|
||||
a += a;
|
||||
BOOST_TEST_EQ(a, make_mp_int(2, 2));
|
||||
BOOST_TEST_EQ(a, 2 * static_cast<double>(vmax) + 2);
|
||||
|
||||
// carry once A
|
||||
a.data[0] = vmax;
|
||||
a.data[1] = 1;
|
||||
++a;
|
||||
BOOST_TEST_EQ(a, make_mp_int(0, 2));
|
||||
// carry once B
|
||||
a.data[0] = vmax;
|
||||
a.data[1] = 1;
|
||||
a += 1;
|
||||
BOOST_TEST_EQ(a, make_mp_int(0, 2));
|
||||
// carry once C
|
||||
a.data[0] = vmax;
|
||||
a.data[1] = 1;
|
||||
a += make_mp_int(1, 1);
|
||||
BOOST_TEST_EQ(a, make_mp_int(0, 3));
|
||||
|
||||
a.data[0] = vmax - 1;
|
||||
a.data[1] = vmax;
|
||||
++a;
|
||||
BOOST_TEST_EQ(a, make_mp_int(vmax, vmax));
|
||||
|
||||
// carry two times A
|
||||
++a;
|
||||
BOOST_TEST_EQ(a, make_mp_int(0, 0, 1));
|
||||
// carry two times B
|
||||
a = make_mp_int(vmax, vmax);
|
||||
a += 1;
|
||||
BOOST_TEST_EQ(a, make_mp_int(0, 0, 1));
|
||||
// carry two times C
|
||||
a = make_mp_int(vmax, vmax);
|
||||
a += large_int(1);
|
||||
BOOST_TEST_EQ(a, make_mp_int(0, 0, 1));
|
||||
|
||||
// carry and enlarge
|
||||
a = make_mp_int(vmax, vmax);
|
||||
a += a;
|
||||
BOOST_TEST_EQ(a, make_mp_int(vmax - 1, vmax, 1));
|
||||
|
||||
// add smaller to larger
|
||||
a = make_mp_int(1, 1, 1);
|
||||
a += make_mp_int(1, 1);
|
||||
BOOST_TEST_EQ(a, make_mp_int(2, 2, 1));
|
||||
|
||||
// add larger to smaller
|
||||
a = make_mp_int(1, 1);
|
||||
a += make_mp_int(1, 1, 1);
|
||||
BOOST_TEST_EQ(a, make_mp_int(2, 2, 1));
|
||||
|
||||
a = large_int(1);
|
||||
auto b = 1.0;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
for (unsigned i = 0; i < 80; ++i) {
|
||||
b += b;
|
||||
BOOST_TEST_NE(a, b);
|
||||
a += a;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
}
|
||||
BOOST_TEST_GT(a.data.size(), 1u);
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
@ -31,31 +31,39 @@ auto str(const T& t) {
|
||||
|
||||
template <typename T>
|
||||
void tests() {
|
||||
using Storage = storage_adaptor<T>;
|
||||
// ctor, copy, move
|
||||
{
|
||||
storage_adaptor<T> a;
|
||||
Storage a;
|
||||
a.reset(2);
|
||||
storage_adaptor<T> b(a);
|
||||
storage_adaptor<T> c;
|
||||
Storage b(a);
|
||||
Storage c;
|
||||
c = a;
|
||||
BOOST_TEST_EQ(std::distance(a.begin(), a.end()), 2);
|
||||
BOOST_TEST_EQ(a.size(), 2);
|
||||
BOOST_TEST_EQ(b.size(), 2);
|
||||
BOOST_TEST_EQ(c.size(), 2);
|
||||
|
||||
storage_adaptor<T> d(std::move(a));
|
||||
Storage d(std::move(a));
|
||||
BOOST_TEST_EQ(d.size(), 2);
|
||||
storage_adaptor<T> e;
|
||||
Storage e;
|
||||
e = std::move(d);
|
||||
BOOST_TEST_EQ(e.size(), 2);
|
||||
|
||||
const auto t = T();
|
||||
storage_adaptor<T> g(t); // tests converting ctor
|
||||
BOOST_TEST_EQ(g.size(), 0);
|
||||
const auto u = std::vector<typename Storage::value_type>(3, 1);
|
||||
Storage h(u); // tests converting ctor
|
||||
BOOST_TEST_EQ(h.size(), 3);
|
||||
BOOST_TEST_EQ(h[0], 1);
|
||||
BOOST_TEST_EQ(h[1], 1);
|
||||
BOOST_TEST_EQ(h[2], 1);
|
||||
}
|
||||
|
||||
// increment, add, sub, set, reset, compare
|
||||
{
|
||||
storage_adaptor<T> a;
|
||||
Storage a;
|
||||
a.reset(1);
|
||||
++a[0];
|
||||
const auto save = a[0]++;
|
||||
@ -88,10 +96,10 @@ void tests() {
|
||||
|
||||
// copy
|
||||
{
|
||||
storage_adaptor<T> a;
|
||||
Storage a;
|
||||
a.reset(1);
|
||||
++a[0];
|
||||
decltype(a) b;
|
||||
Storage b;
|
||||
b.reset(2);
|
||||
BOOST_TEST(!(a == b));
|
||||
b = a;
|
||||
@ -99,7 +107,7 @@ void tests() {
|
||||
BOOST_TEST_EQ(b.size(), 1);
|
||||
BOOST_TEST_EQ(b[0], 1);
|
||||
|
||||
decltype(a) c(a);
|
||||
Storage c(a);
|
||||
BOOST_TEST(a == c);
|
||||
BOOST_TEST_EQ(c.size(), 1);
|
||||
BOOST_TEST_EQ(c[0], 1);
|
||||
@ -107,21 +115,21 @@ void tests() {
|
||||
|
||||
// move
|
||||
{
|
||||
storage_adaptor<T> a;
|
||||
Storage a;
|
||||
a.reset(1);
|
||||
++a[0];
|
||||
decltype(a) b;
|
||||
Storage b;
|
||||
BOOST_TEST(!(a == b));
|
||||
b = std::move(a);
|
||||
BOOST_TEST_EQ(b.size(), 1);
|
||||
BOOST_TEST_EQ(b[0], 1);
|
||||
decltype(a) c(std::move(b));
|
||||
Storage c(std::move(b));
|
||||
BOOST_TEST_EQ(c.size(), 1);
|
||||
BOOST_TEST_EQ(c[0], 1);
|
||||
}
|
||||
|
||||
{
|
||||
storage_adaptor<T> a;
|
||||
Storage a;
|
||||
a.reset(1);
|
||||
a[0] += 2;
|
||||
BOOST_TEST_EQ(str(a[0]), "2"s);
|
||||
@ -242,10 +250,10 @@ int main() {
|
||||
{
|
||||
tracing_allocator_db db;
|
||||
tracing_allocator<char> alloc(db);
|
||||
using map = std::map<std::size_t, double, std::less<std::size_t>,
|
||||
tracing_allocator<std::pair<const std::size_t, double>>>;
|
||||
using A = storage_adaptor<map>;
|
||||
auto a = A(map(alloc));
|
||||
using map_t = std::map<std::size_t, double, std::less<std::size_t>,
|
||||
tracing_allocator<std::pair<const std::size_t, double>>>;
|
||||
using A = storage_adaptor<map_t>;
|
||||
auto a = A(alloc);
|
||||
// MSVC implementation allocates some structures for debugging
|
||||
const auto baseline = db.sum.first;
|
||||
a.reset(10);
|
||||
|
@ -56,7 +56,7 @@ int main() {
|
||||
serialization_impl<uint16_t>();
|
||||
serialization_impl<uint32_t>();
|
||||
serialization_impl<uint64_t>();
|
||||
serialization_impl<unlimited_storage_type::mp_int>();
|
||||
serialization_impl<unlimited_storage_type::large_int>();
|
||||
serialization_impl<double>();
|
||||
}
|
||||
|
||||
|
@ -9,41 +9,31 @@
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <boost/histogram/storage_adaptor.hpp>
|
||||
#include <boost/histogram/unlimited_storage.hpp>
|
||||
#include <boost/histogram/unsafe_access.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
#include <iosfwd>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include "utility_allocator.hpp"
|
||||
#include "utility_meta.hpp"
|
||||
|
||||
namespace bh = boost::histogram;
|
||||
using unlimited_storage_type = bh::unlimited_storage<>;
|
||||
template <typename T>
|
||||
using vector_storage = bh::storage_adaptor<std::vector<T>>;
|
||||
using mp_int = unlimited_storage_type::mp_int;
|
||||
using namespace boost::histogram;
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const mp_int& x) {
|
||||
os << "mp_int";
|
||||
using unlimited_storage_type = unlimited_storage<>;
|
||||
template <typename T>
|
||||
using vector_storage = storage_adaptor<std::vector<T>>;
|
||||
using large_int = unlimited_storage_type::large_int;
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const large_int& x) {
|
||||
os << "large_int";
|
||||
os << x.data;
|
||||
return os;
|
||||
}
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
std::ostream& operator<<(std::ostream& os, unlimited_storage_type::iterator it) {
|
||||
os << "iterator[" << it.buffer_ << ", " << it.base() << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, unlimited_storage_type::const_iterator it) {
|
||||
os << "const_iterator[" << it.buffer_ << ", " << it.base() << "]";
|
||||
return os;
|
||||
}
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
template <typename T = std::uint8_t>
|
||||
unlimited_storage_type prepare(std::size_t n, T x = T()) {
|
||||
unlimited_storage_type prepare(std::size_t n, T x = T{}) {
|
||||
std::unique_ptr<T[]> v(new T[n]);
|
||||
std::fill(v.get(), v.get() + n, static_cast<T>(0));
|
||||
v.get()[0] = x;
|
||||
@ -56,15 +46,8 @@ auto max() {
|
||||
}
|
||||
|
||||
template <>
|
||||
inline auto max<mp_int>() {
|
||||
return mp_int(std::numeric_limits<uint64_t>::max());
|
||||
}
|
||||
|
||||
template <class... Ts>
|
||||
auto make_mp_int(Ts... ts) {
|
||||
mp_int r;
|
||||
r.data = {static_cast<uint64_t>(ts)...};
|
||||
return r;
|
||||
inline auto max<large_int>() {
|
||||
return large_int(std::numeric_limits<uint64_t>::max());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@ -106,7 +89,7 @@ void equal_2() {
|
||||
|
||||
template <typename T>
|
||||
void increase_and_grow() {
|
||||
auto tmax = std::numeric_limits<T>::max();
|
||||
auto tmax = max<T>();
|
||||
auto s = prepare(2, tmax);
|
||||
auto n = s;
|
||||
auto n2 = s;
|
||||
@ -188,58 +171,68 @@ void convert_array_storage() {
|
||||
BOOST_TEST_EQ(h[0], 0);
|
||||
}
|
||||
|
||||
template <typename LHS, typename RHS>
|
||||
void add() {
|
||||
auto a = prepare<LHS>(1);
|
||||
auto b = prepare<RHS>(1);
|
||||
b[0] += 2;
|
||||
a[0] += b[0];
|
||||
BOOST_TEST_EQ(a[0], 2);
|
||||
a[0] -= b[0];
|
||||
BOOST_TEST_EQ(a[0], 0);
|
||||
a[0] -= b[0];
|
||||
BOOST_TEST_EQ(a[0], -2);
|
||||
|
||||
auto c = prepare<RHS>(1);
|
||||
c[0] = max<RHS>();
|
||||
auto d = prepare<LHS>(1);
|
||||
d[0] += c[0];
|
||||
BOOST_TEST_EQ(d[0], max<RHS>());
|
||||
auto e = prepare<LHS>(1);
|
||||
e[0] -= c[0];
|
||||
BOOST_TEST_EQ(e[0], -double(max<RHS>()));
|
||||
}
|
||||
|
||||
template <typename LHS>
|
||||
void add_all_rhs() {
|
||||
add<LHS, uint8_t>();
|
||||
add<LHS, uint16_t>();
|
||||
add<LHS, uint32_t>();
|
||||
add<LHS, uint64_t>();
|
||||
add<LHS, mp_int>();
|
||||
add<LHS, double>();
|
||||
}
|
||||
struct adder {
|
||||
template <class LHS, class RHS>
|
||||
void operator()(boost::mp11::mp_list<LHS, RHS>) {
|
||||
using buffer_type =
|
||||
std::remove_reference_t<decltype(unsafe_access::unlimited_storage_buffer(
|
||||
std::declval<unlimited_storage_type&>()))>;
|
||||
constexpr auto iLHS = buffer_type::template type_index<LHS>();
|
||||
constexpr auto iRHS = buffer_type::template type_index<RHS>();
|
||||
{
|
||||
auto a = prepare<LHS>(1);
|
||||
BOOST_TEST_EQ(unsafe_access::unlimited_storage_buffer(a).type, iLHS);
|
||||
a[0] += static_cast<RHS>(2);
|
||||
// LHS is never downgraded, only upgraded to RHS.
|
||||
// If RHS is normal integer, LHS doesn't change.
|
||||
BOOST_TEST_EQ(unsafe_access::unlimited_storage_buffer(a).type,
|
||||
iRHS < 4 ? iLHS : std::max(iLHS, iRHS));
|
||||
BOOST_TEST_EQ(a[0], 2);
|
||||
}
|
||||
{
|
||||
auto a = prepare<LHS>(1);
|
||||
a[0] += 2;
|
||||
BOOST_TEST_EQ(a[0], 2);
|
||||
// subtracting converts to double
|
||||
a[0] -= 2;
|
||||
BOOST_TEST_EQ(unsafe_access::unlimited_storage_buffer(a).type, 5);
|
||||
BOOST_TEST_EQ(a[0], 0);
|
||||
}
|
||||
{
|
||||
auto a = prepare<LHS>(1);
|
||||
auto b = prepare<RHS>(1, static_cast<RHS>(2u));
|
||||
// LHS is never downgraded, only upgraded to RHS.
|
||||
// If RHS is normal integer, LHS doesn't change.
|
||||
a[0] += b[0];
|
||||
BOOST_TEST_EQ(unsafe_access::unlimited_storage_buffer(a).type,
|
||||
iRHS < 4 ? iLHS : std::max(iLHS, iRHS));
|
||||
BOOST_TEST_EQ(a[0], 2);
|
||||
a[0] -= b[0];
|
||||
BOOST_TEST_EQ(a[0], 0);
|
||||
a[0] -= b[0];
|
||||
BOOST_TEST_EQ(a[0], -2);
|
||||
}
|
||||
{
|
||||
auto a = prepare<LHS>(1);
|
||||
auto b = max<RHS>();
|
||||
// LHS is never downgraded, only upgraded to RHS.
|
||||
// If RHS is normal integer, LHS doesn't change.
|
||||
a[0] += b;
|
||||
// BOOST_TEST_EQ(unsafe_access::unlimited_storage_buffer(a).type,
|
||||
// iRHS < 4 ? iLHS : std::max(iLHS, iRHS));
|
||||
BOOST_TEST_EQ(a[0], max<RHS>());
|
||||
a[0] += prepare<RHS>(1, b)[0];
|
||||
// BOOST_TEST_EQ(unsafe_access::unlimited_storage_buffer(a).type,
|
||||
// iRHS < 4 ? iLHS + 1 : std::max(iLHS, iRHS));
|
||||
BOOST_TEST_EQ(a[0], 2 * double(max<RHS>()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
// low-level tools
|
||||
{
|
||||
uint8_t c = 0;
|
||||
BOOST_TEST_EQ(bh::detail::safe_increment(c), true);
|
||||
BOOST_TEST_EQ(c, 1);
|
||||
c = 255;
|
||||
BOOST_TEST_EQ(bh::detail::safe_increment(c), false);
|
||||
BOOST_TEST_EQ(c, 255);
|
||||
c = 0;
|
||||
BOOST_TEST_EQ(bh::detail::safe_radd(c, 255u), true);
|
||||
BOOST_TEST_EQ(c, 255);
|
||||
c = 1;
|
||||
BOOST_TEST_EQ(bh::detail::safe_radd(c, 255u), false);
|
||||
BOOST_TEST_EQ(c, 1);
|
||||
c = 255;
|
||||
BOOST_TEST_EQ(bh::detail::safe_radd(c, 1u), false);
|
||||
BOOST_TEST_EQ(c, 255);
|
||||
|
||||
auto eq = bh::detail::equal{};
|
||||
auto eq = detail::equal{};
|
||||
BOOST_TEST(eq(-1, -1));
|
||||
BOOST_TEST(eq(1, 1u));
|
||||
BOOST_TEST(eq(1u, 1));
|
||||
@ -247,124 +240,19 @@ int main() {
|
||||
BOOST_TEST_NOT(eq(-1, (unsigned)-1));
|
||||
BOOST_TEST_NOT(eq((unsigned)-1, -1));
|
||||
|
||||
auto lt = bh::detail::less{};
|
||||
auto lt = detail::less{};
|
||||
BOOST_TEST(lt(1u, 2u));
|
||||
BOOST_TEST(lt(-1, (unsigned)-1));
|
||||
BOOST_TEST(lt(1u, 2));
|
||||
BOOST_TEST(lt(-2, -1));
|
||||
|
||||
auto gt = bh::detail::greater{};
|
||||
auto gt = detail::greater{};
|
||||
BOOST_TEST(gt(2u, 1u));
|
||||
BOOST_TEST(gt(2, 1u));
|
||||
BOOST_TEST(gt((unsigned)-1, -1));
|
||||
BOOST_TEST(gt(-1, -2));
|
||||
}
|
||||
|
||||
// mp_int
|
||||
{
|
||||
const auto vmax = max<uint64_t>();
|
||||
|
||||
BOOST_TEST_EQ(mp_int(), 0u);
|
||||
BOOST_TEST_EQ(mp_int(1u), 1u);
|
||||
BOOST_TEST_EQ(mp_int(1u), 1.0);
|
||||
BOOST_TEST_EQ(mp_int(1u), mp_int(1u));
|
||||
BOOST_TEST_NE(mp_int(1u), 2u);
|
||||
BOOST_TEST_NE(mp_int(1u), 2.0);
|
||||
BOOST_TEST_NE(mp_int(1u), mp_int(2u));
|
||||
BOOST_TEST_LT(mp_int(1u), 2u);
|
||||
BOOST_TEST_LT(mp_int(1u), 2.0);
|
||||
BOOST_TEST_LT(mp_int(1u), mp_int(2u));
|
||||
BOOST_TEST_LE(mp_int(1u), 2u);
|
||||
BOOST_TEST_LE(mp_int(1u), 2.0);
|
||||
BOOST_TEST_LE(mp_int(1u), mp_int(2u));
|
||||
BOOST_TEST_LE(mp_int(1u), 1u);
|
||||
BOOST_TEST_GT(mp_int(1u), 0u);
|
||||
BOOST_TEST_GT(mp_int(1u), 0.0);
|
||||
BOOST_TEST_GT(mp_int(1u), mp_int(0u));
|
||||
BOOST_TEST_GE(mp_int(1u), 0u);
|
||||
BOOST_TEST_GE(mp_int(1u), 0.0);
|
||||
BOOST_TEST_GE(mp_int(1u), 1u);
|
||||
BOOST_TEST_GE(mp_int(1u), mp_int(0u));
|
||||
BOOST_TEST_NOT(mp_int(1u) < mp_int(1u));
|
||||
BOOST_TEST_NOT(mp_int(1u) > mp_int(1u));
|
||||
|
||||
auto a = mp_int();
|
||||
++a;
|
||||
BOOST_TEST_EQ(a.data.size(), 1);
|
||||
BOOST_TEST_EQ(a.data[0], 1);
|
||||
++a;
|
||||
BOOST_TEST_EQ(a.data[0], 2);
|
||||
a = vmax;
|
||||
BOOST_TEST_EQ(a, vmax);
|
||||
BOOST_TEST_EQ(a, static_cast<double>(vmax));
|
||||
++a;
|
||||
BOOST_TEST_EQ(a, make_mp_int(0, 1));
|
||||
++a;
|
||||
BOOST_TEST_EQ(a, make_mp_int(1, 1));
|
||||
a += a;
|
||||
BOOST_TEST_EQ(a, make_mp_int(2, 2));
|
||||
BOOST_TEST_EQ(a, 2 * static_cast<double>(vmax) + 2);
|
||||
|
||||
// carry once A
|
||||
a.data[0] = vmax;
|
||||
a.data[1] = 1;
|
||||
++a;
|
||||
BOOST_TEST_EQ(a, make_mp_int(0, 2));
|
||||
// carry once B
|
||||
a.data[0] = vmax;
|
||||
a.data[1] = 1;
|
||||
a += 1;
|
||||
BOOST_TEST_EQ(a, make_mp_int(0, 2));
|
||||
// carry once C
|
||||
a.data[0] = vmax;
|
||||
a.data[1] = 1;
|
||||
a += make_mp_int(1, 1);
|
||||
BOOST_TEST_EQ(a, make_mp_int(0, 3));
|
||||
|
||||
a.data[0] = vmax - 1;
|
||||
a.data[1] = vmax;
|
||||
++a;
|
||||
BOOST_TEST_EQ(a, make_mp_int(vmax, vmax));
|
||||
|
||||
// carry two times A
|
||||
++a;
|
||||
BOOST_TEST_EQ(a, make_mp_int(0, 0, 1));
|
||||
// carry two times B
|
||||
a = make_mp_int(vmax, vmax);
|
||||
a += 1;
|
||||
BOOST_TEST_EQ(a, make_mp_int(0, 0, 1));
|
||||
// carry two times C
|
||||
a = make_mp_int(vmax, vmax);
|
||||
a += mp_int(1);
|
||||
BOOST_TEST_EQ(a, make_mp_int(0, 0, 1));
|
||||
|
||||
// carry and enlarge
|
||||
a = make_mp_int(vmax, vmax);
|
||||
a += a;
|
||||
BOOST_TEST_EQ(a, make_mp_int(vmax - 1, vmax, 1));
|
||||
|
||||
// add smaller to larger
|
||||
a = make_mp_int(1, 1, 1);
|
||||
a += make_mp_int(1, 1);
|
||||
BOOST_TEST_EQ(a, make_mp_int(2, 2, 1));
|
||||
|
||||
// add larger to smaller
|
||||
a = make_mp_int(1, 1);
|
||||
a += make_mp_int(1, 1, 1);
|
||||
BOOST_TEST_EQ(a, make_mp_int(2, 2, 1));
|
||||
|
||||
a = mp_int(1);
|
||||
auto b = 1.0;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
for (unsigned i = 0; i < 80; ++i) {
|
||||
b += b;
|
||||
BOOST_TEST_NE(a, b);
|
||||
a += a;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
}
|
||||
BOOST_TEST_GT(a.data.size(), 1u);
|
||||
}
|
||||
|
||||
// empty state
|
||||
{
|
||||
unlimited_storage_type a;
|
||||
@ -377,7 +265,7 @@ int main() {
|
||||
copy<uint16_t>();
|
||||
copy<uint32_t>();
|
||||
copy<uint64_t>();
|
||||
copy<mp_int>();
|
||||
copy<large_int>();
|
||||
copy<double>();
|
||||
}
|
||||
|
||||
@ -387,20 +275,20 @@ int main() {
|
||||
equal_1<uint16_t>();
|
||||
equal_1<uint32_t>();
|
||||
equal_1<uint64_t>();
|
||||
equal_1<mp_int>();
|
||||
equal_1<large_int>();
|
||||
equal_1<double>();
|
||||
|
||||
equal_2<uint8_t, unsigned>();
|
||||
equal_2<uint16_t, unsigned>();
|
||||
equal_2<uint32_t, unsigned>();
|
||||
equal_2<uint64_t, unsigned>();
|
||||
equal_2<mp_int, unsigned>();
|
||||
equal_2<large_int, unsigned>();
|
||||
equal_2<double, unsigned>();
|
||||
|
||||
equal_2<mp_int, double>();
|
||||
equal_2<large_int, double>();
|
||||
|
||||
auto a = prepare<double>(1);
|
||||
auto b = prepare<mp_int>(1);
|
||||
auto b = prepare<large_int>(1);
|
||||
BOOST_TEST(a == b);
|
||||
++a[0];
|
||||
BOOST_TEST_NOT(a == b);
|
||||
@ -413,8 +301,8 @@ int main() {
|
||||
increase_and_grow<uint32_t>();
|
||||
increase_and_grow<uint64_t>();
|
||||
|
||||
// only increase for mp_int
|
||||
auto a = prepare<mp_int>(2, static_cast<mp_int>(1));
|
||||
// only increase for large_int
|
||||
auto a = prepare<large_int>(2, static_cast<large_int>(1));
|
||||
BOOST_TEST_EQ(a[0], 1);
|
||||
BOOST_TEST_EQ(a[1], 0);
|
||||
++a[0];
|
||||
@ -424,12 +312,9 @@ int main() {
|
||||
|
||||
// add
|
||||
{
|
||||
add_all_rhs<uint8_t>();
|
||||
add_all_rhs<uint16_t>();
|
||||
add_all_rhs<uint32_t>();
|
||||
add_all_rhs<uint64_t>();
|
||||
add_all_rhs<mp_int>();
|
||||
add_all_rhs<double>();
|
||||
using namespace boost::mp11;
|
||||
using L = mp_list<uint8_t, uint16_t, uint64_t, large_int, double>;
|
||||
mp_for_each<mp_product<mp_list, L, L>>(adder());
|
||||
}
|
||||
|
||||
// add_and_grow
|
||||
@ -478,7 +363,7 @@ int main() {
|
||||
convert_array_storage<uint16_t>();
|
||||
convert_array_storage<uint32_t>();
|
||||
convert_array_storage<uint64_t>();
|
||||
convert_array_storage<mp_int>();
|
||||
convert_array_storage<large_int>();
|
||||
convert_array_storage<double>();
|
||||
}
|
||||
|
||||
@ -496,6 +381,16 @@ int main() {
|
||||
BOOST_TEST_EQ(a[0], 1);
|
||||
BOOST_TEST_GE(a[0], 1);
|
||||
BOOST_TEST_LE(a[0], 1);
|
||||
BOOST_TEST_NE(a[0], 2);
|
||||
BOOST_TEST_GT(2, a[0]);
|
||||
BOOST_TEST_LT(0, a[0]);
|
||||
BOOST_TEST_GE(1, a[0]);
|
||||
BOOST_TEST_GE(2, a[0]);
|
||||
BOOST_TEST_LE(0, a[0]);
|
||||
BOOST_TEST_LE(1, a[0]);
|
||||
BOOST_TEST_EQ(1, a[0]);
|
||||
BOOST_TEST_NE(2, a[0]);
|
||||
|
||||
++b[0];
|
||||
BOOST_TEST_EQ(a[0], b[0]);
|
||||
b[0] += 2;
|
||||
@ -507,6 +402,14 @@ int main() {
|
||||
c[0] = c[1] = 1;
|
||||
BOOST_TEST_EQ(c[0], 1);
|
||||
BOOST_TEST_EQ(c[1], 1);
|
||||
|
||||
auto d = prepare(2);
|
||||
d[1] = unlimited_storage_type::large_int{2};
|
||||
BOOST_TEST_EQ(unsafe_access::unlimited_storage_buffer(d).type, 4);
|
||||
d[0] = -2;
|
||||
BOOST_TEST_EQ(unsafe_access::unlimited_storage_buffer(d).type, 5);
|
||||
BOOST_TEST_EQ(d[0], -2);
|
||||
BOOST_TEST_EQ(d[1], 2);
|
||||
}
|
||||
|
||||
// iterators
|
||||
@ -542,5 +445,62 @@ int main() {
|
||||
BOOST_TEST_EQ(a[1], 2);
|
||||
}
|
||||
|
||||
// memory exhaustion
|
||||
{
|
||||
using S = unlimited_storage<failing_allocator<char>>;
|
||||
using alloc_t = typename S::allocator_type;
|
||||
std::size_t large_int_nbytes = 0;
|
||||
typename S::large_int li{1, alloc_t{1024, large_int_nbytes}};
|
||||
BOOST_TEST_GT(large_int_nbytes, 0);
|
||||
const auto n = sizeof(li) + large_int_nbytes;
|
||||
std::size_t nbytes = 0;
|
||||
S s(alloc_t{n, nbytes});
|
||||
s.reset(n); // should work
|
||||
BOOST_TEST_EQ(nbytes, n);
|
||||
bool bad_alloc = false;
|
||||
try {
|
||||
s.reset(n + 1); // should not work
|
||||
} catch (std::bad_alloc&) { bad_alloc = true; }
|
||||
BOOST_TEST(bad_alloc);
|
||||
|
||||
// storage must be still in valid state
|
||||
BOOST_TEST_EQ(s.size(), 0);
|
||||
auto& buffer = unsafe_access::unlimited_storage_buffer(s);
|
||||
BOOST_TEST_EQ(buffer.ptr, nullptr);
|
||||
BOOST_TEST_EQ(buffer.type, 0);
|
||||
// all allocated memory should have returned
|
||||
BOOST_TEST_EQ(nbytes, 0);
|
||||
|
||||
// scenario in which the constructor of large_int fails
|
||||
bad_alloc = false;
|
||||
li.data.resize(10, 1);
|
||||
s.reset(2);
|
||||
const auto old_ptr = buffer.ptr;
|
||||
try {
|
||||
s[0] += li;
|
||||
} catch (std::bad_alloc&) { bad_alloc = true; }
|
||||
BOOST_TEST(bad_alloc);
|
||||
|
||||
// storage still in valid state
|
||||
BOOST_TEST_EQ(s.size(), 2);
|
||||
BOOST_TEST_EQ(buffer.ptr, old_ptr);
|
||||
BOOST_TEST_EQ(buffer.type, 0);
|
||||
BOOST_TEST_EQ(nbytes, 2);
|
||||
|
||||
// buffer.make<large_int>(n) may be called by serialization
|
||||
bad_alloc = false;
|
||||
try {
|
||||
buffer.make<typename S::large_int>(2);
|
||||
} catch (std::bad_alloc&) { bad_alloc = true; }
|
||||
BOOST_TEST(bad_alloc);
|
||||
|
||||
// storage still in valid state
|
||||
BOOST_TEST_EQ(s.size(), 0);
|
||||
BOOST_TEST_EQ(buffer.ptr, nullptr);
|
||||
BOOST_TEST_EQ(buffer.type, 0);
|
||||
// all memory returned
|
||||
BOOST_TEST_EQ(nbytes, 0);
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
@ -32,7 +32,10 @@ struct tracing_allocator {
|
||||
|
||||
tracing_allocator_db* db = nullptr;
|
||||
|
||||
tracing_allocator() noexcept {}
|
||||
tracing_allocator() noexcept = default;
|
||||
tracing_allocator(const tracing_allocator&) noexcept = default;
|
||||
tracing_allocator(tracing_allocator&&) noexcept = default;
|
||||
|
||||
tracing_allocator(tracing_allocator_db& x) noexcept : db(&x) {}
|
||||
template <class U>
|
||||
tracing_allocator(const tracing_allocator<U>& a) noexcept : db(a.db) {}
|
||||
@ -72,6 +75,54 @@ constexpr bool operator!=(const tracing_allocator<T>& t,
|
||||
return !operator==(t, u);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
struct failing_allocator {
|
||||
using value_type = T;
|
||||
|
||||
std::size_t nfail = 0;
|
||||
std::size_t* nbytes = nullptr;
|
||||
|
||||
failing_allocator() noexcept = default;
|
||||
failing_allocator(const failing_allocator& x) noexcept = default;
|
||||
|
||||
explicit failing_allocator(std::size_t nf, std::size_t& nb) noexcept
|
||||
: nfail(nf), nbytes(&nb) {}
|
||||
template <class U>
|
||||
failing_allocator(const failing_allocator<U>& a) noexcept
|
||||
: nfail(a.nfail), nbytes(a.nbytes) {}
|
||||
template <class U>
|
||||
failing_allocator& operator=(const failing_allocator<U>& a) noexcept {
|
||||
nfail = a.nfail;
|
||||
nbytes = a.nbytes;
|
||||
return *this;
|
||||
}
|
||||
~failing_allocator() noexcept {}
|
||||
|
||||
T* allocate(std::size_t n) {
|
||||
if (nbytes) {
|
||||
if (*nbytes + n * sizeof(T) > nfail) throw std::bad_alloc{};
|
||||
*nbytes += n * sizeof(T);
|
||||
}
|
||||
return static_cast<T*>(::operator new(n * sizeof(T)));
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t n) {
|
||||
if (nbytes) *nbytes -= n * sizeof(T);
|
||||
::operator delete((void*)p);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
constexpr bool operator==(const failing_allocator<T>&,
|
||||
const failing_allocator<U>&) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool operator!=(const failing_allocator<T>& t, const failing_allocator<U>& u) noexcept {
|
||||
return !operator==(t, u);
|
||||
}
|
||||
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
|
30
tools/cov.sh
30
tools/cov.sh
@ -1,36 +1,34 @@
|
||||
#!/bin/sh
|
||||
# must be executed in project root folder
|
||||
if [ -z $GCOV ]; then
|
||||
GCOV=gcov
|
||||
GCOV=gcov-5
|
||||
fi
|
||||
|
||||
LCOV_EXE="tools/lcov-1.13/bin/lcov"
|
||||
LCOV_VERSION="1.13"
|
||||
LCOV_DIR="tools/lcov-${LCOV_VERSION}"
|
||||
|
||||
if [ ! -e $LCOV_EXE ]; then
|
||||
if [ ! -e $LCOV_DIR ]; then
|
||||
cd tools
|
||||
wget -O - https://github.com/linux-test-project/lcov/releases/download/v1.13/lcov-1.13.tar.gz | tar zxf -
|
||||
wget -O - https://github.com/linux-test-project/lcov/releases/download/v${LCOV_VERSION}/lcov-${LCOV_VERSION}.tar.gz | tar zxf -
|
||||
cd ..
|
||||
fi
|
||||
|
||||
# LCOV="$LCOV_EXE --gcov-tool=${GCOV} --rc lcov_branch_coverage=1"
|
||||
LCOV="$LCOV_EXE --gcov-tool=${GCOV}" # no branch coverage
|
||||
LCOV="${LCOV_DIR}/bin/lcov --gcov-tool=${GCOV}" # no branch coverage
|
||||
|
||||
if [ ! -e coverage.info ]; then
|
||||
# collect raw data
|
||||
$LCOV --base-directory `pwd`/test --directory `pwd`/../../bin.v2/libs/histogram/test --capture --output-file test.info
|
||||
$LCOV --base-directory `pwd`/examples --directory `pwd`/../../bin.v2/libs/histogram/examples --capture --output-file examples.info
|
||||
# collect raw data
|
||||
$LCOV --base-directory `pwd` \
|
||||
--directory `pwd`/../../bin.v2/libs/histogram/test \
|
||||
--directory `pwd`/../../bin.v2/libs/histogram/examples \
|
||||
--capture --output-file coverage.info
|
||||
|
||||
# merge files
|
||||
$LCOV -a test.info -a examples.info -o all.info
|
||||
|
||||
# remove uninteresting entries
|
||||
$LCOV --extract all.info "*/boost/histogram/*" --output-file coverage.info
|
||||
fi
|
||||
# remove uninteresting entries
|
||||
$LCOV --extract coverage.info "*/boost/histogram/*" --output-file coverage.info
|
||||
|
||||
if [ $CI ]; then
|
||||
# upload if on CI
|
||||
curl -s https://codecov.io/bash | bash -s - -f coverage.info -X gcov -x $GCOV
|
||||
else
|
||||
# otherwise just print
|
||||
$LCOV --list coverage.info
|
||||
$LCOV_DIR/bin/genhtml coverage.info -o coverage-report
|
||||
fi
|
||||
|
2
tools/llvm-gcov.sh
Executable file
2
tools/llvm-gcov.sh
Executable file
@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
exec llvm-cov gcov "$@"
|
Loading…
x
Reference in New Issue
Block a user