Skip to content

Commit

Permalink
add any_allocator
Browse files Browse the repository at this point in the history
  • Loading branch information
alandefreitas committed Jan 16, 2022
1 parent cbd0ad6 commit ed7b9b2
Show file tree
Hide file tree
Showing 8 changed files with 779 additions and 2 deletions.
1 change: 1 addition & 0 deletions doc/qbk/quickref.xml
Expand Up @@ -25,6 +25,7 @@
<entry valign="top">
<bridgehead renderas="sect3">Classes</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="url.ref.boost__urls__any_allocator">any_allocator</link></member>
<member><link linkend="url.ref.boost__urls__authority_view">authority_view</link></member>
<member><link linkend="url.ref.boost__urls__ipv4_address">ipv4_address</link></member>
<member><link linkend="url.ref.boost__urls__ipv6_address">ipv6_address</link></member>
Expand Down
95 changes: 95 additions & 0 deletions include/boost/url/any_allocator.hpp
@@ -0,0 +1,95 @@
//
// Copyright (c) 2022 Alan Freitas (alandefreitas@gmail.com)
//
// 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)
//
// Official repository: https://github.com/CPPAlliance/url
//

#ifndef BOOST_URL_ANY_ALLOCATOR_HPP
#define BOOST_URL_ANY_ALLOCATOR_HPP

#include <boost/url/detail/config.hpp>
#include <boost/url/detail/any_allocator.hpp>

#include <memory>
#include <type_traits>

namespace boost {
namespace urls {

/** A type-erased allocator with shared ownership.
This type satisfies the requirements for <em>Allocator</em>
@par Specification
@li <a href="https://en.cppreference.com/w/cpp/named_req/Allocator"
>Allocator (cppreference.com)</a>
*/
#ifdef BOOST_URL_DOCS
template <class T>
using any_allocator = __see_below__;
#else
template <class T>
class any_allocator
: private detail::any_allocator_base
{
std::shared_ptr<base> p_;

template <class U>
friend class any_allocator;

public:
using value_type = T;

template <class U>
struct rebind
{
using other = any_allocator<U>;
};

using is_always_equal = std::false_type;

any_allocator() noexcept;

any_allocator(any_allocator const&) noexcept = default;

template <class U>
explicit any_allocator(any_allocator<U> const& other) noexcept;

template <class Allocator>
explicit any_allocator(Allocator const& a);

any_allocator&
operator=(any_allocator const&) = default;

T*
allocate(std::size_t n) const;

void
deallocate(T* p, std::size_t n);

friend bool
operator==(const any_allocator& a, const any_allocator& b) noexcept
{
return (a.p_ == b.p_) ||
(a.p_->type == b.p_->type &&
a.p_->is_equal(*b.p_));
}

friend bool
operator!=(const any_allocator& a, const any_allocator& b) noexcept
{
return ! (a == b);
}
};
#endif

} // urls
} // boost

#include <boost/url/impl/any_allocator.hpp>

#endif
176 changes: 176 additions & 0 deletions include/boost/url/detail/any_allocator.hpp
@@ -0,0 +1,176 @@
//
// Copyright (c) 2022 Alan Freitas (alandefreitas@gmail.com)
//
// 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)
//
// Official repository: https://github.com/CPPAlliance/url
//


#ifndef BOOST_URL_DETAIL_ANY_ALLOCATOR_HPP
#define BOOST_URL_DETAIL_ANY_ALLOCATOR_HPP

#include <boost/url/detail/type_id.hpp>

#include <boost/core/allocator_access.hpp>
#include <boost/core/empty_value.hpp>
#include <boost/type_traits/type_with_alignment.hpp>

#include <cstddef>

