#include <memory>
#include <algorithm>
#include <cassert>
#include <iostream>
template <typename T, std::size_t N>
class tiny_buffer {
public:
typedef T value_type;
typedef value_type * iterator;
typedef const value_type * const_iterator;
tiny_buffer()
: size_(0)
{}
tiny_buffer(std::size_t n, const value_type & v)
: size_(n)
{
std::uninitialized_fill(begin(), end(), v);
}
tiny_buffer(std::size_t n)
: size_(n)
{
std::uninitialized_fill(begin(), end(), (value_type()));
}
~tiny_buffer()
{ shrink(0); }
template <typename S, std::size_t M>
tiny_buffer(const tiny_buffer<S, M> & rhs)
: size_(rhs.size())
{
check_size(size_);
std::uninitialized_copy(rhs.begin(), rhs.end(), begin());
}
template <typename S, std::size_t M>
tiny_buffer & operator=(const tiny_buffer<S, M> & rhs)
{
const std::size_t new_size = rhs.size();
if (new_size < size_) {
shrink(new_size);
std::copy(rhs.begin(), rhs.end(), begin());
} else {
check_size(new_size);
std::copy(rhs.begin(), rhs.begin() + size_, begin());
std::uninitialized_copy(rhs.begin() + size_, rhs.end(), end());
size_ = new_size;
}
}
value_type & operator[](std::size_t i)
{ return data()[i]; }
const value_type & operator[](std::size_t i) const
{ return data()[i]; }
value_type & at(std::size_t i)
{
check_index(i);
return (*this)[i];
}
const value_type & at(std::size_t i) const
{
check_index(i);
return (*this)[i];
}
iterator begin()
{ return data(); }
iterator end()
{ return data() + size_; }
const_iterator begin() const
{ return data(); }
const_iterator end() const
{ return data() + size_; }
std::size_t size() const
{ return size_; }
void push_back(const value_type & v)
{
check_size(size_ + 1);
new(data() + size_) value_type(v);
++size_;
}
void shrink(std::size_t new_size)
{
assert(new_size <= size_);
for (std::size_t i = new_size; i < size_; ++i) {
data()[i].~value_type();
}
size_ = new_size;
}
private:
char raw_data_[sizeof(T) * N];
std::size_t size_;
T * data()
{ return reinterpret_cast<T*>(raw_data_); }
const T * data() const
{ return reinterpret_cast<const T*>(raw_data_); }
void check_index(std::size_t i) const
{ assert(i < size() && "Buffer index out of bounds"); }
void check_size(std::size_t s) const
{ assert(s <= N && "Buffer size exceeds capacity"); }
};
// Testing
static int cnt;
struct tracer
{
tracer() { cnt++; std::cout << "ctor\n"; }
tracer(const tracer &) { cnt++; std::cout << "copy ctor\n"; };
~tracer() { cnt--; std::cout << "dtor\n"; }
};
int main()
{
tiny_buffer<int, 5> a;
assert(a.size() == 0);
a.push_back(3);
assert(a[0] == 3);
assert(a.size() == 1);
tiny_buffer<short, 3> b(3, 1);
a = b;
assert(b.size() == 3);
assert(a.size() == b.size());
assert(a[0] == 1 && a[1] == 1 && a[2] == 1);
tiny_buffer<int, 5> c(b);
assert(c.size() == b.size());
assert(c[0] == 1 && c[1] == 1 && c[2] == 1);
tiny_buffer<tracer, 3> ts(2);
assert(ts.size() == 2);
assert(cnt == ts.size());
ts.shrink(1);
assert(ts.size() == 1);
assert(cnt == 1);
ts.push_back((tracer()));
return 0;
}