AUI Framework  master
Cross-platform base for C++ UI apps
Loading...
Searching...
No Matches
ASmallVector.h
    1/*
    2 * AUI Framework - Declarative UI toolkit for modern C++20
    3 * Copyright (C) 2020-2025 Alex2772 and Contributors
    4 *
    5 * SPDX-License-Identifier: MPL-2.0
    6 *
    7 * This Source Code Form is subject to the terms of the Mozilla Public
    8 * License, v. 2.0. If a copy of the MPL was not distributed with this
    9 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
   10 */
   11
   12#pragma once
   13
   14#include <cstddef>
   15#include <functional>
   16
   17#include "ADynamicVector.h"
   18#include "AStaticVector.h"
   19#include "AUI/Traits/concepts.h"
   20
   33template<typename StoredType, std::size_t StaticVectorSize, typename Allocator = std::allocator<StoredType>>
   34class ASmallVector {
   35protected:
   36    using self = ASmallVector;
   37    using super = self;
   38    using StaticVector = AStaticVector<StoredType, StaticVectorSize>;
   39    using DynamicVector = ADynamicVector<StoredType>;
   40
   41public:
   42    using iterator = StoredType*;
   43    using const_iterator = const StoredType*;
   44    using reference = StoredType&;
   45    using const_reference = const StoredType&;
   46    using value = StoredType;
   47
   48    ASmallVector() noexcept {
   49        new (&mBase.inplace) StaticVector();
   50    }
   51
   52    ASmallVector(ASmallVector&& rhs) noexcept {
   53        if (rhs.isInplaceAllocated()) {
   54            new (&mBase.inplace) StaticVector(*rhs.inplace());
   55        } else {
   56            new (&mBase.dynamic) DynamicVector(*rhs.dynamic());
   57        }
   58
   59        new (&rhs.mBase.inplace) StaticVector();
   60    }
   61
   62    ~ASmallVector() noexcept {
   63        deallocate();
   64    }
   65
   66    [[nodiscard]]
   67    constexpr StoredType* data() noexcept {
   68        return reinterpret_cast<StoredType*>(mBase.common.begin);
   69    }
   70    [[nodiscard]]
   71    constexpr const StoredType* data() const noexcept {
   72        return reinterpret_cast<const StoredType*>(mBase.common.begin);
   73    }
   74
   75    [[nodiscard]]
   76    constexpr iterator begin() noexcept {
   77        return mBase.common.begin;
   78    }
   79    [[nodiscard]]
   80    constexpr const_iterator begin() const noexcept {
   81        return mBase.common.begin;
   82    }
   83
   84    [[nodiscard]]
   85    constexpr iterator end() noexcept {
   86        return mBase.common.end;
   87    }
   88
   89    [[nodiscard]]
   90    constexpr const_iterator end() const noexcept {
   91        return mBase.common.end;
   92    }
   93
   94    [[nodiscard]]
   95    constexpr StoredType& front() noexcept {
   96        return *begin();
   97    }
   98
   99    [[nodiscard]]
  100    constexpr StoredType& back() noexcept {
  101        return *(begin() + (size() - 1));
  102    }
  103
  104    [[nodiscard]]
  105    constexpr const StoredType& front() const noexcept {
  106        return *begin();
  107    }
  108
  109    [[nodiscard]]
  110    constexpr const StoredType& back() const noexcept {
  111        return *(begin() + (size() - 1));
  112    }
  113
  114    constexpr void push_back(StoredType value) noexcept {
  115        insert(end(), std::move(value));
  116    }
  117
  118    constexpr void push_front(StoredType value) noexcept {
  119        insert(begin(), std::move(value));
  120    }
  121
  122    constexpr void pop_back() noexcept {
  123        AUI_ASSERTX(size() > 0, "ASmallVector is empty");
  124        erase(std::prev(end()));
  125    }
  126    constexpr void pop_front() noexcept {
  127        AUI_ASSERTX(size() > 0, "ASmallVector is empty");
  128        erase(begin());
  129    }
  130
  131    [[nodiscard]]
  132    constexpr StoredType& operator[](std::size_t index) noexcept {
  133        AUI_ASSERTX(index < size(), "out of bounds");
  134        return *(data() + index);
  135    }
  136
  137    [[nodiscard]]
  138    constexpr StoredType& operator[](std::size_t index) const noexcept {
  139        return const_cast<ASmallVector*>(this)->operator[](index);
  140    }
  141
  142    [[nodiscard]]
  143    constexpr bool empty() const noexcept {
  144        return begin() == end();
  145    }
  146
  147    constexpr void clear() noexcept {
  148        deallocate();
  149        new (&mBase.inplace) StaticVector();
  150    }
  151
  155    [[nodiscard]]
  156    std::size_t size() const noexcept {
  157        return std::distance(begin(), end());
  158    }
  159
  160
  161    [[nodiscard]]
  162    bool isInplaceAllocated() const noexcept {
  163        return size() <= StaticVectorSize;
  164    }
  165
  166
  167    template<typename OtherIterator>
  168    constexpr iterator insert(iterator at, OtherIterator begin, OtherIterator end) {
  169        AUI_ASSERT_MY_ITERATOR(at);
  170        auto distance = std::distance(begin, end);
  171
  172        if (distance + size() <= StaticVectorSize) {
  173            return inplace()->insert(at, begin, end);
  174        }
  175        if (!isInplaceAllocated()) {
  176            return dynamic()->insert(at, begin, end);
  177        }
  178
  179        // switch from static to dynamic
  180        DynamicVector temp;
  181        temp.reserve(aui::bit_ceil(distance + size()));
  182
  183        aui::container::vector_impl::insert_no_growth(temp.mEnd, temp.mEnd,
  184                                                      std::make_move_iterator(this->begin()), std::make_move_iterator(at));
  185
  186        auto result = aui::container::vector_impl::insert_no_growth(temp.mEnd, temp.mEnd,
  187                                                                    begin, end);
  188
  189        aui::container::vector_impl::insert_no_growth(temp.mEnd, temp.mEnd,
  190                                                      std::make_move_iterator(at), std::make_move_iterator(this->end()));
  191
  192        inplace()->~StaticVector();
  193        new (&mBase.dynamic) DynamicVector(std::move(temp));
  194
  195        return result;
  196    }
  197
  198    constexpr iterator erase(iterator begin, iterator end) noexcept {
  199        AUI_ASSERT_MY_ITERATOR(begin);
  200        AUI_ASSERT_MY_ITERATOR(end);
  201
  202        if (isInplaceAllocated()) {
  203            return inplace()->erase(begin, end);
  204        }
  205
  206        if (size() - std::distance(begin, end) > StaticVectorSize) {
  207            return dynamic()->erase(begin, end);
  208        }
  209
  210        auto index = std::distance(this->begin(), begin);
  211
  212        // switch from dynamic to static
  213        auto temp = std::move(*dynamic());
  214        temp.erase(begin, end);
  215        new (inplace()) StaticVector();
  216        inplace()->insert(inplace()->end(), std::make_move_iterator(temp.begin()), std::make_move_iterator(temp.end()));
  217        return this->begin() + index;
  218    }
  219
  220    constexpr iterator insert(iterator at, StoredType value) {
  221        AUI_ASSERT_MY_ITERATOR(at);
  222        return insert(at, std::make_move_iterator(&value), std::make_move_iterator(&value + 1));
  223    }
  224
  225    constexpr iterator erase(iterator at) {
  226        return erase(at, std::next(at));
  227    }
  228
  229
  230    // AUI extensions - see AVector for reference
  231
  232
  239    template<typename OtherContainer>
  240    iterator insertAll(const OtherContainer& c) noexcept {
  241        return insertAll(super::end(), c);
  242    }
  243
  244
  251    template<typename OtherContainer>
  252    iterator insertAll(OtherContainer&& c) noexcept {
  253        return insertAll(super::end(), std::forward<OtherContainer>(c));
  254    }
  255
  256
  264    template<typename OtherContainer>
  265    iterator insertAll(iterator at, const OtherContainer& c) noexcept {
  266        return super::insert(at, c.begin(), c.end());
  267    }
  268
  269
  277    template<typename OtherContainer>
  278    iterator insertAll(iterator at, OtherContainer&& c) noexcept {
  279        return super::insert(at, std::make_move_iterator(c.begin()), std::make_move_iterator(c.end()));
  280    }
  281
  282
  287    void removeAll(const StoredType& item) noexcept
  288    {
  289        aui::container::remove_all(*this, item);
  290    }
  291
  298    template<typename T, aui::mapper<const StoredType&, const T&> Projection>
  299    void removeAll(const T& item, Projection projection) noexcept
  300    {
  301        aui::container::remove_all(*this, item, projection);
  302    }
  303
  308    void removeFirst(const StoredType& item) noexcept
  309    {
  310        aui::container::remove_first(*this, item);
  311    }
  312
  313
  317    template<typename OtherContainer>
  318    bool isSubsetOf(const OtherContainer& c) const noexcept
  319    {
  320        return aui::container::is_subset(*this, c);
  321    }
  322
  326    bool contains(const StoredType& value) const noexcept {
  327        return aui::container::contains(*this, value);
  328    }
  329
  330    [[nodiscard]]
  331    std::size_t sizeInBytes() const noexcept {
  332        return super::size() * sizeof(StoredType);
  333    }
  334
  335    [[nodiscard]]
  336    StoredType& at(std::size_t index) {
  337        if (index >= super::size()) {
  338            aui::impl::outOfBoundsException();
  339        }
  340        return super::operator[](index);
  341    }
  342
  343    [[nodiscard]]
  344    const StoredType& at(std::size_t index) const {
  345        if (index >= super::size()) {
  346            aui::impl::outOfBoundsException();
  347        }
  348        return super::operator[](index);
  349    }
  350
  351
  357    self& operator<<(const StoredType& rhs) noexcept
  358    {
  359        super::push_back(rhs);
  360        return *this;
  361    }
  362
  368    self& operator<<(StoredType&& rhs) noexcept
  369    {
  370        super::push_back(std::forward<StoredType>(rhs));
  371        return *this;
  372    }
  373
  379    template<typename OtherContainer, std::enable_if_t<!std::is_convertible_v<OtherContainer, StoredType>, bool> = true>
  380    self& operator<<(const OtherContainer& c) noexcept
  381    {
  382        insertAll(c);
  383        return *this;
  384    }
  385
  391    template<typename OtherContainer, std::enable_if_t<!std::is_convertible_v<OtherContainer, StoredType>, bool> = true>
  392    self& operator<<(OtherContainer&& c) noexcept
  393    {
  394        insertAll(std::forward<OtherContainer>(c));
  395        return *this;
  396    }
  397
  398
  406    StoredType& first() noexcept
  407    {
  408        AUI_ASSERTX(!super::empty(), "empty container could not have the first element");
  409        return super::front();
  410    }
  411
  419    const StoredType& first() const noexcept
  420    {
  421        AUI_ASSERTX(!super::empty(), "empty container could not have the first element");
  422        return super::front();
  423    }
  424
  432    StoredType& last() noexcept
  433    {
  434        AUI_ASSERTX(!super::empty(), "empty container could not have the last element");
  435        return super::back();
  436    }
  437
  445    const StoredType& last() const noexcept
  446    {
  447        AUI_ASSERTX(!super::empty(), "empty container could not have the last element");
  448        return super::back();
  449    }
  450
  455    [[nodiscard]]
  456    AOptional<size_t> indexOf(const StoredType& value) const noexcept
  457    {
  458        return aui::container::index_of(*this, value);
  459    }
  460
  461
  462    void sort() noexcept {
  463        std::sort(super::begin(), super::end());
  464    }
  465
  466    template<typename Comparator>
  467    void sort(Comparator&& comparator) noexcept {
  468        std::sort(super::begin(), super::end(), std::forward<Comparator>(comparator));
  469    }
  470
  476    template<aui::predicate<StoredType> Predicate>
  477    StoredType* findIf(Predicate&& predicate) noexcept
  478    {
  479        if (auto i = std::find_if(super::begin(), super::end(), std::forward<Predicate>(predicate)); i != super::end()) {
  480            return &*i;
  481        }
  482        return nullptr;
  483    }
  484
  492    template<typename T, aui::mapper<const StoredType&, const T&> Projection>
  493    StoredType* findIf(const T& value, Projection&& projection) noexcept
  494    {
  495        if (auto i = std::find_if(super::begin(),
  496                                  super::end(),
  497                                  [&](const StoredType& s) { return value == std::invoke(projection, s); }
  498                                  ); i != super::end()) {
  499            return &*i;
  500        }
  501        return nullptr;
  502    }
  503
  512    void removeAt(size_t index) noexcept
  513    {
  514        aui::container::remove_at(*this, index);
  515    }
  516
  521    template<typename Predicate>
  522    void removeIf(Predicate&& predicate) noexcept
  523    {
  524        super::erase(std::remove_if(super::begin(), super::end(), std::forward<Predicate>(predicate)), super::end());
  525    }
  526
  527    ASet<StoredType> toSet() const noexcept {
  528        return ASet<StoredType>(super::begin(), super::end());
  529    }
  530
  537    template<aui::incrementable Iterator, aui::invocable<decltype(*std::declval<Iterator>())> UnaryOperation>
  538    static auto fromRange(aui::range<Iterator> range, UnaryOperation&& transformer) -> AVector<decltype(transformer(range.first()))> {
  539        AVector<decltype(transformer(range.first()))> result;
  540        result.reserve(range.size());
  541        std::transform(range.begin(), range.end(), std::back_inserter(result), std::forward<UnaryOperation>(transformer));
  542        return result;
  543    }
  544
  545    template<aui::invocable<const StoredType&> UnaryOperation>
  546    auto map(UnaryOperation&& transformer) const -> AVector<decltype(transformer(std::declval<StoredType>()))> {
  547        AVector<decltype(transformer(std::declval<StoredType>()))> result;
  548        result.reserve(super::size());
  549        std::transform(super::begin(), super::end(), std::back_inserter(result), std::forward<UnaryOperation>(transformer));
  550        return result;
  551    }
  552
  553    template<aui::invocable<const StoredType&> UnaryOperation>
  554    [[nodiscard]]
  555    auto toMap(UnaryOperation&& transformer) const -> AMap<decltype(transformer(std::declval<StoredType>()).first),
  556            decltype(transformer(std::declval<StoredType>()).second)> {
  557        return aui::container::to_map(super::begin(), super::end(), transformer);
  558    }
  559
  560    template<aui::invocable<StoredType&> UnaryOperation>
  561    [[nodiscard]]
  562    auto toMap(UnaryOperation&& transformer) -> AMap<decltype(transformer(std::declval<StoredType>()).first),
  563            decltype(transformer(std::declval<StoredType>()).second)> {
  564        return aui::container::to_map(super::begin(), super::end(), transformer);
  565    }
  566
  567    template<aui::predicate<const StoredType&> Predicate>
  568    self filter(Predicate&& predicate) {
  569        self result;
  570        for (const auto& element : *this) {
  571            if (predicate(element)) {
  572                result.push_back(element);
  573            }
  574        }
  575        return result;
  576    }
  577
  578
  579private:
  580    union {
  581        struct {
  582            StoredType* begin;
  583            StoredType* end;
  584        } common;
  585
  586        std::aligned_storage_t<sizeof(StaticVector), alignof(StaticVector)> inplace;
  587        std::aligned_storage_t<sizeof(DynamicVector), alignof(DynamicVector)> dynamic;
  588    } mBase;
  589
  590
  591    StaticVector* inplace() {
  592        AUI_ASSERT(isInplaceAllocated());
  593        return reinterpret_cast<StaticVector*>(&mBase.inplace);
  594    }
  595
  596    DynamicVector * dynamic() {
  597        AUI_ASSERT(!isInplaceAllocated());
  598        return reinterpret_cast<DynamicVector*>(&mBase.dynamic);
  599    }
  600
  601    void deallocate() {
  602        if (isInplaceAllocated()) {
  603            inplace()->~StaticVector();
  604        } else {
  605            dynamic()->~DynamicVector();
  606        }
  607    }
  608};
