vector.h 3.77 KB
#ifndef __VECTOR_H__
#define __VECTOR_H__
#include <cstring>
#include <limits>
#include <type_traits>

template <class C> class vector {
  C *data;
  unsigned int size;
  unsigned int cap;

private:
  static unsigned int round_up_to_power_of_2(unsigned w) {
    const auto bits = std::numeric_limits<unsigned>::digits;
    --w;
    for (unsigned s = 1; s < bits; s *= 2)
      w |= w >> s;
    return ++w;
  };

  void destroy_array(C *ptr, unsigned int s) {
    for (unsigned j = 0; j < s; ++j)
      (ptr + j)->~C();
    free(ptr);
  }

  void resize_before_push() {
    if (size >= cap) {
      if (cap > std::numeric_limits<decltype(size)>::max() / (2 * sizeof(C)))
        throw length_error("Vector too long");
      unsigned int newcap = (cap == 0) ? 1 : 2 * cap;
      C *newdata;
      if constexpr (!std::is_trivially_copyable<C>()) {
        newdata = static_cast<C *>(
            std::aligned_alloc(alignof(C), newcap * sizeof(C)));
        if (!newdata)
          throw bad_alloc();
        unsigned i;
        try {
          for (i = 0; i < size; i++)
            new (newdata + i) C(std::move(data[i]));
        } catch (...) {
          destroy_array(newdata, i);
          throw;
        }
        destroy_array(data, size);
      } else {
        newdata = static_cast<C *>(std::realloc(data, newcap * sizeof(C)));
        if (!newdata)
          throw bad_alloc();
      }
      data = newdata;
      cap = newcap;
    }
  }

public:
  class index_out_of_range {};
  explicit vector(unsigned s) {

    cap = round_up_to_power_of_2(s);

    data = static_cast<C *>(std::aligned_alloc(alignof(C), cap * sizeof(C)));
    if (!data)
      throw bad_alloc();
    size = s;

    unsigned i;
    try {
      for (i = 0; i < size; i++)
        new (data + i) C();
    } catch (...) {
      destroy_array(data, i);
      throw;
    }
  }

  ~vector() { destroy_array(data, size); }

  C &operator[](unsigned int pos) {
    if (pos >= size)
      throw index_out_of_range();
    return data[pos];
  }

  C operator[](unsigned int pos) const {
    if (pos >= size)
      throw index_out_of_range();
    return data[pos];
  }

  vector(const vector<C> &s) {
    cap = s.cap;
    data = static_cast<C *>(std::aligned_alloc(alignof(C), cap * sizeof(C)));
    if (!data)
      throw bad_alloc();
    size = s.size;
    unsigned i;
    try {
      for (i = 0; i < size; i++)
        new (data + i) C(s.data[i]);
    } catch (...) {
      destroy_array(data, i);
      throw;
    }
  }

  void swap(vector<C> &s) {
    C *t1 = s.data;
    unsigned int t2 = s.size;
    unsigned int t3 = s.cap;
    s.data = data;
    s.size = size;
    data = t1;
    size = t2;
    cap = t3;
  }

  vector<C> &operator=(const vector<C> &s) {
    if (this == &s)
      return *this;
    vector<C> n(s);
    swap(n);
    return *this;
  }
  friend ostream &operator<<(ostream &o, const vector<C> &v) {
    o << '[';
    for (unsigned i = 0; i < v.size; i++) {
      o << v[i];
      if (i != v.size - 1)
        o << ',';
    };
    o << ']';
    return o;
  }

  template <class... Args> C &emplace_back(Args &&... args) {
    //    cout << "emplace_back" << endl;
    resize_before_push();
    new (data + size) C(std::forward<Args>(args)...);
    return *(data + size++);
  }

#if 1
  C &push_back(C &&s) {
    //    cout << "push_back with rvalue argument" << endl;
    resize_before_push();
    new (data + size) C(std::move(s));
    //    new (data + size) C(s);
    return *(data + size++);
  }

  C &push_back(const C &s) {
    //    cout << "push_back with reference argument" << endl;
    resize_before_push();
    new (data + size) C(s);
    return *(data + size++);
  }
#else
  template <class T> C &push_back(T &&s) {
    resize_before_push();
    new (data + size) C(std::forward<T>(s));
    return *(data + size++);
  }
#endif
};
#endif /* __VECTOR_H__ */