/* vim: set ts=4 sw=4 tw=99 et:
 *
 * Copyright (C) 2012 David Anderson
 *
 * This file is part of SourcePawn.
 *
 * SourcePawn is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 * 
 * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * SourcePawn. If not, see http://www.gnu.org/licenses/.
 */
#ifndef _INCLUDE_KEIMA_TPL_CPP_VECTOR_H_
#define _INCLUDE_KEIMA_TPL_CPP_VECTOR_H_

#include <new>
#include <stdlib.h>
#include "AllocatorPolicies.h"
#include "Utility.h"

namespace ke {

template <typename T, typename AllocPolicy = ZoneAllocatorPolicy>
class Vector : public AllocPolicy
{
  public:
    Vector(AllocPolicy = AllocPolicy())
      : data(NULL),
        nitems(0),
        maxsize(0)
    { }
    ~Vector() {
        for (size_t i = 0; i < nitems; i++)
            data[i].~T();
        this->free(data);
    }
    bool append(const T& item) {
        if (!GrowIfNeeded(1))
            return false;
        new (&data[nitems]) T(item);
        nitems++;
        return true;
    }
    void pop() {
        assert(nitems);
        data[nitems - 1].~T();
        nitems--;
    }
    size_t length() const {
        return nitems;
    }
    T& at(size_t i) {
        assert(i < length());
        return data[i];
    }
    const T& at(size_t i) const {
        assert(i < length());
        return data[i];
    }
    T& operator [](size_t i) {
        return at(i);
    }
    const T& operator [](size_t i) const {
        return at(i);
    }
    void clear() {
        nitems = 0;
    }

    T *buffer() const {
        return data;
    }

  private:
    bool GrowIfNeeded(size_t needed)
    {
        if (!IsUintPtrAddSafe(nitems, needed)) {
            this->reportAllocationOverflow();
            return false;
        }
        if (maxsize == 0)
            maxsize = 8;
        while (nitems + needed > maxsize)
        {
            if (!IsUintPtrMultiplySafe(maxsize, 2)) {
                this->reportAllocationOverflow();
                return false;
            }
            maxsize *= 2;
        }
        T* newdata = (T*)this->malloc(sizeof(T) * maxsize);
        if (newdata == NULL)
            return false;
        for (size_t i = 0; i < nitems; i++)
        {
            new (&newdata[i]) T(data[i]);
            data[i].~T();
        }
        this->free(data);
        data = newdata;
        return true;
    }

  private:
    T* data;
    size_t nitems;
    size_t maxsize;
};

}

#endif /* _INCLUDE_KEIMA_TPL_CPP_VECTOR_H_ */

