285 lines
12 KiB
C
285 lines
12 KiB
C
|
|
// Copyright (c) 2016-2018 Easy2D - Nomango
|
||
|
|
//
|
||
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
|
// of this software and associated documentation files (the "Software"), to deal
|
||
|
|
// in the Software without restriction, including without limitation the rights
|
||
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
|
// copies of the Software, and to permit persons to whom the Software is
|
||
|
|
// furnished to do so, subject to the following conditions:
|
||
|
|
//
|
||
|
|
// The above copyright notice and this permission notice shall be included in
|
||
|
|
// all copies or substantial portions of the Software.
|
||
|
|
//
|
||
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
|
|
// THE SOFTWARE.
|
||
|
|
|
||
|
|
#pragma once
|
||
|
|
#include <memory>
|
||
|
|
#include <type_traits>
|
||
|
|
#include <exception>
|
||
|
|
|
||
|
|
namespace easy2d
|
||
|
|
{
|
||
|
|
//
|
||
|
|
// Array
|
||
|
|
// Lightweight std::vector<>-like class
|
||
|
|
//
|
||
|
|
template<typename _Ty, typename _Alloc, typename _Manager>
|
||
|
|
class Array;
|
||
|
|
|
||
|
|
|
||
|
|
//
|
||
|
|
// ArrayManager<> with memory operations
|
||
|
|
//
|
||
|
|
template<typename _Ty, typename _Alloc, bool _IsClassType = std::is_class<_Ty>::value>
|
||
|
|
struct __ArrayManager;
|
||
|
|
|
||
|
|
|
||
|
|
//
|
||
|
|
// Array<>
|
||
|
|
//
|
||
|
|
template<
|
||
|
|
typename _Ty,
|
||
|
|
typename _Alloc = std::allocator<_Ty>,
|
||
|
|
typename _Manager = __ArrayManager<_Ty, _Alloc>>
|
||
|
|
class Array
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
using value_type = _Ty;
|
||
|
|
using size_type = std::size_t;
|
||
|
|
using iterator = value_type * ;
|
||
|
|
using const_iterator = const value_type*;
|
||
|
|
using reference = value_type & ;
|
||
|
|
using const_reference = const value_type &;
|
||
|
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
||
|
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||
|
|
using allocator_type = typename _Alloc;
|
||
|
|
using manager = typename _Manager;
|
||
|
|
using initializer_list = std::initializer_list<value_type>;
|
||
|
|
|
||
|
|
public:
|
||
|
|
inline Array() : size_(0), capacity_(0), data_(nullptr) { }
|
||
|
|
inline Array(size_type count) : Array() { reserve(count); }
|
||
|
|
inline Array(size_type count, const _Ty& val) : Array() { assign(count, val); }
|
||
|
|
inline Array(initializer_list list) : Array() { assign(list); }
|
||
|
|
inline Array(const Array& src) : Array() { assign(src); }
|
||
|
|
inline Array(Array&& src) : Array() { swap(src); }
|
||
|
|
inline ~Array() { destroy(); }
|
||
|
|
|
||
|
|
template <typename _Iter>
|
||
|
|
inline Array(_Iter first, _Iter last) : Array() { assign(first, last); }
|
||
|
|
|
||
|
|
inline Array& operator=(const Array& src) { if (&src != this) { resize(src.size_); manager::copy_data(begin(), src.cbegin(), size_); } return (*this); }
|
||
|
|
inline Array& operator=(Array&& src) { swap(src); return *this; }
|
||
|
|
inline Array& operator=(initializer_list list) { if (list.size()) { assign(list.begin(), list.end()); } else clear(); return (*this); }
|
||
|
|
|
||
|
|
inline Array& assign(size_type count, const _Ty& val) { if (count > 0) { resize(count); manager::copy_data(begin(), count, val); } else clear(); return (*this); }
|
||
|
|
inline Array& assign(const Array& src) { return operator=(src); }
|
||
|
|
inline Array& assign(initializer_list list) { return operator=(list); }
|
||
|
|
|
||
|
|
template <typename _Iter>
|
||
|
|
inline void assign(_Iter first, _Iter last) { auto diff = std::distance(first, last); resize((size_type)diff); auto data = begin(); while (first != last) (*data++) = (*first++); }
|
||
|
|
|
||
|
|
inline void clear() { destroy(); size_ = capacity_ = 0; data_ = nullptr; }
|
||
|
|
inline void swap(Array& rhs) { std::swap(size_, rhs.size_); std::swap(capacity_, rhs.capacity_); std::swap(data_, rhs.data_); }
|
||
|
|
|
||
|
|
inline void resize(size_type new_size) { resize(new_size, _Ty()); }
|
||
|
|
inline void resize(size_type new_size, const _Ty& v);
|
||
|
|
inline void reserve(size_type new_capacity);
|
||
|
|
|
||
|
|
inline void push_back(const _Ty& val) { resize(size_ + 1, val); }
|
||
|
|
inline void pop_back() { if (empty()) throw std::out_of_range("pop() called on empty vector"); resize(size_ - 1); }
|
||
|
|
inline void push_front(const _Ty& val) { if (size_ == 0) push_back(val); else insert(begin(), val); }
|
||
|
|
|
||
|
|
inline iterator erase(const_iterator where) { return erase(where, where + 1); }
|
||
|
|
inline iterator erase(const_iterator first, const_iterator last);
|
||
|
|
|
||
|
|
inline iterator insert(const_iterator where, const _Ty& v);
|
||
|
|
|
||
|
|
inline bool empty() const { return size_ == 0; }
|
||
|
|
inline size_type size() const { return size_; }
|
||
|
|
inline size_type size_in_bytes() const { return size_ * ((size_type)sizeof(_Ty)); }
|
||
|
|
inline size_type capacity() const { return capacity_; }
|
||
|
|
inline reference operator[](size_type off) { if (off < 0 || off >= size_) throw std::out_of_range("vector subscript out of range"); return data_[off]; }
|
||
|
|
inline const_reference operator[](size_type off) const { if (off < 0 || off >= size_) throw std::out_of_range("vector subscript out of range"); return data_[off]; }
|
||
|
|
|
||
|
|
|
||
|
|
inline bool contains(const _Ty& v) const { auto data = cbegin(); const auto data_end = cend(); while (data != data_end) if (*(data++) == v) return true; return false; }
|
||
|
|
inline size_type index_of(const_iterator it) const { check_offset(it - cbegin(), "invalid array position"); return it - data_; }
|
||
|
|
|
||
|
|
inline iterator begin() { return iterator(data_); }
|
||
|
|
inline const_iterator begin() const { return const_iterator(data_); }
|
||
|
|
inline const_iterator cbegin() const { return begin(); }
|
||
|
|
inline iterator end() { return iterator(data_ + size_); }
|
||
|
|
inline const_iterator end() const { return const_iterator(data_ + size_); }
|
||
|
|
inline const_iterator cend() const { return end(); }
|
||
|
|
inline reverse_iterator rbegin() { return reverse_iterator(end()); }
|
||
|
|
inline const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
|
||
|
|
inline const_reverse_iterator crbegin() const { return rbegin(); }
|
||
|
|
inline reverse_iterator rend() { return reverse_iterator(begin()); }
|
||
|
|
inline const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
|
||
|
|
inline const_reverse_iterator crend() const { return rend(); }
|
||
|
|
inline reference front() { if (empty()) throw std::out_of_range("front() called on empty array"); return data_[0]; }
|
||
|
|
inline const_reference front() const { if (empty()) throw std::out_of_range("front() called on empty array"); return data_[0]; }
|
||
|
|
inline reference back() { if (empty()) throw std::out_of_range("back() called on empty array"); return data_[size_ - 1]; }
|
||
|
|
inline const_reference back() const { if (empty()) throw std::out_of_range("back() called on empty array"); return data_[size_ - 1]; }
|
||
|
|
|
||
|
|
private:
|
||
|
|
inline size_type grow_capacity(size_type sz) const { size_type new_capacity = capacity_ ? (capacity_ + capacity_ / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
|
||
|
|
inline void check_offset(const size_type off) const { if (off < 0 || off >= size_) throw std::out_of_range("invalid vector position"); }
|
||
|
|
|
||
|
|
inline void destroy() { manager::destroy(data_, size_); manager::deallocate(data_, capacity_); }
|
||
|
|
protected:
|
||
|
|
size_type size_;
|
||
|
|
size_type capacity_;
|
||
|
|
_Ty* data_;
|
||
|
|
};
|
||
|
|
|
||
|
|
template<typename _Ty, typename _Alloc, typename _Manager>
|
||
|
|
inline void Array<_Ty, _Alloc, _Manager>::resize(size_type new_size, const _Ty& val)
|
||
|
|
{
|
||
|
|
if (new_size > size_)
|
||
|
|
{
|
||
|
|
if (new_size > capacity_)
|
||
|
|
{
|
||
|
|
reserve(grow_capacity(new_size));
|
||
|
|
}
|
||
|
|
manager::construct(begin() + size_, new_size - size_, val);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
manager::destroy(begin() + new_size, size_ - new_size);
|
||
|
|
}
|
||
|
|
size_ = new_size;
|
||
|
|
}
|
||
|
|
|
||
|
|
template<typename _Ty, typename _Alloc, typename _Manager>
|
||
|
|
inline void Array<_Ty, _Alloc, _Manager>::reserve(size_type new_capacity)
|
||
|
|
{
|
||
|
|
if (new_capacity <= capacity_)
|
||
|
|
return;
|
||
|
|
|
||
|
|
auto new_data = manager::allocate(new_capacity);
|
||
|
|
if (data_)
|
||
|
|
{
|
||
|
|
manager::construct(new_data, size_/* only construct needed size */);
|
||
|
|
manager::copy_data(new_data, data_, size_);
|
||
|
|
/* destroy old memory, but not resize */
|
||
|
|
destroy();
|
||
|
|
}
|
||
|
|
data_ = new_data;
|
||
|
|
capacity_ = new_capacity;
|
||
|
|
}
|
||
|
|
|
||
|
|
template<typename _Ty, typename _Alloc, typename _Manager>
|
||
|
|
inline typename Array<_Ty, _Alloc, _Manager>::iterator
|
||
|
|
Array<_Ty, _Alloc, _Manager>::erase(const_iterator first, const_iterator last)
|
||
|
|
{
|
||
|
|
const auto off = first - begin();
|
||
|
|
const auto count = last - first;
|
||
|
|
|
||
|
|
if (count != 0)
|
||
|
|
{
|
||
|
|
check_offset(off);
|
||
|
|
|
||
|
|
manager::move_data(begin() + off, begin() + off + count, size_ - off - count);
|
||
|
|
resize(size_ - count); // do destruction
|
||
|
|
}
|
||
|
|
return begin() + off;
|
||
|
|
}
|
||
|
|
|
||
|
|
template<typename _Ty, typename _Alloc, typename _Manager>
|
||
|
|
inline typename Array<_Ty, _Alloc, _Manager>::iterator
|
||
|
|
Array<_Ty, _Alloc, _Manager>::insert(const_iterator where, const _Ty& v)
|
||
|
|
{
|
||
|
|
const auto off = where - begin();
|
||
|
|
const auto insert_at = begin() + off;
|
||
|
|
|
||
|
|
check_offset(off);
|
||
|
|
|
||
|
|
resize(size_ + 1);
|
||
|
|
manager::move_data(insert_at + 1, insert_at, size_ - off - 1);
|
||
|
|
data_[off] = v;
|
||
|
|
return begin() + off;
|
||
|
|
}
|
||
|
|
|
||
|
|
//
|
||
|
|
// ArrayManager for common type
|
||
|
|
//
|
||
|
|
template<typename _Ty, typename _Alloc>
|
||
|
|
struct __ArrayManager<_Ty, _Alloc, false>
|
||
|
|
{
|
||
|
|
using value_type = _Ty;
|
||
|
|
using size_type = int;
|
||
|
|
using allocator_type = typename _Alloc;
|
||
|
|
|
||
|
|
static inline void copy_data(value_type* dest, const value_type* src, size_type count) { if (src == dest) return; ::memcpy(dest, src, (size_t)count * sizeof(value_type)); }
|
||
|
|
static inline void copy_data(value_type* dest, size_type count, const value_type& val) { ::memset(dest, (int)val, (size_t)count * sizeof(value_type)); }
|
||
|
|
static inline void move_data(value_type* dest, const value_type* src, size_type count) { if (src == dest) return; ::memmove(dest, src, (size_t)count * sizeof(value_type)); }
|
||
|
|
|
||
|
|
static inline value_type* allocate(size_type count) { return get_allocator().allocate(count); }
|
||
|
|
static inline void deallocate(value_type*& ptr, size_type count) { if (ptr) { get_allocator().deallocate(ptr, count); ptr = nullptr; } }
|
||
|
|
|
||
|
|
static inline void construct(value_type* ptr, size_type count) { }
|
||
|
|
static inline void construct(value_type* ptr, size_type count, const value_type& val) { while (count) { --count; *(ptr + count) = val; } }
|
||
|
|
static inline void destroy(value_type* ptr, size_type count) { }
|
||
|
|
|
||
|
|
private:
|
||
|
|
static allocator_type& get_allocator()
|
||
|
|
{
|
||
|
|
static allocator_type allocator_;
|
||
|
|
return allocator_;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
//
|
||
|
|
// ArrayManager for class
|
||
|
|
//
|
||
|
|
template<typename _Ty, typename _Alloc>
|
||
|
|
struct __ArrayManager<_Ty, _Alloc, true>
|
||
|
|
{
|
||
|
|
using value_type = _Ty;
|
||
|
|
using size_type = int;
|
||
|
|
using allocator_type = typename _Alloc;
|
||
|
|
|
||
|
|
static inline void copy_data(value_type* dest, const value_type* src, size_type count) { if (src == dest) return; while (count--) (*dest++) = (*src++); }
|
||
|
|
static inline void copy_data(value_type* dest, size_type count, const value_type& val) { while (count--) (*dest++) = val; }
|
||
|
|
static inline void move_data(value_type* dest, const value_type* src, size_type count)
|
||
|
|
{
|
||
|
|
if (src == dest) return;
|
||
|
|
if (dest > src && dest < src + count)
|
||
|
|
{
|
||
|
|
src = src + count - 1;
|
||
|
|
dest = dest + count - 1;
|
||
|
|
while (count--)
|
||
|
|
(*dest--) = (*src--);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
while (count--)
|
||
|
|
(*dest++) = (*src++);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline value_type* allocate(size_type count) { return get_allocator().allocate(count); }
|
||
|
|
static inline void deallocate(value_type*& ptr, size_type count) { if (ptr) { get_allocator().deallocate(ptr, count); ptr = nullptr; } }
|
||
|
|
|
||
|
|
static inline void construct(value_type* ptr, size_type count) { construct(ptr, count, value_type()); }
|
||
|
|
static inline void construct(value_type* ptr, size_type count, const value_type& val) { while (count) get_allocator().construct(ptr + (--count), val); }
|
||
|
|
static inline void destroy(value_type* ptr, size_type count) { while (count) get_allocator().destroy(ptr + (--count)); }
|
||
|
|
|
||
|
|
private:
|
||
|
|
static allocator_type& get_allocator()
|
||
|
|
{
|
||
|
|
static allocator_type allocator_;
|
||
|
|
return allocator_;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
}
|