Danny Robson
fdaa5e1392
LIMIT hid an off-by-one bug when tests used end iterators. We rename the assertion to uncover all uses of the flawed implementation, and split it into an identical assertion, and one intended to protect against iterator ends.
223 lines
6.0 KiB
C++
223 lines
6.0 KiB
C++
/*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "../debug/assert.hpp"
|
|
#include "../iterator/zip.hpp"
|
|
|
|
#include <iterator>
|
|
|
|
#include <cstddef>
|
|
|
|
|
|
namespace cruft {
|
|
/// An array-like object with capacity fixed at instantiation time, and a
|
|
/// size which is variable at runtime.
|
|
template <std::size_t CapacityV, typename ValueT>
|
|
class darray {
|
|
public:
|
|
using value_type = ValueT;
|
|
using size_type = std::size_t;
|
|
static constexpr auto elements = CapacityV;
|
|
|
|
using pointer = ValueT*;
|
|
using const_pointer = ValueT const*;
|
|
|
|
using iterator = pointer;
|
|
using const_iterator = const_pointer;
|
|
|
|
darray (): m_size (0) { ; }
|
|
darray (darray const&) = default;
|
|
darray (darray &&) = default;
|
|
|
|
darray& operator= (darray const&) = default;
|
|
darray& operator= (darray &&) = default;
|
|
|
|
~darray ()
|
|
{
|
|
for (auto &i: *this)
|
|
i.~ValueT ();
|
|
}
|
|
|
|
darray (std::initializer_list<ValueT> init):
|
|
m_size (init.size ())
|
|
{
|
|
CHECK_LE (init.size (), CapacityV);
|
|
|
|
for (auto &&[idx, src]: cruft::iterator::izip (init))
|
|
m_data.objects[idx] = std::move (src);
|
|
}
|
|
|
|
|
|
template <typename InputT>
|
|
darray (InputT first, InputT last):
|
|
darray ()
|
|
{
|
|
for ( ; first != last; ++first) {
|
|
CHECK_LT (m_size, CapacityV);
|
|
m_data.objects[m_size++] = *first;
|
|
}
|
|
}
|
|
|
|
ValueT& operator[] (size_type idx)& noexcept
|
|
{
|
|
CHECK_LT (idx, m_size);
|
|
return m_data.objects[idx];
|
|
}
|
|
|
|
ValueT const& operator[] (size_type idx) const& noexcept
|
|
{
|
|
CHECK_LT (idx, m_size);
|
|
return m_data.objects[idx];
|
|
}
|
|
|
|
|
|
iterator begin (void)& { return m_data.objects + 0; }
|
|
iterator end (void)& { return m_data.objects + m_size; }
|
|
|
|
const_iterator begin (void) const& { return m_data.objects + 0; }
|
|
const_iterator end (void) const& { return m_data.objects + m_size; }
|
|
|
|
decltype(auto) cbegin (void) const& { return begin (); }
|
|
decltype(auto) cend (void) const& { return end (); }
|
|
|
|
constexpr value_type* data (void)& noexcept
|
|
{
|
|
return m_data.objects + 0;
|
|
}
|
|
|
|
constexpr value_type const* data (void) const& noexcept
|
|
{
|
|
return m_data.objects + 0;
|
|
}
|
|
|
|
size_type size (void) const noexcept { return m_size; }
|
|
constexpr auto capacity (void) const noexcept { return CapacityV; }
|
|
constexpr auto remain (void) const noexcept { return capacity () - size (); }
|
|
|
|
constexpr void
|
|
resize (size_type const count, value_type const &value)
|
|
{
|
|
if (count < m_size) {
|
|
while (m_size > count) {
|
|
--m_size;
|
|
m_data.objects[m_size].~ValueT ();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (count > m_size) {
|
|
for ( ; m_size < count; ++m_size)
|
|
new (m_data.objects + m_size) ValueT (value);
|
|
return;
|
|
}
|
|
}
|
|
|
|
constexpr void resize (size_type count) { return resize (count, value_type {}); }
|
|
|
|
constexpr bool empty (void) const noexcept { return m_size == 0; }
|
|
constexpr bool full (void) const noexcept { return m_size == CapacityV; }
|
|
|
|
|
|
void erase (iterator pos)
|
|
{
|
|
CHECK_GE (pos, begin ());
|
|
CHECK_LT (pos, end ());
|
|
|
|
for (auto cursor = pos + 1; cursor != end (); ++cursor)
|
|
*(cursor - 1) = std::move (*cursor);
|
|
|
|
--m_size;
|
|
}
|
|
|
|
|
|
/// Insert one copy of `value' before `pos'
|
|
iterator insert (iterator pos, ValueT const &val)
|
|
{
|
|
return insert (pos, 1u, val);
|
|
}
|
|
|
|
|
|
/// Insert `count' copies of `value' before `pos'.
|
|
iterator insert (const_iterator pos, size_type count, ValueT const &val)
|
|
{
|
|
// Ensure we have enough space
|
|
CHECK_GE (pos, begin ());
|
|
CHECK_LT (pos, end ());
|
|
CHECK_LE (m_size, CapacityV - count);
|
|
|
|
std::move_backward (pos, cend (), end () + count);
|
|
m_size += count;
|
|
|
|
auto dst = const_cast<iterator> (pos);
|
|
std::fill_n (dst, count, val);
|
|
|
|
return dst;
|
|
}
|
|
|
|
|
|
iterator push_back (value_type const &val)&
|
|
{
|
|
CHECK_LT (m_size, CapacityV);
|
|
m_data.objects[m_size] = val;
|
|
return m_data.objects + m_size++;
|
|
}
|
|
|
|
|
|
iterator push_back (value_type &&val)&
|
|
{
|
|
CHECK_LT (m_size, CapacityV);
|
|
m_data.objects[m_size] = std::move (val);
|
|
return m_data.objects + m_size++;
|
|
}
|
|
|
|
|
|
private:
|
|
union alignas (ValueT) storage_t {
|
|
storage_t () { };
|
|
|
|
char store[sizeof (ValueT) * CapacityV];
|
|
ValueT objects[CapacityV];
|
|
} m_data;
|
|
|
|
size_type m_size;
|
|
};
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
std::size_t SizeA, typename ValueA,
|
|
std::size_t SizeB, typename ValueB
|
|
>
|
|
constexpr auto
|
|
operator== (
|
|
darray<SizeA,ValueA> const &a,
|
|
darray<SizeB,ValueB> const &b
|
|
) {
|
|
return std::equal (
|
|
a.begin (), a.end (),
|
|
b.begin (), b.end ()
|
|
);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
std::size_t SizeA, typename ValueA,
|
|
std::size_t SizeB, typename ValueB
|
|
>
|
|
constexpr auto
|
|
operator!= (
|
|
darray<SizeA,ValueA> const &a,
|
|
darray<SizeB,ValueB> const &b
|
|
) {
|
|
return !(a == b);
|
|
}
|
|
}
|