// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CC_BASE_RANDOM_ACCESS_LIST_CONTAINER_H_
#define CC_BASE_RANDOM_ACCESS_LIST_CONTAINER_H_

#include <stddef.h>

#include <vector>

#include "base/logging.h"
#include "cc/base/list_container_helper.h"

namespace cc {

// RandomAccessListContainer is a container similar to ListContainer (see
// list_container.h), but it allows random access into its elements via
// operator[]. In order to have efficient support for random access, some
// functionality is not available for RandomAccessListContainers, such as
// insert/deletes in the middle of the list.
template <class BaseElementType>
class RandomAccessListContainer {
 public:
  // BaseElementType is the type of raw pointers this class hands out; however,
  // its derived classes might require different memory sizes.
  // max_size_for_derived_class the largest memory size required for all the
  // derived classes to use for allocation.
  explicit RandomAccessListContainer(size_t max_size_for_derived_class)
      : helper_(max_size_for_derived_class) {}

  // This constructor reserves the requested memory up front so only a single
  // allocation is needed. When num_of_elements_to_reserve_for is zero, use the
  // default size.
  RandomAccessListContainer(size_t max_size_for_derived_class,
                            size_t num_of_elements_to_reserve_for)
      : helper_(max_size_for_derived_class, num_of_elements_to_reserve_for) {
    items_.reserve(num_of_elements_to_reserve_for);
  }

  ~RandomAccessListContainer() {
    for (BaseElementType* item : items_)
      item->~BaseElementType();
  }

  void clear() {
    for (BaseElementType* item : items_)
      item->~BaseElementType();
    helper_.clear();
    items_.clear();
  }

  bool empty() const { return helper_.empty(); }
  size_t size() const { return helper_.size(); }
  size_t GetCapacityInBytes() const { return helper_.GetCapacityInBytes(); }

  template <typename DerivedElementType>
  DerivedElementType* AllocateAndConstruct() {
    auto* value =
        new (helper_.Allocate(sizeof(DerivedElementType))) DerivedElementType;
    items_.push_back(value);
    return value;
  }

  void RemoveLast() {
    items_.back()->~BaseElementType();
    items_.pop_back();
    helper_.RemoveLast();
  }

  const BaseElementType* operator[](size_t index) const {
    DCHECK_GE(index, 0u);
    DCHECK_LT(index, items_.size());
    return items_[index];
  }

  BaseElementType* operator[](size_t index) {
    DCHECK_GE(index, 0u);
    DCHECK_LT(index, items_.size());
    return items_[index];
  }

  // Note that although BaseElementType objects can change, the pointer itself
  // (in the vector) cannot. So this class only supports a const iterator.
  using ConstIterator = typename std::vector<BaseElementType*>::const_iterator;
  ConstIterator begin() const { return items_.begin(); }
  ConstIterator end() const { return items_.end(); }

 private:
  ListContainerHelper helper_;
  std::vector<BaseElementType*> items_;
};

}  // namespace cc

#endif  // CC_BASE_RANDOM_ACCESS_LIST_CONTAINER_H_