Vector implementation for ASmallVector.
Definition ADynamicVector.h:35
Utility wrapper implementing the stack-allocated (fast) optional idiom.
Definition AOptional.h:33
A std::set with AUI extensions.
Definition ASet.h:25
self & operator<<(const OtherContainer &c) noexcept
Definition ASmallVector.h:380
void removeAt(size_t index) noexcept
Definition ASmallVector.h:512
StoredType * findIf(const T &value, Projection &&projection) noexcept
Finds element by value.
Definition ASmallVector.h:493
StoredType & last() noexcept
Definition ASmallVector.h:432
void removeFirst(const StoredType &item) noexcept
Definition ASmallVector.h:308
bool contains(const StoredType &value) const noexcept
Definition ASmallVector.h:326
std::size_t size() const noexcept
Definition ASmallVector.h:156
iterator insertAll(iterator at, OtherContainer &&c) noexcept
Definition ASmallVector.h:278
const StoredType & last() const noexcept
Definition ASmallVector.h:445
void removeAll(const StoredType &item) noexcept
Definition ASmallVector.h:287
self & operator<<(const StoredType &rhs) noexcept
Definition ASmallVector.h:357
void removeIf(Predicate &&predicate) noexcept
Definition ASmallVector.h:522
StoredType * findIf(Predicate &&predicate) noexcept
Finds element by predicate.
Definition ASmallVector.h:477
self & operator<<(StoredType &&rhs) noexcept
Definition ASmallVector.h:368
iterator insertAll(const OtherContainer &c) noexcept
Definition ASmallVector.h:240
iterator insertAll(iterator at, const OtherContainer &c) noexcept
Definition ASmallVector.h:265
static auto fromRange(aui::range< Iterator > range, UnaryOperation &&transformer) -> AVector< decltype(transformer(range.first()))>
Constructs a new vector of transformed items of the range.
Definition ASmallVector.h:538
const StoredType & first() const noexcept
Definition ASmallVector.h:419
iterator insertAll(OtherContainer &&c) noexcept
Definition ASmallVector.h:252
AOptional< size_t > indexOf(const StoredType &value) const noexcept
Definition ASmallVector.h:456
bool isSubsetOf(const OtherContainer &c) const noexcept
Definition ASmallVector.h:318
StoredType & first() noexcept
Definition ASmallVector.h:406
self & operator<<(OtherContainer &&c) noexcept
Definition ASmallVector.h:392
void removeAll(const T &item, Projection projection) noexcept
Definition ASmallVector.h:299
Vector-like container up to maxSize elements inplace.
Definition AStaticVector.h:33
A std::vector with AUI extensions.
Definition AVector.h:39
bool is_subset(LContainer &l, RContainer &r) noexcept
Definition containers.h:205
AOptional< size_t > index_of(const Container &c, const typename Container::const_reference value) noexcept
Finds the index of the first occurrence of the value.
Definition containers.h:112
bool contains(const Container &c, const typename Container::const_reference value) noexcept
Definition containers.h:124
AOptional< std::size_t > remove_first(Container &container, typename Container::const_reference value) noexcept
Removes first occurrence of value.
Definition containers.h:172
auto to_map(Iterator begin, Iterator end, UnaryOperation &&transformer)
Transforms sequence to map.
Definition AMap.h:237
void remove_at(Container &c, size_t index) noexcept
Removes element at the specified index.
Definition containers.h:98
void remove_all(Container &container, typename Container::const_reference value) noexcept
Removes all occurrences of value.
Definition containers.h:143
#define AUI_ASSERT(condition)
Asserts that the passed condition evaluates to true.
Definition Assert.h:55
#define AUI_ASSERTX(condition, what)
Asserts that the passed condition evaluates to true. Adds extra message string.
Definition Assert.h:74
Definition iterators.h:50