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:
Hans Dembinski 2019-04-22 03:33:05 +02:00 committed by GitHub
parent ed4935cd93
commit 6b386c9b51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1022 additions and 798 deletions

1
.gitignore vendored
View File

@ -15,3 +15,4 @@ CMakeSettings.json
.vs*
*.info
tools/lcov-*
coverage-report

View File

@ -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

View File

@ -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
View File

@ -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

View 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

View File

@ -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

View File

@ -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",

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 ]

View 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();
}

View File

@ -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);

View File

@ -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>();
}

View File

@ -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();
}

View File

@ -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

View File

@ -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
View File

@ -0,0 +1,2 @@
#!/bin/sh
exec llvm-cov gcov "$@"