File: //usr/include/hphp/util/insertion-ordered-map.h
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-present 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_INSERTION_ORDERED_MAP_H_
#define incl_INSERTION_ORDERED_MAP_H_
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index_container.hpp>
namespace HPHP {
/*
* This is an insertion-order preserving hash map. Its used to emulate
* the behavior of php and hack arrays in hhbbc. Handling of int-like
* string keys must be done by the user.
*/
template <class K, class V, class Hash, class Equal>
struct InsertionOrderedMap {
using value_type = std::pair<K, V>;
private:
using extractor = boost::multi_index::member<
value_type, K, &value_type::first>;
struct List {};
struct Unordered {};
using map = boost::multi_index::multi_index_container<
value_type,
boost::multi_index::indexed_by<
boost::multi_index::sequenced<boost::multi_index::tag<List>>,
boost::multi_index::hashed_unique<boost::multi_index::tag<Unordered>,
extractor, Hash, Equal>>>;
using unordered_index = typename boost::multi_index::index<
map, Unordered>::type;
using list_index = typename boost::multi_index::index<map, List>::type;
public:
using iterator = typename list_index::iterator;
using const_iterator = typename list_index::const_iterator;
iterator find(const K& k) {
return m_map.template project<0>(getUnordered().find(k));
}
const_iterator find(const K& k) const {
return m_map.template project<0>(getUnordered().find(k));
}
template<class T>
const_iterator find(T&& k) const {
return m_map.template project<0>(
getUnordered().find(std::forward<T>(k)));
}
void update(iterator it, const V& v) {
const_cast<V&>(it->second) = v;
}
void update(iterator it, V&& v) {
const_cast<V&>(it->second) = std::move(v);
}
V& operator[](const K&k) {
// emplace_back won't insert a new entry if the key already exists
return const_cast<V&>(emplace_back(k, V{}).first->second);
}
std::pair<iterator, bool> emplace_back(const K& k, const V& v) {
return getList().push_back({k, v});
}
std::pair<iterator, bool> emplace_front(const K& k, const V& v) {
return getList().push_front({k, v});
}
iterator begin() { return getList().begin(); }
iterator end() { return getList().end(); }
const_iterator begin() const { return getList().begin(); }
const_iterator end() const { return getList().end(); }
friend iterator begin(InsertionOrderedMap& m) { return m.begin(); }
friend iterator end(InsertionOrderedMap& m) { return m.end(); }
friend const_iterator begin(const InsertionOrderedMap& m) {
return m.begin();
}
friend const_iterator end(const InsertionOrderedMap& m) { return m.end(); }
friend bool operator==(const InsertionOrderedMap& a,
const InsertionOrderedMap& b) {
if (a.size() != b.size()) return false;
auto it = b.begin();
for (auto& kv : a) {
if (!Equal{}(kv.first, it->first)) return false;
if (!(kv.second == it->second)) return false;
++it;
}
return true;
}
bool empty() const { return m_map.empty(); }
size_t size() const { return m_map.size(); }
void clear() { m_map = {}; }
private:
unordered_index& getUnordered() { return m_map.template get<Unordered>(); }
const unordered_index& getUnordered() const {
return m_map.template get<Unordered>();
}
list_index& getList() { return m_map.template get<List>(); }
const list_index& getList() const { return m_map.template get<List>(); }
map m_map;
};
}
#endif