mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
This change introduces accumulate, addressof, advance, all_of, distance, array, enable_if, allocator_traits, back_inserter, bad_alloc, is_signed, any_of, copy, exception, fill, fill_n, is_same, is_same_v, out_of_range, lexicographical_compare, is_integral, uninitialized_fill_n, is_unsigned, numeric_limits, uninitialized_fill, iterator_traits, move_backward, min, max, iterator_tag, move_iterator, reverse_iterator, uninitialized_move_n This change experiments with rewriting the ctl::vector class to make the CTL design more similar to the STL. So far it has not slowed things down to have 42 #include lines rather than 2, since it's still almost nothing compared to LLVM's code. In fact the closer we can flirt with being just like libcxx, the better chance we might have of discovering exactly what makes it so slow to compile. It would be an enormous discovery if we can find one simple trick to solving the issue there instead. This also fixes a bug in `ctl::string(const string &s)` when `s` is big.
594 lines
14 KiB
C++
594 lines
14 KiB
C++
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
|
|
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
|
|
#ifndef CTL_VECTOR_H_
|
|
#define CTL_VECTOR_H_
|
|
#include "allocator.h"
|
|
#include "allocator_traits.h"
|
|
#include "copy.h"
|
|
#include "distance.h"
|
|
#include "equal.h"
|
|
#include "fill_n.h"
|
|
#include "initializer_list.h"
|
|
#include "iterator_traits.h"
|
|
#include "lexicographical_compare.h"
|
|
#include "max.h"
|
|
#include "move_backward.h"
|
|
#include "move_iterator.h"
|
|
#include "out_of_range.h"
|
|
#include "reverse_iterator.h"
|
|
#include "uninitialized_fill.h"
|
|
#include "uninitialized_fill_n.h"
|
|
#include "uninitialized_move_n.h"
|
|
|
|
namespace ctl {
|
|
|
|
template<typename T, typename Allocator = ctl::allocator<T>>
|
|
class vector
|
|
{
|
|
public:
|
|
using value_type = T;
|
|
using allocator_type = Allocator;
|
|
using size_type = size_t;
|
|
using difference_type = ptrdiff_t;
|
|
using reference = value_type&;
|
|
using const_reference = const value_type&;
|
|
using pointer = typename ctl::allocator_traits<Allocator>::pointer;
|
|
using const_pointer =
|
|
typename ctl::allocator_traits<Allocator>::const_pointer;
|
|
using iterator = pointer;
|
|
using const_iterator = const_pointer;
|
|
using reverse_iterator = ctl::reverse_iterator<iterator>;
|
|
using const_reverse_iterator = ctl::reverse_iterator<const_iterator>;
|
|
|
|
public:
|
|
vector() noexcept(noexcept(Allocator()))
|
|
: alloc_(), data_(nullptr), size_(0), capacity_(0)
|
|
{
|
|
}
|
|
|
|
explicit vector(const Allocator& alloc) noexcept
|
|
: alloc_(alloc), data_(nullptr), size_(0), capacity_(0)
|
|
{
|
|
}
|
|
|
|
vector(size_type count,
|
|
const T& value,
|
|
const Allocator& alloc = Allocator())
|
|
: alloc_(alloc), data_(nullptr), size_(0), capacity_(0)
|
|
{
|
|
assign(count, value);
|
|
}
|
|
|
|
explicit vector(size_type count, const Allocator& alloc = Allocator())
|
|
: alloc_(alloc), data_(nullptr), size_(0), capacity_(0)
|
|
{
|
|
resize(count);
|
|
}
|
|
|
|
template<class InputIt>
|
|
vector(InputIt first, InputIt last, const Allocator& alloc = Allocator())
|
|
: alloc_(alloc), data_(nullptr), size_(0), capacity_(0)
|
|
{
|
|
assign(first, last);
|
|
}
|
|
|
|
vector(const vector& other)
|
|
: alloc_(ctl::allocator_traits<
|
|
Allocator>::select_on_container_copy_construction(other.alloc_))
|
|
, data_(nullptr)
|
|
, size_(0)
|
|
, capacity_(0)
|
|
{
|
|
assign(other.begin(), other.end());
|
|
}
|
|
|
|
vector(const vector& other, const Allocator& alloc)
|
|
: alloc_(alloc), data_(nullptr), size_(0), capacity_(0)
|
|
{
|
|
assign(other.begin(), other.end());
|
|
}
|
|
|
|
vector(vector&& other) noexcept
|
|
: alloc_(ctl::move(other.alloc_))
|
|
, data_(other.data_)
|
|
, size_(other.size_)
|
|
, capacity_(other.capacity_)
|
|
{
|
|
other.data_ = nullptr;
|
|
other.size_ = 0;
|
|
other.capacity_ = 0;
|
|
}
|
|
|
|
vector(vector&& other, const Allocator& alloc)
|
|
: alloc_(alloc), data_(nullptr), size_(0), capacity_(0)
|
|
{
|
|
if (alloc_ == other.alloc_) {
|
|
data_ = other.data_;
|
|
size_ = other.size_;
|
|
capacity_ = other.capacity_;
|
|
other.data_ = nullptr;
|
|
other.size_ = 0;
|
|
other.capacity_ = 0;
|
|
} else {
|
|
assign(ctl::make_move_iterator(other.begin()),
|
|
ctl::make_move_iterator(other.end()));
|
|
}
|
|
}
|
|
|
|
vector(std::initializer_list<T> init, const Allocator& alloc = Allocator())
|
|
: alloc_(alloc), data_(nullptr), size_(0), capacity_(0)
|
|
{
|
|
assign(init.begin(), init.end());
|
|
}
|
|
|
|
~vector()
|
|
{
|
|
clear();
|
|
ctl::allocator_traits<Allocator>::deallocate(alloc_, data_, capacity_);
|
|
}
|
|
|
|
vector& operator=(const vector& other)
|
|
{
|
|
if (this != &other) {
|
|
if (ctl::allocator_traits<
|
|
Allocator>::propagate_on_container_copy_assignment::value)
|
|
alloc_ = other.alloc_;
|
|
assign(other.begin(), other.end());
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
vector& operator=(vector&& other) noexcept(
|
|
ctl::allocator_traits<Allocator>::is_always_equal::value)
|
|
{
|
|
if (this != &other) {
|
|
clear();
|
|
ctl::allocator_traits<Allocator>::deallocate(
|
|
alloc_, data_, capacity_);
|
|
if (ctl::allocator_traits<
|
|
Allocator>::propagate_on_container_move_assignment::value)
|
|
alloc_ = ctl::move(other.alloc_);
|
|
data_ = other.data_;
|
|
size_ = other.size_;
|
|
capacity_ = other.capacity_;
|
|
other.data_ = nullptr;
|
|
other.size_ = 0;
|
|
other.capacity_ = 0;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
vector& operator=(std::initializer_list<T> ilist)
|
|
{
|
|
assign(ilist.begin(), ilist.end());
|
|
return *this;
|
|
}
|
|
|
|
void assign(size_type count, const T& value)
|
|
{
|
|
clear();
|
|
if (count > capacity_)
|
|
reallocate(count);
|
|
ctl::uninitialized_fill_n(data_, count, value);
|
|
size_ = count;
|
|
}
|
|
|
|
template<class InputIt>
|
|
void assign(InputIt first, InputIt last)
|
|
{
|
|
clear();
|
|
for (; first != last; ++first)
|
|
push_back(*first);
|
|
}
|
|
|
|
void assign(std::initializer_list<T> ilist)
|
|
{
|
|
assign(ilist.begin(), ilist.end());
|
|
}
|
|
|
|
reference at(size_type pos)
|
|
{
|
|
if (pos >= size_)
|
|
throw ctl::out_of_range("out of range");
|
|
return data_[pos];
|
|
}
|
|
|
|
const_reference at(size_type pos) const
|
|
{
|
|
if (pos >= size_)
|
|
throw ctl::out_of_range("out of range");
|
|
return data_[pos];
|
|
}
|
|
|
|
reference operator[](size_type pos)
|
|
{
|
|
return data_[pos];
|
|
}
|
|
|
|
const_reference operator[](size_type pos) const
|
|
{
|
|
return data_[pos];
|
|
}
|
|
|
|
reference front()
|
|
{
|
|
return data_[0];
|
|
}
|
|
|
|
const_reference front() const
|
|
{
|
|
return data_[0];
|
|
}
|
|
|
|
reference back()
|
|
{
|
|
return data_[size_ - 1];
|
|
}
|
|
|
|
const_reference back() const
|
|
{
|
|
return data_[size_ - 1];
|
|
}
|
|
|
|
T* data() noexcept
|
|
{
|
|
return data_;
|
|
}
|
|
|
|
const T* data() const noexcept
|
|
{
|
|
return data_;
|
|
}
|
|
|
|
iterator begin() noexcept
|
|
{
|
|
return data_;
|
|
}
|
|
|
|
const_iterator begin() const noexcept
|
|
{
|
|
return data_;
|
|
}
|
|
|
|
const_iterator cbegin() const noexcept
|
|
{
|
|
return data_;
|
|
}
|
|
|
|
iterator end() noexcept
|
|
{
|
|
return data_ + size_;
|
|
}
|
|
|
|
const_iterator end() const noexcept
|
|
{
|
|
return data_ + size_;
|
|
}
|
|
|
|
const_iterator cend() const noexcept
|
|
{
|
|
return data_ + size_;
|
|
}
|
|
|
|
reverse_iterator rbegin() noexcept
|
|
{
|
|
return reverse_iterator(end());
|
|
}
|
|
|
|
const_reverse_iterator rbegin() const noexcept
|
|
{
|
|
return const_reverse_iterator(end());
|
|
}
|
|
|
|
const_reverse_iterator crbegin() const noexcept
|
|
{
|
|
return const_reverse_iterator(end());
|
|
}
|
|
|
|
reverse_iterator rend() noexcept
|
|
{
|
|
return reverse_iterator(begin());
|
|
}
|
|
|
|
const_reverse_iterator rend() const noexcept
|
|
{
|
|
return const_reverse_iterator(begin());
|
|
}
|
|
|
|
const_reverse_iterator crend() const noexcept
|
|
{
|
|
return const_reverse_iterator(begin());
|
|
}
|
|
|
|
bool empty() const noexcept
|
|
{
|
|
return size_ == 0;
|
|
}
|
|
|
|
size_type size() const noexcept
|
|
{
|
|
return size_;
|
|
}
|
|
|
|
size_type max_size() const noexcept
|
|
{
|
|
return __PTRDIFF_MAX__;
|
|
}
|
|
|
|
size_type capacity() const noexcept
|
|
{
|
|
return capacity_;
|
|
}
|
|
|
|
void reserve(size_type new_cap)
|
|
{
|
|
if (new_cap > capacity_)
|
|
reallocate(new_cap);
|
|
}
|
|
|
|
void shrink_to_fit()
|
|
{
|
|
if (size_ < capacity_)
|
|
reallocate(size_);
|
|
}
|
|
|
|
void clear() noexcept
|
|
{
|
|
for (size_type i = 0; i < size_; ++i)
|
|
ctl::allocator_traits<Allocator>::destroy(alloc_, data_ + i);
|
|
size_ = 0;
|
|
}
|
|
|
|
iterator insert(const_iterator pos, const T& value)
|
|
{
|
|
return insert(pos, 1, value);
|
|
}
|
|
|
|
iterator insert(const_iterator pos, T&& value)
|
|
{
|
|
difference_type index = pos - begin();
|
|
if (size_ == capacity_)
|
|
grow();
|
|
iterator it = begin() + index;
|
|
ctl::move_backward(it, end(), end() + 1);
|
|
*it = ctl::move(value);
|
|
++size_;
|
|
return it;
|
|
}
|
|
|
|
iterator insert(const_iterator pos, size_type count, const T& value)
|
|
{
|
|
difference_type index = pos - begin();
|
|
if (size_ + count > capacity_)
|
|
reallocate(ctl::max(size_ + count, capacity_ * 2));
|
|
iterator it = begin() + index;
|
|
ctl::move_backward(it, end(), end() + count);
|
|
ctl::fill_n(it, count, value);
|
|
size_ += count;
|
|
return it;
|
|
}
|
|
|
|
template<class InputIt>
|
|
iterator insert(const_iterator pos, InputIt first, InputIt last)
|
|
{
|
|
difference_type count = ctl::distance(first, last);
|
|
difference_type index = pos - begin();
|
|
if (size_ + count > capacity_)
|
|
reallocate(ctl::max(size_ + count, capacity_ * 2));
|
|
iterator it = begin() + index;
|
|
ctl::move_backward(it, end(), end() + count);
|
|
ctl::copy(first, last, it);
|
|
size_ += count;
|
|
return it;
|
|
}
|
|
|
|
iterator insert(const_iterator pos, std::initializer_list<T> ilist)
|
|
{
|
|
return insert(pos, ilist.begin(), ilist.end());
|
|
}
|
|
|
|
template<class... Args>
|
|
iterator emplace(const_iterator pos, Args&&... args)
|
|
{
|
|
difference_type index = pos - begin();
|
|
if (size_ == capacity_)
|
|
grow();
|
|
iterator it = begin() + index;
|
|
ctl::move_backward(it, end(), end() + 1);
|
|
ctl::allocator_traits<Allocator>::construct(
|
|
alloc_, it, ctl::forward<Args>(args)...);
|
|
++size_;
|
|
return it;
|
|
}
|
|
|
|
iterator erase(const_iterator pos)
|
|
{
|
|
return erase(pos, pos + 1);
|
|
}
|
|
|
|
iterator erase(const_iterator first, const_iterator last)
|
|
{
|
|
difference_type index = first - begin();
|
|
difference_type count = last - first;
|
|
iterator it = begin() + index;
|
|
ctl::move(it + count, end(), it);
|
|
for (difference_type i = 0; i < count; ++i)
|
|
ctl::allocator_traits<Allocator>::destroy(alloc_, end() - i - 1);
|
|
size_ -= count;
|
|
return it;
|
|
}
|
|
|
|
void push_back(const T& value)
|
|
{
|
|
if (size_ == capacity_)
|
|
grow();
|
|
ctl::allocator_traits<Allocator>::construct(
|
|
alloc_, data_ + size_, value);
|
|
++size_;
|
|
}
|
|
|
|
void push_back(T&& value)
|
|
{
|
|
if (size_ == capacity_)
|
|
grow();
|
|
ctl::allocator_traits<Allocator>::construct(
|
|
alloc_, data_ + size_, ctl::move(value));
|
|
++size_;
|
|
}
|
|
|
|
template<class... Args>
|
|
reference emplace_back(Args&&... args)
|
|
{
|
|
if (size_ == capacity_)
|
|
grow();
|
|
ctl::allocator_traits<Allocator>::construct(
|
|
alloc_, data_ + size_, ctl::forward<Args>(args)...);
|
|
return data_[size_++];
|
|
}
|
|
|
|
void pop_back()
|
|
{
|
|
if (!empty()) {
|
|
ctl::allocator_traits<Allocator>::destroy(alloc_,
|
|
data_ + size_ - 1);
|
|
--size_;
|
|
}
|
|
}
|
|
|
|
void resize(size_type count)
|
|
{
|
|
resize(count, T());
|
|
}
|
|
|
|
void resize(size_type count, const value_type& value)
|
|
{
|
|
if (count > size_) {
|
|
if (count > capacity_)
|
|
reallocate(count);
|
|
ctl::uninitialized_fill(data_ + size_, data_ + count, value);
|
|
} else if (count < size_) {
|
|
for (size_type i = count; i < size_; ++i)
|
|
ctl::allocator_traits<Allocator>::destroy(alloc_, data_ + i);
|
|
}
|
|
size_ = count;
|
|
}
|
|
|
|
void swap(vector& other) noexcept(
|
|
ctl::allocator_traits<Allocator>::is_always_equal::value)
|
|
{
|
|
using ctl::swap;
|
|
swap(alloc_, other.alloc_);
|
|
swap(data_, other.data_);
|
|
swap(size_, other.size_);
|
|
swap(capacity_, other.capacity_);
|
|
}
|
|
|
|
allocator_type get_allocator() const noexcept
|
|
{
|
|
return alloc_;
|
|
}
|
|
|
|
private:
|
|
void grow()
|
|
{
|
|
size_type c2;
|
|
c2 = capacity_;
|
|
if (c2 < 4)
|
|
c2 = 4;
|
|
c2 += c2 >> 1;
|
|
reallocate(c2);
|
|
}
|
|
|
|
void reallocate(size_type new_capacity)
|
|
{
|
|
pointer new_data =
|
|
ctl::allocator_traits<Allocator>::allocate(alloc_, new_capacity);
|
|
size_type new_size = size_ < new_capacity ? size_ : new_capacity;
|
|
try {
|
|
ctl::uninitialized_move_n(data_, new_size, new_data);
|
|
} catch (...) {
|
|
ctl::allocator_traits<Allocator>::deallocate(
|
|
alloc_, new_data, new_capacity);
|
|
throw;
|
|
}
|
|
for (size_type i = 0; i < size_; ++i)
|
|
ctl::allocator_traits<Allocator>::destroy(alloc_, data_ + i);
|
|
ctl::allocator_traits<Allocator>::deallocate(alloc_, data_, capacity_);
|
|
data_ = new_data;
|
|
size_ = new_size;
|
|
capacity_ = new_capacity;
|
|
}
|
|
|
|
Allocator alloc_;
|
|
pointer data_;
|
|
size_type size_;
|
|
size_type capacity_;
|
|
};
|
|
|
|
template<class T, class Alloc>
|
|
bool
|
|
operator==(const vector<T, Alloc>& lhs, const vector<T, Alloc>& rhs)
|
|
{
|
|
return lhs.size() == rhs.size() &&
|
|
ctl::equal(lhs.begin(), lhs.end(), rhs.begin());
|
|
}
|
|
|
|
template<class T, class Alloc>
|
|
bool
|
|
operator!=(const vector<T, Alloc>& lhs, const vector<T, Alloc>& rhs)
|
|
{
|
|
return !(lhs == rhs);
|
|
}
|
|
|
|
template<class T, class Alloc>
|
|
bool
|
|
operator<(const vector<T, Alloc>& lhs, const vector<T, Alloc>& rhs)
|
|
{
|
|
return ctl::lexicographical_compare(
|
|
lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
|
|
}
|
|
|
|
template<class T, class Alloc>
|
|
bool
|
|
operator<=(const vector<T, Alloc>& lhs, const vector<T, Alloc>& rhs)
|
|
{
|
|
return !(rhs < lhs);
|
|
}
|
|
|
|
template<class T, class Alloc>
|
|
bool
|
|
operator>(const vector<T, Alloc>& lhs, const vector<T, Alloc>& rhs)
|
|
{
|
|
return rhs < lhs;
|
|
}
|
|
|
|
template<class T, class Alloc>
|
|
bool
|
|
operator>=(const vector<T, Alloc>& lhs, const vector<T, Alloc>& rhs)
|
|
{
|
|
return !(lhs < rhs);
|
|
}
|
|
|
|
template<class T, class Alloc>
|
|
void
|
|
swap(vector<T, Alloc>& lhs,
|
|
vector<T, Alloc>& rhs) noexcept(noexcept(lhs.swap(rhs)))
|
|
{
|
|
lhs.swap(rhs);
|
|
}
|
|
|
|
template<class InputIt,
|
|
class Alloc =
|
|
ctl::allocator<typename ctl::iterator_traits<InputIt>::value_type>>
|
|
vector(InputIt, InputIt, Alloc = Alloc())
|
|
-> vector<typename ctl::iterator_traits<InputIt>::value_type, Alloc>;
|
|
|
|
template<class Alloc>
|
|
vector(size_t,
|
|
const typename ctl::allocator_traits<Alloc>::value_type&,
|
|
Alloc = Alloc())
|
|
-> vector<typename ctl::allocator_traits<Alloc>::value_type, Alloc>;
|
|
|
|
} // namespace ctl
|
|
|
|
#endif // CTL_VECTOR_H_
|