Browse Source

feat(libstdc++): add std::list

greatbridf 1 year ago
parent
commit
1f1af196a0
2 changed files with 638 additions and 15 deletions
  1. 465 0
      gblibstdc++/include/list
  2. 173 15
      gblibstdc++/include/memory

+ 465 - 0
gblibstdc++/include/list

@@ -0,0 +1,465 @@
+#ifndef __GBLIBCPP_LIST__
+#define __GBLIBCPP_LIST__
+
+#include <memory>
+#include <type_traits>
+#include <utility>
+#include <cstddef>
+
+namespace std {
+
+template <typename T,
+    typename Allocator = std::allocator<T>>
+class list {
+private:
+    struct node_base {
+        node_base* prev;
+        node_base* next;
+        constexpr node_base() noexcept
+            : prev { this }, next { this } {}
+
+        constexpr void connect(node_base* _next) noexcept
+        {
+            this->next = _next;
+            _next->prev = static_cast<node_base*>(this);
+        }
+
+        constexpr void swap(node_base& other) noexcept
+        {
+            std::swap(prev, other.prev);
+            std::swap(next, other.next);
+        }
+    };
+
+    struct node : public node_base {
+        T value;
+        template <typename... Args>
+        explicit constexpr node(Args&&... args)
+            : value(std::forward<Args>(args)...) { }
+    };
+
+public:
+    template <bool Const>
+    class _iterator;
+
+public:
+    using value_type = T;
+    using allocator_type = Allocator;
+    using size_type = std::size_t;
+    using difference_type = std::ptrdiff_t;
+    using reference = T&;
+    using const_reference = const T&;
+    using pointer = typename std::allocator_traits<Allocator>::pointer;
+    using const_pointer = typename
+        std::allocator_traits<Allocator>::const_pointer;
+    using iterator = _iterator<false>;
+    using const_iterator = _iterator<true>;
+
+private:
+    using alloc_traits = std::allocator_traits<allocator_type>;
+    using node_alloc_type = typename
+        std::allocator_traits<Allocator>::template rebind_alloc<node>;
+    using node_alloc_traits = std::allocator_traits<node_alloc_type>;
+
+public:
+    template <bool Const>
+    class _iterator {
+    public:
+        using const_node_pointer = const node_base*;
+        using node_pointer = node_base*;
+        using value_type = std::conditional_t<Const, const T, T>;
+        using pointer = std::add_pointer_t<value_type>;
+        using reference = std::add_lvalue_reference_t<value_type>;
+
+        friend class list;
+    
+    private:
+        node_pointer p;
+
+    public:
+        constexpr _iterator() noexcept = default;
+        explicit constexpr _iterator(const_node_pointer p)
+            : p { const_cast<node_pointer>(p) } {}
+        constexpr _iterator(const _iterator& iter) noexcept = default;
+        constexpr _iterator(_iterator&& iter) noexcept = default;
+        constexpr ~_iterator() = default;
+        constexpr _iterator& operator=(const _iterator& iter) noexcept = default;
+        constexpr _iterator& operator=(_iterator&& iter) noexcept = default;
+        constexpr bool operator==(const _iterator& iter) const noexcept = default;
+
+        constexpr reference operator*() const noexcept
+        { return ((node*)p)->value; }
+        constexpr pointer operator&() const noexcept
+        { return std::addressof(this->operator*()); }
+        constexpr pointer operator->() const noexcept
+        { return this->operator&(); }
+        constexpr _iterator& operator++() noexcept
+        { p = p->next; return *this; }
+        constexpr _iterator operator++(int) noexcept
+        { _iterator ret(p); (void)this->operator++(); return ret; }
+        constexpr _iterator& operator--(void) noexcept
+        { p = p->prev; return *this; }
+        constexpr _iterator operator--(int) noexcept
+        { _iterator ret(p); (void)this->operator--(); return ret; }
+        constexpr operator bool() { return p; }
+        constexpr operator _iterator<true>() { return _iterator<true> { p }; }
+    };
+
+private:
+    node_base m_head;
+    size_type m_size;
+    node_alloc_type m_alloc;
+
+private:
+    // move m_head and m_size of other to *this
+    // other MUST NOT be empty, *this MUST be empty
+    constexpr void _move_from(list&& other) noexcept
+    {
+        std::swap(m_size, other.m_size);
+        other.m_head.prev->connect(&m_head);
+        m_head.connect(other.m_head.next);
+        other.m_head.next = other.m_head.prev = &other.m_head;
+    }
+
+public:
+    __GBLIBCPP_CONSTEXPR
+    iterator end(void) noexcept { return iterator { &m_head }; }
+    __GBLIBCPP_CONSTEXPR
+    const_iterator cend(void) const noexcept { return const_iterator { &m_head }; }
+    __GBLIBCPP_CONSTEXPR
+    const_iterator end(void) const noexcept { return cend(); }
+
+    __GBLIBCPP_CONSTEXPR
+    iterator begin(void) noexcept { return iterator { m_head.next }; }
+    __GBLIBCPP_CONSTEXPR
+    const_iterator cbegin(void) const noexcept
+    { return const_iterator { m_head.next }; }
+    __GBLIBCPP_CONSTEXPR
+    const_iterator begin(void) const noexcept { return cbegin(); }
+
+    template <typename... Args>
+    __GBLIBCPP_CONSTEXPR
+    iterator emplace(const_iterator pos, Args&&... args)
+    {
+        node* nd = node_alloc_traits::allocate(m_alloc, 1);
+        node_alloc_traits::construct(m_alloc, nd, std::forward<Args>(args)...);
+
+        nd->next = pos.p;
+        nd->prev = pos.p->prev;
+        nd->next->prev = nd;
+        nd->prev->next = nd;
+
+        ++m_size;
+        return iterator { nd };
+    }
+    
+    explicit __GBLIBCPP_CONSTEXPR
+    list(const Allocator& alloc)
+        : m_head { }, m_size { }, m_alloc(alloc) { }
+
+    __GBLIBCPP_CONSTEXPR
+    list() : list(Allocator()) {}
+
+    __GBLIBCPP_CONSTEXPR
+    explicit list(size_type count,
+        const Allocator& alloc = Allocator())
+        : list(alloc)
+    {
+        while (count--)
+            emplace_back();
+    }
+
+    __GBLIBCPP_CONSTEXPR
+    list(size_type count, const T& value,
+        const Allocator& alloc = Allocator())
+        : list(alloc)
+    {
+        while (count--)
+            emplace_back(value);
+    }
+
+    template <typename InputIter>
+    __GBLIBCPP_CONSTEXPR
+    list(InputIter first, InputIter last,
+        const Allocator& alloc = Allocator())
+        : list(alloc) { insert(first, last); }
+
+    __GBLIBCPP_CONSTEXPR
+    list(const list& other, const Allocator& alloc)
+        : list(alloc)
+    {
+        // TODO: select_on_container_copy_construction
+        for (const auto& item : other)
+            emplace_back(item);
+    }
+    __GBLIBCPP_CONSTEXPR
+    list(const list& other)
+        : list(other,
+            alloc_traits::select_on_container_copy_construction(m_alloc))
+    { }
+
+    __GBLIBCPP_CONSTEXPR
+    list(list&& other)
+        : m_head { }, m_size { }
+        , m_alloc(std::move(other.m_alloc))
+    {
+        if (other.empty())
+            return;
+        _move_from(std::move(other));
+    }
+
+    __GBLIBCPP_CONSTEXPR
+    list(list&& other, const Allocator& alloc)
+        : m_head { }, m_size { }, m_alloc(alloc)
+    {
+        if (other.m_alloc != alloc) {
+            for (auto iter = other.begin(); iter != other.end(); ++iter)
+                emplace(cend(), std::move(*iter));
+            other.clear();
+            return;
+        }
+        // other.m_alloc == alloc
+        if (other.empty())
+            return;
+
+        _move_from(std::move(other));
+    }
+    
+    __GBLIBCPP_CONSTEXPR
+    ~list()
+    {
+        clear();
+        m_head.next = m_head.prev = nullptr;
+    }
+    
+    __GBLIBCPP_CONSTEXPR
+    list& operator=(const list& other)
+    {
+        // TODO: reuse memory if m_alloc == other.m_alloc
+        clear();
+
+        if constexpr (alloc_traits::
+            propagate_on_container_copy_assignment::value)
+            m_alloc = other.m_alloc;
+
+        for (const auto& item : other)
+            emplace_back(item);
+
+        return *this;
+    }
+    __GBLIBCPP_CONSTEXPR
+    list& operator=(list&& other)
+    {
+        if (alloc_traits::
+            propagate_on_container_move_assignment::value) {
+            clear();
+            m_alloc = std::move(other.m_alloc);
+
+            if (other.empty())
+                return *this;
+
+            _move_from(std::move(other));
+            return *this;
+        }
+
+        // TODO: reuse memory if m_alloc == other.m_alloc
+        clear();
+
+        if (m_alloc != other.m_alloc) {
+            for (auto iter = other.begin(); iter != other.end(); ++iter)
+                emplace(cend(), std::move(*iter));
+            other.clear();
+            return *this;
+        }
+
+        _move_from(std::move(other));
+        return *this;
+    }
+
+    // TODO: std::initializer_list
+    // list(std::initializer_list<Key> init,
+    //     const Allocator& alloc = Allocator());
+    //
+    // list& operator=(std::initializer_list<Key> ilist);
+    __GBLIBCPP_CONSTEXPR
+    reference front() { return *begin(); }
+    __GBLIBCPP_CONSTEXPR
+    const_reference front() const { return *cbegin(); }
+
+    __GBLIBCPP_CONSTEXPR
+    reference back() { return *--end(); }
+    __GBLIBCPP_CONSTEXPR
+    const_reference back() const { return *--cend(); }
+
+    __GBLIBCPP_CONSTEXPR
+    iterator insert(const_iterator pos, const T& value)
+    { return emplace(pos, value); }
+    __GBLIBCPP_CONSTEXPR
+    iterator insert(const_iterator pos, T&& value)
+    { return emplace(pos, std::move(value)); }
+
+    __GBLIBCPP_CONSTEXPR
+    iterator insert(const_iterator pos, size_type count, const T& value)
+    {
+        if (!(count--))
+            return pos;
+        auto ret = insert(pos, value);
+        while (count--)
+            insert(pos, value);
+        return ret;
+    }
+
+    template <typename InputIter>
+    __GBLIBCPP_CONSTEXPR
+    void insert(InputIter first, InputIter last)
+    {
+        for ( ; first != last; ++first)
+            emplace_back(*first);
+    }
+
+    template <typename... Args>
+    __GBLIBCPP_CONSTEXPR
+    reference emplace_back(Args&&... args)
+    { return *emplace(end(), std::forward<Args>(args)...); }
+
+    template <typename... Args>
+    __GBLIBCPP_CONSTEXPR
+    reference emplace_front(Args&&... args)
+    { return *emplace(begin(), std::forward<Args>(args)...); }
+
+    __GBLIBCPP_CONSTEXPR
+    void push_back(const T& value)
+    { emplace_back(value); }
+    __GBLIBCPP_CONSTEXPR
+    void push_back(T&& value)
+    { emplace_back(std::move(value)); }
+
+    __GBLIBCPP_CONSTEXPR
+    void push_front(const T& value)
+    { emplace_front(value); }
+    __GBLIBCPP_CONSTEXPR
+    void push_front(T&& value)
+    { emplace_front(std::move(value)); }
+
+    __GBLIBCPP_CONSTEXPR
+    void pop_back() { erase(--end()); }
+    __GBLIBCPP_CONSTEXPR
+    void pop_front() { erase(begin()); }
+
+    __GBLIBCPP_CONSTEXPR
+    iterator erase(const_iterator pos) noexcept
+    {
+        iterator ret { pos.p->next };
+
+        pos.p->next->prev = pos.p->prev;
+        pos.p->prev->next = pos.p->next;
+
+        node_alloc_traits::destroy(m_alloc, (node*)pos.p);
+        node_alloc_traits::deallocate(m_alloc, (node*)pos.p, 1);
+
+        --m_size;
+        return ret;
+    }
+
+    __GBLIBCPP_CONSTEXPR
+    iterator erase(const_iterator first, const_iterator last) noexcept
+    {
+        while (first != last)
+            first = erase(first);
+        return first;
+    }
+
+    __GBLIBCPP_CONSTEXPR
+    void clear() noexcept
+    {
+        for (auto iter = begin(); iter != end(); )
+            iter = erase(iter);
+    }
+
+    __GBLIBCPP_CONSTEXPR
+    size_type size() const noexcept { return m_size; }
+
+    __GBLIBCPP_CONSTEXPR
+    bool empty() const noexcept { return size() == 0; }
+
+    __GBLIBCPP_CONSTEXPR
+    void swap(list& other)
+    {
+        if constexpr (alloc_traits::propagate_on_container_swap::value)
+            std::swap(m_alloc, other.m_alloc);
+
+        std::swap(m_size, other.m_size);
+        std::swap(m_head, other.m_head);
+        std::swap(m_head.next->prev, other.m_head.next->prev);
+        std::swap(m_head.prev->next, other.m_head.prev->next);
+    }
+
+    __GBLIBCPP_CONSTEXPR
+    void resize(size_type count)
+    {
+        while (count > size())
+            emplace_back();
+        while (count < size())
+            pop_back();
+    }
+
+    __GBLIBCPP_CONSTEXPR
+    void resize(size_type count, const value_type& value)
+    {
+        while (count > size())
+            emplace_back(value);
+        while (count < size())
+            pop_back();
+    }
+
+    __GBLIBCPP_CONSTEXPR
+    size_type remove(const T& value)
+    {
+        size_type retval = 0;
+        for (auto iter = begin(); iter != end(); ) {
+            if (value != *iter) {
+                ++iter;
+                continue;
+            }
+            ++retval;
+            iter = erase(iter);
+        }
+        return retval;
+    }
+
+    template <typename UnaryPredicate>
+    __GBLIBCPP_CONSTEXPR
+    size_type remove_if(UnaryPredicate p)
+    {
+        size_type retval = 0;
+        for (auto iter = begin(); iter != end(); ) {
+            if (!p(*iter)) {
+                ++iter;
+                continue;
+            }
+            ++retval;
+            iter = erase(iter);
+        }
+        return retval;
+    }
+};
+
+template <typename T, typename Allocator>
+void swap(std::list<T, Allocator>& lhs,
+    std::list<T, Allocator>& rhs) { lhs.swap(rhs); }
+
+template <typename T, typename Allocator, typename U>
+typename std::list<T, Allocator>::size_type
+    erase(std::list<T, Allocator>& l, const U& value)
+{
+    return l.remove_if([&](auto& elem) { return elem == value; });
+}
+
+template <typename T, typename Allocator, typename Predicate>
+typename std::list<T, Allocator>::size_type
+    erase_if(std::list<T, Allocator>& l, Predicate p)
+{ return l.remove_if(p); }
+
+} // namespace std
+
+#endif

