blob: e35fd8ae707cd00b5df43395478e473733f747ed [file] [log] [blame]
//========================================================================
//
// GooVector.h
//
// This file is licensed under the GPLv2 or later
//
// Copyright 2010 David Benjamin <davidben@mit.edu>
// Copyright 2010 Albert Astals Cid <aacid@kde.org>
//
//========================================================================
#ifndef GOO_GOOVECTOR_H
#define GOO_GOOVECTOR_H
#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif
#include <new> // vector implementations need placement-new
#include <assert.h>
#include <stdlib.h>
/* Mostly STL-compatible vector class. Should correctly call constructors and
* destructors, but does not carefully handle alignment requirements. */
template<class T> class GooVector {
public:
/* various STL-compatible typedefs */
typedef T value_type;
typedef T* pointer;
typedef T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef int difference_type;
typedef T* iterator;
typedef const T* const_iterator;
// TODO: reverse_iterator, if we feel like it
GooVector() : m_data(NULL), m_capacity(0), m_size(0) {}
explicit GooVector(size_type n) : m_data(NULL), m_capacity(0), m_size(0) {
resize(n);
}
explicit GooVector(size_type n, const T& t) : m_data(NULL), m_capacity(0), m_size(0) {
resize(n, t);
}
explicit GooVector(const GooVector& gv) : m_data(NULL), m_capacity(0), m_size(0) {
reserve(gv.size());
for (size_type i = 0; i < m_size; i++) {
push_back(gv[i]);
}
}
~GooVector() {
clear();
}
iterator begin() { return m_data; }
const_iterator begin() const { return m_data; }
iterator end() { return m_data + m_size; }
const_iterator end() const { return m_data + m_size; }
size_type size() const { return m_size; }
size_type capacity() const { return m_capacity; }
bool empty() const { return m_size == 0; }
reference operator[] (size_type n) { return m_data[n]; }
const_reference operator[] (size_type n) const { return m_data[n]; }
reference at(size_type n) {
assert(n < m_size);
return m_data[n];
}
const_reference at(size_type n) const {
assert(n < m_size);
return m_data[n];
}
reference front() { assert(!empty()); return m_data[0]; }
const_reference front() const { assert(!empty()); return m_data[0]; }
reference back() { assert(!empty()); return m_data[m_size-1]; }
const_reference back() const { assert(!empty()); return m_data[m_size-1]; }
void push_back(const T& v) {
reserve(m_size + 1);
place_new(m_data + m_size, v);
m_size++;
}
void pop_back() {
assert(!empty());
m_size--;
destruct(m_data + m_size);
}
void clear() {
for (size_t i = 0; i < m_size; i++) {
destruct(m_data + i);
}
m_size = 0;
free(m_data);
m_data = NULL;
m_capacity = 0;
}
void reserve(size_type cap) {
if (m_capacity >= cap) return;
// make sure we always at least double
if (m_capacity*2 > cap)
cap = m_capacity*2;
resize_internal(cap);
}
void resize(size_type n) { resize(n, T()); }
void resize(size_type n, const T& t) {
reserve(n);
while (m_size < n)
push_back(t);
while (m_size > n)
pop_back();
}
private:
T *m_data;
size_type m_capacity;
size_type m_size;
inline void destruct(T *obj) {
obj->~T();
}
inline void place_new(T *loc, const T& v) {
new (loc) T(v);
}
inline void resize_internal(size_type new_cap) {
assert(new_cap >= m_capacity);
// To be correct with ctors and dtors, we do not use realloc and friends.
// A more efficient implementation would specialize for POD types and just
// realloc() or something. Meh, if we care, we ought to use just STL's
T *new_data = (T*) malloc(sizeof(T) * new_cap);
assert(new_data);
// Move over old data
if (m_data) {
for (size_type i = 0; i < m_size; i++) {
place_new(new_data + i, m_data[i]);
destruct(m_data + i);
}
free(m_data);
}
// And set the new values
m_data = new_data;
m_capacity = new_cap;
}
};
#endif