| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- /* Copyright The kNet Project.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License. */
- #pragma once
- /** @file Array.h
- @brief The Array<T> template class. Provides a std::vector-like array, but with instance-based memory allocation. */
- #include "SharedPtr.h"
- #include "Allocator.h"
- #include "StdCMallocHeap.h"
- namespace kNet
- {
- template<typename T, typename AllocT = StdCAlloc>
- class Array;
- /// Sequential iterator for Array.
- template<typename T, typename AllocT>
- class ArrayIterator
- {
- public:
- ArrayIterator(const ArrayIterator &rhs):ptr(rhs.ptr), container(rhs.container) {}
- ~ArrayIterator() {}
- inline void operator++()
- {
- assert(ptr <= container->endptr());
- assert(ptr >= container->beginptr());
- ++ptr;
- }
- inline void operator--()
- {
- assert(ptr <= container->endptr());
- assert(ptr >= container->beginptr());
- --ptr;
- }
- inline operator T*()
- {
- assert(ptr >= container->beginptr());
- assert(ptr <= container->endptr());
- return ptr;
- }
- inline operator const T*() const
- {
- assert(ptr >= container->beginptr());
- assert(ptr <= container->endptr());
- return ptr;
- }
- inline const T &operator*() const
- {
- assert(ptr >= container->beginptr());
- assert(ptr <= container->endptr());
- return *ptr;
- }
- inline T &operator*()
- {
- assert(ptr >= container->beginptr());
- assert(ptr <= container->endptr());
- return *ptr;
- }
- inline bool operator<(const ArrayIterator &rhs) const { return ptr < rhs.ptr; }
- inline bool operator<=(const ArrayIterator &rhs) const { return ptr <= rhs.ptr; }
- inline bool operator==(const ArrayIterator &rhs) const { return ptr == rhs.ptr; }
- inline bool operator!=(const ArrayIterator &rhs) const { return ptr != rhs.ptr; }
- inline bool operator>(const ArrayIterator &rhs) const { return ptr > rhs.ptr; }
- inline bool operator>=(const ArrayIterator &rhs) const { return ptr >= rhs.ptr; }
- private:
- T *ptr;
- Array<T, AllocT> *container;
- ArrayIterator(Array<T, AllocT> &container_, T *ptr_)
- :container(&container_), ptr(ptr_)
- {
- assert(ptr >= container->beginptr());
- assert(ptr <= container->endptr());
- }
- friend class Array<T, AllocT>;
- };
- /// std::vector -equivalent.
- template<typename T, typename AllocT>
- class Array
- {
- public:
- typedef ArrayIterator<T, AllocT> iterator;
- Array(AllocT *allocator_ = 0)
- :data(0), allocator(allocator_), cap(0), used(0)
- { }
- Array(const Array &rhs)
- :data(0), allocator(rhs.allocator), cap(0), used(0)
- {
- reserve(rhs.cap);
- for(size_t i = 0; i < rhs.size(); ++i)
- data[i] = rhs.data[i];
- used = rhs.used;
- }
- ~Array()
- {
- for(size_t i = 0; i < used; ++i)
- data[i] = T();
- DeleteArray(data, allocator);
- }
- const T &front() const { assert(used > 0); return data[0]; }
- const T &back() const { assert(used > 0); return data[used-1]; }
- T &front() { assert(used > 0); return data[0]; }
- T &back() { assert(used > 0); return data[used-1]; }
- /// @return The number of elements in the container.
- size_t size() const { return used; }
- /// @return The number of elements that the container can hold without needing to reserve more.
- size_t capacity() const { return cap; }
- /// Sets the allocator used by this Array.
- void set_allocator(AllocT *allocator_)
- {
- assert(cap == 0);
- assert(data == 0);
- allocator = allocator_;
- }
- /// @return The allocator used by this Array, or 0 if none was set.
- AllocT *get_allocator() const { return allocator; }
- const T *beginptr() const { return data; } ///< Pointer to the first element.
- const T *endptr() const { return data + used; } ///< Pointer to one past the last used element.
- const T *capptr() const { return data + cap; } ///< Pointer to the first element beyond allocated memory.
- /// @return An iterator to start of the data.
- iterator begin() { return iterator(*this, data); }
- /// @return A const pointer to start of the data.
- // const *begin() const { return data; }
- /// @return An iterator to past the last element in the array.
- iterator end() { return iterator(*this, data + used); }
- /// @return A const pointer to past the last element in the array.
- const T *end() const { return data + used ; }
- void insert(const T &val) { insert(used, val); }
- /// Inserts an element before the ith element of the Array. Running time is O(n).
- void insert(int i, const T &val)
- {
- if (used >= cap)
- reserve(cap*2);
- // Push all data up one index to make room for the new element.
- for(int j = used+1; j > i; --j)
- data[j] = data[j-1];
- // Add the new element.
- data[i] = val;
- ++used;
- }
- void InsertMultiple(int i, const T &val, int numTimes)
- {
- if (used + numTimes > cap)
- reserve((cap+numTimes)*2);
- // Push all data up numTimes slots to make room for the new element.
- for(int j = used+numTimes; j > i+numTimes; --j)
- data[j] = data[j-numTimes];
- // Add the new element.
- for(int j = i; j < i + numTimes; ++j)
- data[j] = val;
-
- used += numTimes;
- }
- /// Returns true if the members of this array all have distinct values. Tests each pair and runs in O(n^2).
- bool MembersUnique() const
- {
- for(size_t i = 0; i < used; ++i)
- for(size_t j = i+1; j < used; ++j)
- if (data[i] == data[j])
- return false;
- return true;
- }
- /// Inserts an element to the front of the Array. Running time is O(n).
- void push_front(const T &val) { insert(0, val); }
- /// Inserts the given element to back of the Array. Running time is O(1).
- void push_back(const T &val)
- {
- if (used >= cap)
- reserve(cap*2);
- data[used] = val;
- ++used;
- }
- /// Inserts the given element to back of the Array, but without doing bounds checking.
- /// Call this function only if you know there is enough capacity in the Array.
- void push_back_unsafe(const T &val)
- {
- assert(used < cap);
- data[used++] = val;
- }
- /// Inserts an uninitialized value to the back of the Array, and returns a reference to it.
- /// This function was implemented after profiling a code generation issue with VS2008 that
- /// resulted in suboptimal performance.
- T &push_back_unsafe_pod()
- {
- assert(used < cap);
- return data[used++];
- }
- /// Inserts a default-constructed element to back of the Array. Running time is O(1).
- T &push_back()
- {
- if (used >= cap)
- reserve(cap*2);
- data[used] = T();
- return data[used++];
- }
- /// Erases a range of elements, excluding the element at index end. That is, the interval [start, end[.
- /// Running time is O(n).
- void erase(int start, int end)
- {
- assert(start >= 0);
- assert(end >= 0);
- assert(start <= end);
- assert(end <= used);
- if (start == end)
- return;
- const int items = end-start;
- for(int i = start; i < used-items; ++i)
- data[i] = data[i+items];
- for(int i = used-items; i < used; ++i)
- data[i] = T(); ///< \todo Placement new/delete instead of this crap.
- used -= items;
- }
- /// Removes the ith element from the array. Running time is O(n).
- void erase(int i)
- {
- assert(i >= 0);
- erase((size_t)i);
- }
- /// Removes the ith element from the array. Running time is O(n).
- void erase(size_t i)
- {
- assert(i < used);
- for(size_t j = i; j+1 < used; ++j)
- data[j] = data[j+1];
- data[used-1] = T(); ///< \todo Placement new/delete instead of this crap.
- --used;
- }
- /// Clears the last element of the array. Running time is O(1).
- void pop_back()
- {
- assert(used > 0);
- data[used-1] = T(); ///< \todo Placement new/delete instead of this crap.
- --used; /// \note Doesn't call destructor for the popped object.
- }
- /// Clears the last element of the array. Running time is O(1). Use for pod data that don't need
- /// to be cleared when uninitializing.
- void pop_back_pod()
- {
- assert(used > 0);
- --used; /// \note Doesn't call destructor for the popped object.
- }
- /// Removes all elements in the array. Running time is O(n).
- void clear()
- {
- for(size_t i = 0; i < used; ++i)
- data[i] = T(); ///< \todo Placement new/delete instead of this crap.
- used = 0;
- }
- /// Clears the whole Array by simply marking the size to 0. Running time is O(1).
- /// Only call this function if the element type is a POD that does not need its dtor
- /// to be called for cleanup.
- void clear_pod()
- {
- used = 0;
- }
- /// Enlarges the allocated memory area if it's not big enough to hold newSize elements.
- void reserve(size_t newSize)
- {
- newSize = newSize < initialSize ? initialSize : newSize;
- // only to enlarge.
- if (newSize < cap)
- return;
- const size_t alignment = 4; ///\ todo
- T *newData = NewArray<T, AllocT>(newSize, allocator, alignment);
- // For POD types, this is more efficient, but need some kind of trait mechanism to detect whether T is POD or not.
- // memcpy(newData, data, sizeof(T)*used);
- for(size_t i = 0; i < used; ++i)
- newData[i] = data[i];
- DeleteArray(data, allocator);
- data = newData;
- cap = newSize;
- }
- const T &operator[](int index) const
- {
- assert(index >= 0);
- assert((size_t)index < used);
- return data[index];
- }
- T &operator[](int index)
- {
- assert(index >= 0);
- assert((size_t)index < used);
- return data[index];
- }
- Array<T, AllocT> &operator =(const Array<T, AllocT> &rhs)
- {
- if (this == &rhs)
- return *this;
- reserve(rhs.size());
- for(size_t i = 0; i < rhs.size(); ++i)
- data[i] = rhs.data[i];
- used = rhs.size();
- return *this;
- }
- private:
- T *data;
- AllocT *allocator;
- size_t cap; ///< The number of elements allocated in the data.
- size_t used; ///< The number of elements actually in use.
- static const size_t initialSize = 32;
- };
- } // ~kNet
|