/*
   +----------------------------------------------------------------------+
   | HipHop for PHP                                                       |
   +----------------------------------------------------------------------+
   | Copyright (c) 2010-2015 Facebook, Inc. (http://www.facebook.com)     |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
   | available through the world-wide-web at the following url:           |
   | http://www.php.net/license/3_01.txt                                  |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | license@php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
*/

#ifndef incl_HPHP_ZEND_REQUEST_LOCAL
#define incl_HPHP_ZEND_REQUEST_LOCAL

#include "hphp/runtime/base/request-local.h"
#include <unordered_map>
#include <vector>
#include "hphp/runtime/base/request-event-handler.h"

namespace HPHP {

// a trick to allow us to parenthesize the type, so the macros
// don't get confused by the commas
namespace detail {
template<typename T> struct get_first_param;
template<typename P> struct get_first_param<void(P)> {
  typedef P type;
};
}

#define ZEND_REQUEST_LOCAL_VECTOR(T, F, N, Z)                   \
  IMPLEMENT_STATIC_REQUEST_LOCAL(                               \
    HPHP::detail::get_first_param<                              \
    void(HPHP::ZendRequestLocalVector<T, F>)>::type, N);        \
  typedef HPHP::ZendRequestLocalVector<T, F>::container Z

template <class T, class F>
struct ZendRequestLocalVector final : RequestEventHandler {
  static_assert(std::is_pointer<T>::value,
                "ZendRequestLocalVector only stores pointers");
  using container = std::vector<T>;
  container& get() { return m_container; }
  void requestInit() override { clear(); }
  void requestShutdown() override { clear(); }
  void vscan(IMarker& mark) const override {
    for (const auto& p : m_container) {
      if (p) p->scan(mark);
    }
  }
private:
  void clear() {
    for (size_t i = 0; i < m_container.size(); ++i) {
      if (m_container[i]) {
        auto element = m_container[i];
        m_container[i] = nullptr;
        m_destroy_callback(element);
      }
    }
    m_container.clear();
    m_container.push_back(nullptr); // don't give out id 0
  }

  container m_container;
  F m_destroy_callback;
};

#define ZEND_REQUEST_LOCAL_MAP(K, V, N)                 \
  IMPLEMENT_STATIC_REQUEST_LOCAL(                       \
    HPHP::detail::get_first_param<                      \
    void(HPHP::ZendRequestLocalMap<K, V>)>::type, N)

template <class K, class V>
struct ZendRequestLocalMap final : RequestEventHandler {
  using container = std::unordered_map<K, V>;
  container& get() { return m_map; }
  void requestInit() override { m_map.clear(); }
  void requestShutdown() override { m_map.clear(); }
  void vscan(IMarker& mark) const override {
    // TODO: t7925927 this is wrong when container has pointers. The below is
    // sufficient for the current usage of this class.
    for (const auto& pair : m_map) {
      mark(&pair.second, sizeof(pair.second));
    }
  }
private:
  container m_map;
};

}

#endif // incl_HPHP_ZEND_REQUEST_LOCAL