namespace boost {
namespace urls {
namespace detail {

struct any_allocator_base
{
struct base
{
type_id_t type;

virtual
~base() = default;

virtual
void *
allocate(std::size_t n, std::size_t size, std::size_t align) = 0;

virtual
void
deallocate(void *p, std::size_t n, std::size_t size, std::size_t align) = 0;

virtual
bool
is_equal(const any_allocator_base::base &p) = 0;
};

template <typename Allocator>
struct impl
: public base,
private empty_value<boost::allocator_rebind_t<Allocator, char>, 0>
{
using allocator_type = boost::allocator_rebind_t<Allocator, char>;

explicit
impl(allocator_type const& a)
: empty_value<allocator_type, 0>(empty_init_t(), a)
{
type = get_type_id<allocator_type>();
}

void*
allocate(std::size_t n, std::size_t size, std::size_t align) override
{
if (align == 1)
return allocator().allocate(n);
else
return allocate_aligned(n, size, align);
}

void
deallocate(void* p, std::size_t n, std::size_t size, std::size_t align)
{
if (align == alignof(char))
allocator().deallocate(reinterpret_cast<char*>(p), n);
else
deallocate_aligned(p, n, size, align);
}

bool
is_equal(const any_allocator_base::base& other) override
{
const impl* other_impl = static_cast<const impl*>(&other);
return allocator() == other_impl->allocator();
};

const allocator_type& allocator() const {
return empty_value<allocator_type, 0>::get();
}

allocator_type& allocator() {
return empty_value<allocator_type, 0>::get();
}

#if BOOST_WORKAROUND(BOOST_GCC, < 40900)
using max_align_t = ::max_align_t;
#else
using max_align_t = std::max_align_t;
#endif

static
std::size_t
ceil_div(std::size_t n, std::size_t d)
{
return (n + d - 1) / d;
}

template <std::size_t N>
typename std::enable_if<alignof(max_align_t) <= N, void*>::type
try_allocate_aligned(std::size_t n, std::size_t size, std::size_t align)
{
using aligned_t = max_align_t;
using aligned_allocator_t = boost::allocator_rebind_t<Allocator, aligned_t>;
return aligned_allocator_t(allocator()).allocate(n * ceil_div(size, align));
}

template <std::size_t N>
typename std::enable_if<N < alignof(max_align_t), void*>::type
try_allocate_aligned(std::size_t n, std::size_t size, std::size_t align)
{
if (align <= N)
{
using aligned_t = typename boost::type_with_alignment<N>::type;
using aligned_allocator_t = boost::allocator_rebind_t<Allocator, aligned_t>;
return aligned_allocator_t(allocator()).allocate(n * ceil_div(size, align));
}
else
{
return try_allocate_aligned<2*N>(n, size, align);
}
}

void*
allocate_aligned(std::size_t n, std::size_t size, std::size_t align)
{
return try_allocate_aligned<2>(n, size, align);
}


template <std::size_t N>
typename std::enable_if<alignof(max_align_t) <= N, void>::type
try_deallocate_aligned(void* p, std::size_t n, std::size_t size, std::size_t align)
{
using aligned_t = max_align_t;
using aligned_allocator_t = boost::allocator_rebind_t<Allocator, aligned_t>;
aligned_allocator_t(allocator()).deallocate(reinterpret_cast<aligned_t*>(p), n * ceil_div(size, align));
}

template <std::size_t N>
typename std::enable_if<N < alignof(max_align_t), void>::type
try_deallocate_aligned(void* p, std::size_t n, std::size_t size, std::size_t align)
{
if (align <= N)
{
using aligned_t = typename boost::type_with_alignment<N>::type;
using aligned_allocator_t = boost::allocator_rebind_t<Allocator, aligned_t>;
aligned_allocator_t(allocator()).deallocate(reinterpret_cast<aligned_t*>(p), n * ceil_div(size, align));
} else {
try_deallocate_aligned<2*N>(p, n, size, align);
}
}

void
deallocate_aligned(void* p, std::size_t n, std::size_t size, std::size_t align)
{
try_deallocate_aligned<2>(p, n, size, align);
}
};
};

} // detail
} // urls
} // boost


#endif
32 changes: 32 additions & 0 deletions include/boost/url/detail/type_id.hpp
@@ -0,0 +1,32 @@
//
// Copyright (c) 2022 Alan Freitas (alandefreitas@gmail.com)
//
// 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)
//
// Official repository: https://github.com/CPPAlliance/url
//


#ifndef BOOST_URL_DETAIL_TYPE_ID_HPP
#define BOOST_URL_DETAIL_TYPE_ID_HPP

namespace boost {
namespace urls {
namespace detail {

using type_id_t = void const *;

template <class T>
type_id_t
get_type_id() noexcept
{
static constexpr char c{};
return &c;
}

} // namespace detail
} // namespace urls
} // namespace boost

#endif
70 changes: 70 additions & 0 deletions include/boost/url/impl/any_allocator.hpp
@@ -0,0 +1,70 @@
//
// Copyright (c) 2022 Alan Freitas (alandefreitas@gmail.com)
//
// 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)
//
// Official repository: https://github.com/CPPAlliance/url
//

#ifndef BOOST_URL_IMPL_ANY_ALLOCATOR_HPP
#define BOOST_URL_IMPL_ANY_ALLOCATOR_HPP

#include <boost/assert.hpp>
#include <boost/config/workaround.hpp>
#include <boost/type_traits/make_void.hpp>

#include <cstddef>

namespace boost {
namespace urls {

template <class T>
any_allocator<T>::
any_allocator() noexcept
: any_allocator(std::allocator<char>{})
{
}

template <class T>
template <class Allocator>
any_allocator<T>::
any_allocator(Allocator const& a)
{
using char_allocator_type = boost::allocator_rebind_t<Allocator, char>;
p_ = std::allocate_shared<impl<char_allocator_type>>(a, char_allocator_type{});
}

template <class T>
template <class U>
any_allocator<T>::
any_allocator(
any_allocator<U> const& other) noexcept
: p_(other.p_)
{
}

template <class T>
T*
any_allocator<T>::
allocate(
std::size_t n) const
{
return reinterpret_cast<T*>(p_->allocate(n, sizeof(T), alignof(T)));
}

template <class T>
void
any_allocator<T>::
deallocate(
T* p,
std::size_t n)
{
return p_->deallocate(p, n, sizeof(T), alignof(T));
}

} // urls
} // boost


#endif
3 changes: 1 addition & 2 deletions test/unit/CMakeLists.txt
Expand Up @@ -14,9 +14,8 @@ if(NOT TARGET tests)
endif()

set(BOOST_URL_TESTS_FILES
CMakeLists.txt
Jamfile
test_rule.hpp
any_allocator.cpp
authority_view.cpp
error.cpp
error_code.cpp
Expand Down

0 comments on commit ed7b9b2

Please sign in to comment.