+ 173 - 15
gblibstdc++/include/memory

@@ -36,17 +36,149 @@ const T* addressof(const T&&) = delete;
 
 namespace __helpers {
 
+template <typename Ptr, typename = void>
+struct pointer_difference_type
+{ using type = std::ptrdiff_t; };
+
+template <typename Ptr>
+struct pointer_difference_type<Ptr,
+    std::void_t<typename Ptr::difference_type>>
+{ using type = typename Ptr::difference_type; };
+
+template <typename Ptr>
+using pointer_difference_type_t =
+    typename pointer_difference_type<Ptr>::type;
+
+template <typename Base, typename T>
+struct rebind;
+
+template <template <typename, typename...> typename Template,
+    typename NewType, typename OldType, typename... Args>
+struct rebind<Template<OldType, Args...>, NewType> {
+    using type = Template<NewType, Args...>;
+};
+
+template <typename Ptr, typename T, typename = void>
+struct try_rebind { using type = typename rebind<Ptr, T>::type; };
+
+template <typename Ptr, typename T>
+struct try_rebind<Ptr, T,
+    std::void_t<typename Ptr::template rebind<T>>> {
+    using type = typename Ptr::template rebind<T>;
+};
+
+template <typename Ptr, typename = void>
+struct pointer_element {};
+
+template <typename Ptr>
+struct pointer_element<Ptr, std::enable_if_t<
+    std::is_same_v<void, std::void_t<typename Ptr::element_type>>
+>> { using type = typename Ptr::element_type; };
+
+template <template <typename, typename...> typename Template,
+    typename T, typename... Args>
+struct pointer_element<Template<T, Args...>, void>
+{ using type = T; };
+
+template <typename Ptr, typename = void>
+struct pointer_traits_impl {};
+
+template <typename Ptr>
+struct pointer_traits_impl<Ptr,
+    std::void_t<typename pointer_element<Ptr>::type>> {
+    using pointer = Ptr;
+    using element_type = typename pointer_element<Ptr>::type;
+    using difference_type = pointer_difference_type_t<Ptr>;
+
+    template <typename U>
+    using rebind = typename try_rebind<Ptr, U>::type;
+
+    static pointer pointer_to(element_type& ref)
+    { return Ptr::pointer_to(ref); }
+};
+
+template <typename T>
+struct pointer_traits_impl<T*, void> {
+    using pointer = T*;
+    using element_type = T;
+    using difference_type = std::ptrdiff_t;
+
+    template <typename U>
+    using rebind = U*;
+
+    static pointer pointer_to(element_type& ref)
+    { return std::addressof(ref); }
+};
+
+} // namespace __helpers
+
+template <typename Ptr>
+struct pointer_traits : public __helpers::pointer_traits_impl<Ptr> {};
+
+namespace __helpers {
+
 template <typename Alloc, typename = void>
 struct allocator_pointer
-{ using type = std::add_pointer_t<typename Alloc::value_type>; };
+{ using type = typename Alloc::value_type*; };
+
 template <typename Alloc>
 struct allocator_pointer<Alloc,
     std::void_t<typename Alloc::pointer>>
 { using type = typename Alloc::pointer; };
+
 template <typename Alloc>
 using allocator_pointer_t =
     typename allocator_pointer<Alloc>::type;
 
+
+template <typename Alloc, typename Pointer, typename = void>
+struct allocator_const_pointer {
+    using type = typename std::pointer_traits<Pointer>::template
+        rebind<const typename Alloc::value_type>;
+};
+
+template <typename Alloc, typename Pointer>
+struct allocator_const_pointer<Alloc, Pointer,
+    std::void_t<typename Alloc::const_pointer>>
+{ using type = typename Alloc::const_pointer; };
+
+template <typename Alloc, typename Pointer>
+using allocator_const_pointer_t =
+    typename allocator_const_pointer<Alloc, Pointer>::type;
+
+
+template <typename Alloc, typename Pointer, typename = void>
+struct allocator_void_pointer {
+    using type = typename std::pointer_traits<Pointer>::template
+        rebind<void>;
+};
+
+template <typename Alloc, typename Pointer>
+struct allocator_void_pointer<Alloc, Pointer,
+    std::void_t<typename Alloc::void_pointer>>
+{ using type = typename Alloc::void_pointer; };
+
+template <typename Alloc, typename Pointer>
+using allocator_void_pointer_t =
+    typename allocator_void_pointer<Alloc, Pointer>::type;
+
+
+template <typename Alloc, typename Pointer, typename = void>
+struct allocator_const_void_pointer {
+    using type = typename std::pointer_traits<Pointer>::template
+        rebind<const void>;
+};
+
+template <typename Alloc, typename Pointer>
+struct allocator_const_void_pointer<Alloc, Pointer,
+    std::void_t<typename Alloc::const_void_pointer>>
+{ using type = typename Alloc::const_void_pointer; };
+
+template <typename Alloc, typename Pointer>
+using allocator_const_void_pointer_t =
+    typename allocator_const_void_pointer<Alloc, Pointer>::type;
+
+
 template <typename Alloc, typename = void>
 struct allocator_difference_type
 { using type = std::ptrdiff_t; };
@@ -102,11 +234,39 @@ template <typename Alloc>
 using allocator_prop_swap_t =
     typename allocator_prop_swap<Alloc>::type;
 
+template <typename Alloc, typename = void>
+struct allocator_select_on_copy {
+    static constexpr Alloc get(const Alloc& alloc)
+    { return alloc; }
+};
+
+template <typename Alloc>
+struct allocator_select_on_copy<Alloc, std::enable_if_t<
+    std::is_same_v<void, std::void_t<decltype(
+        std::declval<Alloc>().select_on_container_copy_construction()
+    )>> >> {
+    static constexpr Alloc get(const Alloc& alloc)
+    { return alloc.select_on_container_copy_construction(); }
+};
+
+template <typename Allocator, typename T, typename = void>
+struct allocator_rebind_type {
+    using type = typename rebind<Allocator, T>::type;
+};
+
+template <typename Allocator, typename T>
+struct allocator_rebind_type<Allocator, T, std::void_t<
+    typename Allocator::template rebind<T>::other
+>> {
+    using type = typename Allocator::template rebind<T>::other;
+};
+
 } // namespace __helpers
 
 template <typename T>
 struct allocator {
     using value_type = T;
+    using propagate_on_container_move_assignment = std::true_type;
 
     constexpr allocator() noexcept = default;
     constexpr allocator(const allocator& other) noexcept = default;
@@ -144,25 +304,18 @@ constexpr void destroy_at(T* p)
     p->~T();
 }
 
