// // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot 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/boostorg/url // #ifndef BOOST_URL_GRAMMAR_IMPL_RANGE_HPP #define BOOST_URL_GRAMMAR_IMPL_RANGE_HPP #include #include #include #include #include #include #include #include #include #include // ::max_align_t namespace boost { namespace urls { namespace grammar { // VFALCO This could be reused for // other things that need to type-erase //------------------------------------------------ // // any_rule // //------------------------------------------------ // base class for the type-erased rule pair template struct range:: any_rule { virtual ~any_rule() = default; virtual void move(void* dest) noexcept { ::new(dest) any_rule( std::move(*this)); } virtual void copy(void* dest) const noexcept { ::new(dest) any_rule(*this); } virtual system::result first( char const*&, char const*) const noexcept { return system::error_code{}; } virtual system::result next( char const*&, char const*) const noexcept { return system::error_code{}; } }; //------------------------------------------------ // small template template struct range::impl1 : any_rule , private empty_value { explicit impl1(R const& next) noexcept : empty_value( empty_init, next) { } private: impl1(impl1&&) noexcept = default; impl1(impl1 const&) noexcept = default; void move(void* dest ) noexcept override { ::new(dest) impl1( std::move(*this)); } void copy(void* dest ) const noexcept override { ::new(dest) impl1(*this); } system::result first( char const*& it, char const* end) const noexcept override { return grammar::parse( it, end, this->get()); } system::result next( char const*& it, char const* end) const noexcept override { return grammar::parse( it, end, this->get()); } }; //------------------------------------------------ // big template template struct range::impl1 : any_rule { explicit impl1(R const& next) noexcept { ::new(p_->addr()) impl{next}; } private: struct impl { R r; }; recycled_ptr< aligned_storage> p_; impl1(impl1&&) noexcept = default; impl1(impl1 const&) noexcept = default; impl const& get() const noexcept { return *reinterpret_cast< impl const*>(p_->addr()); } ~impl1() { if(p_) get().~impl(); } void move(void* dest ) noexcept override { ::new(dest) impl1( std::move(*this)); } void copy(void* dest ) const noexcept override { ::new(dest) impl1(*this); } system::result first( char const*& it, char const* end) const noexcept override { return grammar::parse( it, end, this->get().r); } system::result next( char const*& it, char const* end) const noexcept override { return grammar::parse( it, end, this->get().r); } }; //------------------------------------------------ // small template template< class R0, class R1, bool Small> struct range::impl2 : any_rule , private empty_value , private empty_value { impl2( R0 const& first, R1 const& next) noexcept : empty_value( empty_init, first) , empty_value( empty_init, next) { } private: impl2(impl2&&) noexcept = default; impl2(impl2 const&) noexcept = default; void move(void* dest ) noexcept override { ::new(dest) impl2( std::move(*this)); } void copy(void* dest ) const noexcept override { ::new(dest) impl2(*this); } system::result first( char const*& it, char const* end) const noexcept override { return grammar::parse(it, end, empty_value< R0,0>::get()); } system::result next( char const*& it, char const* end) const noexcept override { return grammar::parse(it, end, empty_value< R1,1>::get()); } }; //------------------------------------------------ // big template template< class R0, class R1> struct range::impl2 : any_rule { impl2( R0 const& first, R1 const& next) noexcept { ::new(p_->addr()) impl{ first, next}; } private: struct impl { R0 first; R1 next; }; recycled_ptr< aligned_storage> p_; impl2(impl2&&) noexcept = default; impl2(impl2 const&) noexcept = default; impl const& get() const noexcept { return *reinterpret_cast< impl const*>(p_->addr()); } ~impl2() { if(p_) get().~impl(); } void move(void* dest ) noexcept override { ::new(dest) impl2( std::move(*this)); } void copy(void* dest ) const noexcept override { ::new(dest) impl2(*this); } system::result first( char const*& it, char const* end) const noexcept override { return grammar::parse( it, end, get().first); } system::result next( char const*& it, char const* end) const noexcept override { return grammar::parse( it, end, get().next); } }; //------------------------------------------------ // // iterator // //------------------------------------------------ template class range:: iterator { public: using value_type = T; using reference = T const&; using pointer = void const*; using difference_type = std::ptrdiff_t; using iterator_category = std::forward_iterator_tag; iterator() = default; iterator( iterator const&) = default; iterator& operator=( iterator const&) = default; reference operator*() const noexcept { return *rv_; } bool operator==( iterator const& other) const noexcept { // can't compare iterators // from different containers! BOOST_ASSERT(r_ == other.r_); return p_ == other.p_; } bool operator!=( iterator const& other) const noexcept { return !(*this == other); } iterator& operator++() noexcept { BOOST_ASSERT( p_ != nullptr); auto const end = r_->s_.data() + r_->s_.size(); rv_ = r_->get().next(p_, end); if( !rv_ ) p_ = nullptr; return *this; } iterator operator++(int) noexcept { auto tmp = *this; ++*this; return tmp; } private: friend class range; range const* r_ = nullptr; char const* p_ = nullptr; system::result rv_; iterator( range const& r) noexcept : r_(&r) , p_(r.s_.data()) { auto const end = r_->s_.data() + r_->s_.size(); rv_ = r_->get().first(p_, end); if( !rv_ ) p_ = nullptr; } constexpr iterator( range const& r, int) noexcept : r_(&r) , p_(nullptr) { } }; //------------------------------------------------ template template range:: range( core::string_view s, std::size_t n, R const& next) : s_(s) , n_(n) { BOOST_STATIC_ASSERT( sizeof(impl1) <= BufferSize); ::new(&get()) impl1) <= BufferSize>(next); } //------------------------------------------------ template template< class R0, class R1> range:: range( core::string_view s, std::size_t n, R0 const& first, R1 const& next) : s_(s) , n_(n) { BOOST_STATIC_ASSERT( sizeof(impl2) <= BufferSize); ::new(&get()) impl2 ) <= BufferSize>( first, next); } //------------------------------------------------ template range:: ~range() { get().~any_rule(); } template range:: range() noexcept { ::new(&get()) any_rule{}; char const* it = nullptr; get().first(it, nullptr); get().next(it, nullptr); } template range:: range( range&& other) noexcept : s_(other.s_) , n_(other.n_) { other.s_ = {}; other.n_ = {}; other.get().move(&get()); other.get().~any_rule(); ::new(&other.get()) any_rule{}; } template range:: range( range const& other) noexcept : s_(other.s_) , n_(other.n_) { other.get().copy(&get()); } template auto range:: operator=( range&& other) noexcept -> range& { s_ = other.s_; n_ = other.n_; other.s_ = {}; other.n_ = 0; // VFALCO we rely on nothrow move // construction here, but if necessary we // could move to a local buffer first. get().~any_rule(); other.get().move(&get()); other.get().~any_rule(); ::new(&other.get()) any_rule{}; return *this; } template auto range:: operator=( range const& other) noexcept -> range& { s_ = other.s_; n_ = other.n_; // VFALCO we rely on nothrow copy // construction here, but if necessary we // could construct to a local buffer first. get().~any_rule(); other.get().copy(&get()); return *this; } template auto range:: begin() const noexcept -> iterator { return { *this }; } template auto range:: end() const noexcept -> iterator { return { *this, 0 }; } //------------------------------------------------ template auto range_rule_t:: parse( char const*& it, char const* end) const -> system::result { using T = typename R::value_type; std::size_t n = 0; auto const it0 = it; auto it1 = it; auto rv = (grammar::parse)( it, end, next_); if( !rv ) { if(rv.error() != error::end_of_range) { // rewind unless error::end_of_range it = it1; } if(n < N_) { // too few BOOST_URL_RETURN_EC( error::mismatch); } // good return range( core::string_view(it0, it - it0), n, next_); } for(;;) { ++n; it1 = it; rv = (grammar::parse)( it, end, next_); if( !rv ) { if(rv.error() != error::end_of_range) { // rewind unless error::end_of_range it = it1; } break; } if(n >= M_) { // too many BOOST_URL_RETURN_EC( error::mismatch); } } if(n < N_) { // too few BOOST_URL_RETURN_EC( error::mismatch); } // good return range( core::string_view(it0, it - it0), n, next_); } //------------------------------------------------ template auto range_rule_t:: parse( char const*& it, char const* end) const -> system::result> { using T = typename R0::value_type; std::size_t n = 0; auto const it0 = it; auto it1 = it; auto rv = (grammar::parse)( it, end, first_); if( !rv ) { if(rv.error() != error::end_of_range) { // rewind unless error::end_of_range it = it1; } if(n < N_) { // too few BOOST_URL_RETURN_EC( error::mismatch); } // good return range( core::string_view(it0, it - it0), n, first_, next_); } for(;;) { ++n; it1 = it; rv = (grammar::parse)( it, end, next_); if( !rv ) { if(rv.error() != error::end_of_range) { // rewind unless error::end_of_range it = it1; } break; } if(n >= M_) { // too many BOOST_URL_RETURN_EC( error::mismatch); } } if(n < N_) { // too few BOOST_URL_RETURN_EC( error::mismatch); } // good return range( core::string_view(it0, it - it0), n, first_, next_); } } // grammar } // urls } // boost #endif