LCOV - code coverage report
Current view: top level - structures - dynamic_array.h (source / functions) Coverage Total Hit
Test: hashbrowns Coverage Lines: 95.4 % 241 230
Test Date: 2026-04-10 19:00:18 Functions: 83.8 % 191 160
Legend: Lines: hit not hit

            Line data    Source code
       1              : #ifndef HASHBROWNS_DYNAMIC_ARRAY_H
       2              : #define HASHBROWNS_DYNAMIC_ARRAY_H
       3              : 
       4              : #include "core/data_structure.h"
       5              : #include "core/memory_manager.h"
       6              : #include <algorithm>
       7              : #include <initializer_list>
       8              : #include <iostream>
       9              : #include <iterator>
      10              : #include <stdexcept>
      11              : #include <utility>
      12              : 
      13              : namespace hashbrowns {
      14              : 
      15              : /**
      16              :  * @brief Growth strategy enumeration for dynamic array resizing
      17              :  */
      18              : enum class GrowthStrategy {
      19              :     MULTIPLICATIVE_2_0,  // Growth factor of 2.0
      20              :     MULTIPLICATIVE_1_5,  // Growth factor of 1.5
      21              :     FIBONACCI,           // Fibonacci sequence growth
      22              :     ADDITIVE             // Fixed increment growth
      23              : };
      24              : 
      25              : // Stream operator for GrowthStrategy enum
      26              : inline std::ostream& operator<<(std::ostream& os, GrowthStrategy strategy) {
      27              :     switch (strategy) {
      28              :         case GrowthStrategy::MULTIPLICATIVE_2_0:
      29              :             return os << "MULTIPLICATIVE_2_0";
      30              :         case GrowthStrategy::MULTIPLICATIVE_1_5:
      31              :             return os << "MULTIPLICATIVE_1_5";
      32              :         case GrowthStrategy::FIBONACCI:
      33              :             return os << "FIBONACCI";
      34              :         case GrowthStrategy::ADDITIVE:
      35              :             return os << "ADDITIVE";
      36              :         default:
      37              :             return os << "UNKNOWN";
      38              :     }
      39              : }
      40              : 
      41              : /**
      42              :  * @brief High-performance dynamic array with configurable growth strategies
      43              :  * 
      44              :  * This implementation showcases advanced C++ techniques:
      45              :  * - Template metaprogramming with SFINAE
      46              :  * - Custom allocator support
      47              :  * - STL-compatible iterators
      48              :  * - Exception safety guarantees
      49              :  * - Move semantics and perfect forwarding
      50              :  * - Multiple growth strategies for performance analysis
      51              :  * 
      52              :  * @tparam T Element type
      53              :  * @tparam Allocator Allocator type (defaults to TrackedAllocator)
      54              :  */
      55              : template<typename T, typename Allocator = TrackedAllocator<T>>
      56              : class DynamicArray : public DataStructure {
      57              : public:
      58              :     // Type aliases for STL compatibility
      59              :     using value_type = T;
      60              :     using allocator_type = Allocator;
      61              :     using size_type = std::size_t;
      62              :     using difference_type = std::ptrdiff_t;
      63              :     using reference = T&;
      64              :     using const_reference = const T&;
      65              :     using pointer = typename std::allocator_traits<Allocator>::pointer;
      66              :     using const_pointer = typename std::allocator_traits<Allocator>::const_pointer;
      67              : 
      68              :     // Forward declare iterator classes
      69              :     template<typename ValueType>
      70              :     class iterator_impl;
      71              :     
      72              :     using iterator = iterator_impl<T>;
      73              :     using const_iterator = iterator_impl<const T>;
      74              :     using reverse_iterator = std::reverse_iterator<iterator>;
      75              :     using const_reverse_iterator = std::reverse_iterator<const_iterator>;
      76              : 
      77              : private:
      78              :     pointer data_;
      79              :     size_type size_;
      80              :     size_type capacity_;
      81              :     GrowthStrategy growth_strategy_;
      82              :     Allocator allocator_;
      83              : 
      84              :     // Fibonacci growth state
      85              :     mutable size_type fib_prev_ = 1;
      86              :     mutable size_type fib_curr_ = 1;
      87              : 
      88              : public:
      89              :     /**
      90              :      * @brief Construct empty array with specified growth strategy
      91              :      * @param strategy Growth strategy to use for resizing
      92              :      * @param alloc Allocator instance
      93              :      */
      94           49 :     explicit DynamicArray(GrowthStrategy strategy = GrowthStrategy::MULTIPLICATIVE_2_0,
      95              :                          const Allocator& alloc = Allocator())
      96           54 :         : data_(nullptr), size_(0), capacity_(0), growth_strategy_(strategy), allocator_(alloc) {}
      97              : 
      98              :     /**
      99              :      * @brief Construct array with initial capacity
     100              :      * @param initial_capacity Initial capacity to reserve
     101              :      * @param strategy Growth strategy to use
     102              :      * @param alloc Allocator instance
     103              :      */
     104            1 :     explicit DynamicArray(size_type initial_capacity, 
     105              :                          GrowthStrategy strategy = GrowthStrategy::MULTIPLICATIVE_2_0,
     106              :                          const Allocator& alloc = Allocator())
     107            1 :         : data_(nullptr), size_(0), capacity_(0), growth_strategy_(strategy), allocator_(alloc) {
     108            1 :         reserve(initial_capacity);
     109            1 :     }
     110              : 
     111              :     /**
     112              :      * @brief Construct from initializer list
     113              :      * @param init Initializer list of elements
     114              :      * @param strategy Growth strategy to use
     115              :      * @param alloc Allocator instance
     116              :      */
     117            1 :     DynamicArray(std::initializer_list<T> init,
     118              :                 GrowthStrategy strategy = GrowthStrategy::MULTIPLICATIVE_2_0,
     119              :                 const Allocator& alloc = Allocator())
     120            1 :         : data_(nullptr), size_(0), capacity_(0), growth_strategy_(strategy), allocator_(alloc) {
     121            1 :         reserve(init.size());
     122            6 :         for (const auto& item : init) {
     123            5 :             push_back(item);
     124              :         }
     125            1 :     }
     126              : 
     127              :     /**
     128              :      * @brief Copy constructor
     129              :      */
     130            5 :     DynamicArray(const DynamicArray& other)
     131            5 :         : data_(nullptr), size_(0), capacity_(0), growth_strategy_(other.growth_strategy_), 
     132           14 :           allocator_(std::allocator_traits<Allocator>::select_on_container_copy_construction(other.allocator_)) {
     133            5 :         reserve(other.size_);
     134          407 :         for (size_type i = 0; i < other.size_; ++i) {
     135          402 :             push_back(other.data_[i]);
     136              :         }
     137            5 :     }
     138              : 
     139              :     /**
     140              :      * @brief Move constructor
     141              :      */
     142            4 :     DynamicArray(DynamicArray&& other) noexcept
     143            4 :         : data_(other.data_), size_(other.size_), capacity_(other.capacity_),
     144            4 :           growth_strategy_(other.growth_strategy_), allocator_(std::move(other.allocator_)),
     145            8 :           fib_prev_(other.fib_prev_), fib_curr_(other.fib_curr_) {
     146            4 :         other.data_ = nullptr;
     147            4 :         other.size_ = 0;
     148            4 :         other.capacity_ = 0;
     149            4 :     }
     150              : 
     151              :     /**
     152              :      * @brief Destructor
     153              :      */
     154           90 :     ~DynamicArray() {
     155           60 :         clear();
     156           60 :         deallocate();
     157          163 :     }
     158              : 
     159              :     /**
     160              :      * @brief Copy assignment operator
     161              :      */
     162            1 :     DynamicArray& operator=(const DynamicArray& other) {
     163            1 :         if (this != &other) {
     164            1 :             DynamicArray temp(other);
     165            1 :             swap(temp);
     166            1 :         }
     167            1 :         return *this;
     168              :     }
     169              : 
     170              :     /**
     171              :      * @brief Move assignment operator
     172              :      */
     173            1 :     DynamicArray& operator=(DynamicArray&& other) noexcept {
     174            1 :         if (this != &other) {
     175            1 :             clear();
     176            1 :             deallocate();
     177              :             
     178            1 :             data_ = other.data_;
     179            1 :             size_ = other.size_;
     180            1 :             capacity_ = other.capacity_;
     181            1 :             growth_strategy_ = other.growth_strategy_;
     182            1 :             allocator_ = std::move(other.allocator_);
     183            1 :             fib_prev_ = other.fib_prev_;
     184            1 :             fib_curr_ = other.fib_curr_;
     185              :             
     186            1 :             other.data_ = nullptr;
     187            1 :             other.size_ = 0;
     188            1 :             other.capacity_ = 0;
     189              :         }
     190            1 :         return *this;
     191              :     }
     192              : 
     193              :     // DataStructure interface implementation
     194          746 :     void insert(int key, const std::string& value) override {
     195              :         if constexpr (std::is_same_v<T, std::pair<int, std::string>>) {
     196          746 :             push_back(std::make_pair(key, value));
     197              :         } else {
     198            0 :             throw std::invalid_argument("DynamicArray<T> does not support key-value insertion unless T is std::pair<int, std::string>");
     199              :         }
     200          746 :     }
     201              : 
     202          522 :     bool search(int key, std::string& value) const override {
     203              :         if constexpr (std::is_same_v<T, std::pair<int, std::string>>) {
     204        11726 :             auto it = std::find_if(begin(), end(), [key](const auto& pair) {
     205        11204 :                 return pair.first == key;
     206              :             });
     207          522 :             if (it != end()) {
     208          521 :                 value = it->second;
     209          521 :                 return true;
     210              :             }
     211              :         }
     212            1 :         return false;
     213              :     }
     214              : 
     215          521 :     bool remove(int key) override {
     216              :         if constexpr (std::is_same_v<T, std::pair<int, std::string>>) {
     217         3284 :             auto it = std::find_if(begin(), end(), [key](const auto& pair) {
     218         2763 :                 return pair.first == key;
     219              :             });
     220          521 :             if (it != end()) {
     221          521 :                 erase(it);
     222          521 :                 return true;
     223              :             }
     224              :         }
     225            0 :         return false;
     226              :     }
     227              : 
     228          450 :     size_t size() const override { return size_; }
     229            6 :     bool empty() const override { return size_ == 0; }
     230              :     
     231           62 :     void clear() override {
     232         1221 :         for (size_type i = 0; i < size_; ++i) {
     233         1159 :             std::allocator_traits<Allocator>::destroy(allocator_, &data_[i]);
     234              :         }
     235           62 :         size_ = 0;
     236           62 :     }
     237              : 
     238            7 :     size_t memory_usage() const override {
     239            7 :         return capacity_ * sizeof(T) + sizeof(*this);
     240              :     }
     241              : 
     242            3 :     std::string type_name() const override { return "DynamicArray"; }
     243            3 :     std::string insert_complexity() const override { return "O(1) amortized"; }
     244            3 :     std::string search_complexity() const override { return "O(n)"; }
     245            3 :     std::string remove_complexity() const override { return "O(n)"; }
     246              : 
     247              :     // Array-specific interface
     248          912 :     void push_back(const T& value) {
     249          912 :         emplace_back(value);
     250          912 :     }
     251              : 
     252          767 :     void push_back(T&& value) {
     253          767 :         emplace_back(std::move(value));
     254          767 :     }
     255              : 
     256              :     template<typename... Args>
     257         1679 :     reference emplace_back(Args&&... args) {
     258         1679 :         if (size_ == capacity_) {
     259          212 :             grow();
     260              :         }
     261         1679 :         std::allocator_traits<Allocator>::construct(allocator_, &data_[size_], std::forward<Args>(args)...);
     262         1679 :         return data_[size_++];
     263              :     }
     264              : 
     265            3 :     void pop_back() {
     266            3 :         if (empty()) {
     267            1 :             throw std::out_of_range("pop_back() called on empty array");
     268              :         }
     269            2 :         --size_;
     270            2 :         std::allocator_traits<Allocator>::destroy(allocator_, &data_[size_]);
     271            2 :     }
     272              : 
     273            2 :     reference at(size_type index) {
     274            2 :         if (index >= size_) {
     275            1 :             throw std::out_of_range("Index out of range");
     276              :         }
     277            1 :         return data_[index];
     278              :     }
     279              : 
     280            1 :     const_reference at(size_type index) const {
     281            1 :         if (index >= size_) {
     282            0 :             throw std::out_of_range("Index out of range");
     283              :         }
     284            1 :         return data_[index];
     285              :     }
     286              : 
     287         1318 :     reference operator[](size_type index) { return data_[index]; }
     288            1 :     const_reference operator[](size_type index) const { return data_[index]; }
     289              : 
     290            1 :     reference front() { return data_[0]; }
     291            1 :     const_reference front() const { return data_[0]; }
     292            1 :     reference back() { return data_[size_ - 1]; }
     293            1 :     const_reference back() const { return data_[size_ - 1]; }
     294              : 
     295            1 :     T* data() noexcept { return data_; }
     296            1 :     const T* data() const noexcept { return data_; }
     297              : 
     298            4 :     size_type capacity() const noexcept { return capacity_; }
     299              :     
     300           10 :     void reserve(size_type new_capacity) {
     301           10 :         if (new_capacity > capacity_) {
     302           10 :             reallocate(new_capacity);
     303              :         }
     304           10 :     }
     305              : 
     306            1 :     void shrink_to_fit() {
     307            1 :         if (size_ < capacity_) {
     308            1 :             reallocate(size_);
     309              :         }
     310            1 :     }
     311              : 
     312            1 :     void resize(size_type new_size) {
     313            1 :         if (new_size > capacity_) {
     314            1 :             reserve(new_size);
     315              :         }
     316              :         
     317            1 :         if (new_size > size_) {
     318            9 :             for (size_type i = size_; i < new_size; ++i) {
     319            8 :                 std::allocator_traits<Allocator>::construct(allocator_, &data_[i]);
     320              :             }
     321            0 :         } else if (new_size < size_) {
     322            0 :             for (size_type i = new_size; i < size_; ++i) {
     323            0 :                 std::allocator_traits<Allocator>::destroy(allocator_, &data_[i]);
     324              :             }
     325              :         }
     326            1 :         size_ = new_size;
     327            1 :     }
     328              : 
     329            1 :     void resize(size_type new_size, const T& value) {
     330            1 :         if (new_size > capacity_) {
     331            0 :             reserve(new_size);
     332              :         }
     333              :         
     334            1 :         if (new_size > size_) {
     335            0 :             for (size_type i = size_; i < new_size; ++i) {
     336            0 :                 std::allocator_traits<Allocator>::construct(allocator_, &data_[i], value);
     337              :             }
     338            1 :         } else if (new_size < size_) {
     339            6 :             for (size_type i = new_size; i < size_; ++i) {
     340            5 :                 std::allocator_traits<Allocator>::destroy(allocator_, &data_[i]);
     341              :             }
     342              :         }
     343            1 :         size_ = new_size;
     344            1 :     }
     345              : 
     346              :     // Iterator interface
     347          537 :     iterator begin() { return iterator(data_); }
     348          538 :     const_iterator begin() const { return const_iterator(data_); }
     349          521 :     const_iterator cbegin() const { return const_iterator(data_); }
     350              :     
     351         1055 :     iterator end() { return iterator(data_ + size_); }
     352         1048 :     const_iterator end() const { return const_iterator(data_ + size_); }
     353            1 :     const_iterator cend() const { return const_iterator(data_ + size_); }
     354              :     
     355            1 :     reverse_iterator rbegin() { return reverse_iterator(end()); }
     356            1 :     const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
     357            1 :     const_reverse_iterator crbegin() const { return const_reverse_iterator(end()); }
     358              :     
     359            6 :     reverse_iterator rend() { return reverse_iterator(begin()); }
     360            6 :     const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
     361            6 :     const_reverse_iterator crend() const { return const_reverse_iterator(begin()); }
     362              : 
     363          521 :     iterator erase(const_iterator pos) {
     364          521 :         return erase(pos, pos + 1);
     365              :     }
     366              : 
     367          521 :     iterator erase(const_iterator first, const_iterator last) {
     368          521 :         auto diff = last - first;
     369          521 :         auto index = first - cbegin();
     370              :         
     371              :         // Move elements
     372         8961 :         for (size_type i = index; i + diff < size_; ++i) {
     373         8440 :             data_[i] = std::move(data_[i + diff]);
     374              :         }
     375              :         
     376              :         // Destroy moved-from elements
     377         1042 :         for (size_type i = size_ - diff; i < size_; ++i) {
     378          521 :             std::allocator_traits<Allocator>::destroy(allocator_, &data_[i]);
     379              :         }
     380              :         
     381          521 :         size_ -= diff;
     382          521 :         return iterator(data_ + index);
     383              :     }
     384              : 
     385              :     // Utility methods
     386            2 :     void swap(DynamicArray& other) noexcept {
     387            2 :         std::swap(data_, other.data_);
     388            2 :         std::swap(size_, other.size_);
     389            2 :         std::swap(capacity_, other.capacity_);
     390            2 :         std::swap(growth_strategy_, other.growth_strategy_);
     391            2 :         std::swap(allocator_, other.allocator_);
     392            2 :         std::swap(fib_prev_, other.fib_prev_);
     393            2 :         std::swap(fib_curr_, other.fib_curr_);
     394            2 :     }
     395              : 
     396            2 :     GrowthStrategy growth_strategy() const { return growth_strategy_; }
     397            1 :     void set_growth_strategy(GrowthStrategy strategy) { growth_strategy_ = strategy; }
     398              : 
     399              : private:
     400          212 :     void grow() {
     401          212 :         size_type new_capacity = calculate_growth();
     402          212 :         reallocate(new_capacity);
     403          212 :     }
     404              : 
     405          212 :     size_type calculate_growth() const {
     406          212 :         if (capacity_ == 0) return 1;
     407              :         
     408          174 :         switch (growth_strategy_) {
     409           11 :             case GrowthStrategy::MULTIPLICATIVE_1_5: {
     410           11 :                 size_type new_cap = capacity_ + (capacity_ + 1) / 2; // Ceiling division
     411           11 :                 return std::max(new_cap, capacity_ + 1);
     412              :             }
     413          143 :             case GrowthStrategy::MULTIPLICATIVE_2_0:
     414          143 :                 return capacity_ * 2;
     415           10 :             case GrowthStrategy::FIBONACCI: {
     416           10 :                 size_type next_fib = fib_prev_ + fib_curr_;
     417           10 :                 fib_prev_ = fib_curr_;
     418           10 :                 fib_curr_ = next_fib;
     419           10 :                 return std::max(capacity_ + 1, next_fib);
     420              :             }
     421           10 :             case GrowthStrategy::ADDITIVE:
     422           10 :                 return capacity_ + 10; // Fixed increment for testing
     423            0 :             default:
     424            0 :                 return capacity_ * 2;
     425              :         }
     426              :     }
     427              : 
     428          223 :     void reallocate(size_type new_capacity) {
     429          223 :         pointer new_data = std::allocator_traits<Allocator>::allocate(allocator_, new_capacity);
     430              :         
     431              :         // Move construct existing elements
     432         2297 :         for (size_type i = 0; i < size_; ++i) {
     433         2074 :             std::allocator_traits<Allocator>::construct(allocator_, &new_data[i], std::move(data_[i]));
     434         2074 :             std::allocator_traits<Allocator>::destroy(allocator_, &data_[i]);
     435              :         }
     436              :         
     437          223 :         deallocate();
     438          223 :         data_ = new_data;
     439          223 :         capacity_ = new_capacity;
     440          223 :     }
     441              : 
     442          284 :     void deallocate() {
     443          284 :         if (data_) {
     444          223 :             std::allocator_traits<Allocator>::deallocate(allocator_, data_, capacity_);
     445          223 :             data_ = nullptr;
     446              :         }
     447          284 :     }
     448              : };
     449              : 
     450              : /**
     451              :  * @brief STL-compatible iterator implementation
     452              :  */
     453              : template<typename T, typename Allocator>
     454              : template<typename ValueType>
     455              : class DynamicArray<T, Allocator>::iterator_impl {
     456              : public:
     457              :     using iterator_category = std::random_access_iterator_tag;
     458              :     using value_type = std::remove_cv_t<ValueType>;
     459              :     using difference_type = std::ptrdiff_t;
     460              :     using pointer = ValueType*;
     461              :     using reference = ValueType&;
     462              : 
     463              : private:
     464              :     pointer ptr_;
     465              : 
     466              : public:
     467              :     iterator_impl() : ptr_(nullptr) {}
     468         4872 :     explicit iterator_impl(pointer ptr) : ptr_(ptr) {}
     469              :     
     470              :     // Copy constructor from non-const to const
     471              :     template<typename U, typename = std::enable_if_t<std::is_same_v<U, value_type> && std::is_const_v<ValueType>>>
     472          521 :     iterator_impl(const iterator_impl<U>& other) : ptr_(other.get_ptr()) {}
     473              : 
     474        19437 :     reference operator*() const { return *ptr_; }
     475          521 :     pointer operator->() const { return ptr_; }
     476              :     
     477        13904 :     iterator_impl& operator++() { ++ptr_; return *this; }
     478            1 :     iterator_impl operator++(int) { iterator_impl temp(*this); ++ptr_; return temp; }
     479              :     
     480         1432 :     iterator_impl& operator--() { --ptr_; return *this; }
     481            1 :     iterator_impl operator--(int) { iterator_impl temp(*this); --ptr_; return temp; }
     482              :     
     483            1 :     iterator_impl& operator+=(difference_type n) { ptr_ += n; return *this; }
     484            1 :     iterator_impl& operator-=(difference_type n) { ptr_ -= n; return *this; }
     485              :     
     486          622 :     iterator_impl operator+(difference_type n) const { return iterator_impl(ptr_ + n); }
     487           29 :     iterator_impl operator-(difference_type n) const { return iterator_impl(ptr_ - n); }
     488              :     
     489         2264 :     difference_type operator-(const iterator_impl& other) const { return ptr_ - other.ptr_; }
     490              :     
     491            1 :     reference operator[](difference_type n) const { return ptr_[n]; }
     492              :     
     493           23 :     bool operator==(const iterator_impl& other) const { return ptr_ == other.ptr_; }
     494         1470 :     bool operator!=(const iterator_impl& other) const { return ptr_ != other.ptr_; }
     495          225 :     bool operator<(const iterator_impl& other) const { return ptr_ < other.ptr_; }
     496            1 :     bool operator<=(const iterator_impl& other) const { return ptr_ <= other.ptr_; }
     497            1 :     bool operator>(const iterator_impl& other) const { return ptr_ > other.ptr_; }
     498            1 :     bool operator>=(const iterator_impl& other) const { return ptr_ >= other.ptr_; }
     499              : 
     500              :     friend iterator_impl operator+(difference_type n, const iterator_impl& it) {
     501              :         return iterator_impl(it.ptr_ + n);
     502              :     }
     503              : 
     504              :     // Allow access to ptr_ for conversions
     505          521 :     pointer get_ptr() const { return ptr_; }
     506              : };
     507              : 
     508              : // Non-member functions
     509              : template<typename T, typename Allocator>
     510              : void swap(DynamicArray<T, Allocator>& lhs, DynamicArray<T, Allocator>& rhs) noexcept {
     511              :     lhs.swap(rhs);
     512              : }
     513              : 
     514              : template<typename T, typename Allocator>
     515            3 : bool operator==(const DynamicArray<T, Allocator>& lhs, const DynamicArray<T, Allocator>& rhs) {
     516            3 :     return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
     517              : }
     518              : 
     519              : template<typename T, typename Allocator>
     520            2 : bool operator!=(const DynamicArray<T, Allocator>& lhs, const DynamicArray<T, Allocator>& rhs) {
     521            2 :     return !(lhs == rhs);
     522              : }
     523              : 
     524              : } // namespace hashbrowns
     525              : 
     526              : #endif // HASHBROWNS_DYNAMIC_ARRAY_H
        

Generated by: LCOV version 2.0-1