-namespace __helpers {
-
-template <typename Allocator, typename T>
-struct rebind;
-
-template <template <typename, typename...> typename Allocator,
-    typename NewType, typename OldType, typename... Args>
-struct rebind<Allocator<OldType, Args...>, NewType> {
-    using type = Allocator<NewType, Args...>;
-};
-
-} // namespace __helpers
-
 template <typename Allocator>
 struct allocator_traits {
     using allocator_type = Allocator;
     using value_type = typename Allocator::value_type;
     using pointer =
         __helpers::allocator_pointer_t<Allocator>;
+    using const_pointer =
+        __helpers::allocator_const_pointer_t<Allocator, pointer>;
+    using void_pointer =
+        __helpers::allocator_void_pointer_t<Allocator, pointer>;
+    using const_void_pointer =
+        __helpers::allocator_const_void_pointer_t<Allocator, pointer>;
     using difference_type =
         __helpers::allocator_difference_type_t<Allocator>;
     using size_type =
@@ -175,7 +328,8 @@ struct allocator_traits {
         __helpers::allocator_prop_swap_t<Allocator>;
 
     template <typename T>
-    using rebind_alloc = typename __helpers::rebind<Allocator, T>::type;
+    using rebind_alloc =
+        typename __helpers::allocator_rebind_type<Allocator, T>::type;
 
     [[nodiscard]] static constexpr pointer allocate(Allocator& alloc, size_type n)
     { return alloc.allocate(n); }
@@ -187,6 +341,10 @@ struct allocator_traits {
     template <typename T>
     static constexpr void destroy(Allocator&, T* p)
     { std::destroy_at(p); }
+
+    static constexpr Allocator
+        select_on_container_copy_construction(const Allocator& alloc)
+    { return __helpers::allocator_select_on_copy<Allocator>::get(alloc); }
 };
 
 } // namespace std