Program Listing for File MemoryMap.inl

Return to documentation for file (umpire/util/MemoryMap.inl)

//////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016-20, Lawrence Livermore National Security, LLC and Umpire
// project contributors. See the COPYRIGHT file for details.
//
// SPDX-License-Identifier: (MIT)
//////////////////////////////////////////////////////////////////////////////
#ifndef UMPIRE_MemoryMap_INL
#define UMPIRE_MemoryMap_INL

#include "umpire/util/Macros.hpp"

namespace umpire {
namespace util {

namespace {

// Judy: number of Integers in a key
static constexpr unsigned int judy_depth{1};

// Judy: max height of stack
static constexpr unsigned int judy_max_levels{sizeof(uintptr_t)};

// Judy: length of key in bytes
static constexpr unsigned int judy_max{judy_depth * JUDY_key_size};

} // end anonymous namespace

// MemoryMap
template <typename V>
MemoryMap<V>::MemoryMap() :
  m_array{nullptr},
  m_last{nullptr},
  m_oper{0},
  m_pool{sizeof(Value)},
  m_size{0}
{
  // Create new judy array
  m_array = judy_open(judy_max_levels, judy_depth);
}

template <typename V>
MemoryMap<V>::~MemoryMap()
{
  // Delete all entries
  clear();

  // Close the judy array, freeing all memory
  judy_close(m_array);
}

template <typename V>
template <typename... Args>
std::pair<typename MemoryMap<V>::Iterator, bool>
MemoryMap<V>::doInsert(Key ptr, Args&&... args) noexcept
{
  // Find the ptr and update m_oper
  m_last = judy_cell(m_array, reinterpret_cast<unsigned char*>(&ptr), judy_max);
  m_oper = reinterpret_cast<uintptr_t>(this);
  UMPIRE_ASSERT(m_last);

  auto pval = reinterpret_cast<Value**>(m_last);
  const bool not_found{!(*pval)};

  if (not_found) {
    // Create it and increment size
    (*pval) = new (m_pool.allocate()) Value{std::forward<Args>(args)...};
    ++m_size;
  }

  return std::make_pair(Iterator{this, ptr}, not_found);
}

template <typename V>
std::pair<typename MemoryMap<V>::Iterator, bool>
MemoryMap<V>::insert(Key ptr, const Value& val) noexcept
{
  UMPIRE_LOG(Debug, "ptr = " << ptr);

  auto it_pair = doInsert(ptr, val);
  it_pair.second = !it_pair.second;

  return it_pair;
}

template <typename V>
template <typename P>
std::pair<typename MemoryMap<V>::Iterator, bool>
MemoryMap<V>::insert(P&& pair) noexcept
{
  return insert(pair.first, pair.second);
}

template <typename V>
template <typename... Args>
std::pair<typename MemoryMap<V>::Iterator, bool>
MemoryMap<V>::insert(Key ptr, Args&&... args) noexcept
{
  UMPIRE_LOG(Debug, "ptr = " << ptr);
  return doInsert(ptr, std::forward<Args>(args)...);
}

template <typename V>
typename MemoryMap<V>::Key
MemoryMap<V>::doFindOrBefore(Key ptr) const noexcept
{
  UMPIRE_LOG(Debug, "ptr = " << ptr);

  // Find the ptr and update m_oper
  m_last = judy_strt(m_array, reinterpret_cast<unsigned char*>(&ptr), judy_max);
  m_oper = reinterpret_cast<uintptr_t>(this);

  Key parent_ptr{0};
  judy_key(m_array, reinterpret_cast<unsigned char*>(&parent_ptr), judy_max);

  const Value* value{m_last ? reinterpret_cast<const Value*>(*m_last) : nullptr};

  // If the ptrs do not match, or the key does not exist, get the previous entry
  if (parent_ptr != ptr || !value)
  {
    m_last = judy_prv(m_array);
    judy_key(m_array, reinterpret_cast<unsigned char*>(&parent_ptr), judy_max);
  }
  UMPIRE_LOG(Debug, "returning " << parent_ptr);

  return parent_ptr;
}

template <typename V>
typename MemoryMap<V>::Iterator MemoryMap<V>::findOrBefore(Key ptr) noexcept
{
  ptr = doFindOrBefore(ptr);
  return Iterator{this, ptr};
}

template <typename V>
typename MemoryMap<V>::ConstIterator
MemoryMap<V>::findOrBefore(Key ptr) const noexcept
{
  ptr = doFindOrBefore(ptr);
  return ConstIterator{this, ptr};
}

template <typename V>
typename MemoryMap<V>::Iterator MemoryMap<V>::find(Key ptr) noexcept
{
  UMPIRE_LOG(Debug, "ptr = " << ptr);

  // Find the ptr and update m_oper
  m_last = judy_slot(m_array, reinterpret_cast<unsigned char*>(&ptr), judy_max);
  m_oper = reinterpret_cast<uintptr_t>(this);
  return m_last ? Iterator{this, ptr} : Iterator{this, iterator_end{}};
}

template <typename V>
typename MemoryMap<V>::ConstIterator
MemoryMap<V>::find(Key ptr) const noexcept
{
  UMPIRE_LOG(Debug, "ptr = " << ptr);

  // Find the ptr and update m_oper
  m_last = judy_slot(m_array, reinterpret_cast<unsigned char*>(&ptr), judy_max);
  m_oper = reinterpret_cast<uintptr_t>(this);
  return m_last ? ConstIterator{this, ptr} : ConstIterator{this, iterator_end{}};
}

template <typename V>
void MemoryMap<V>::erase(Key ptr)
{
  UMPIRE_LOG(Debug, "ptr = " << ptr);

  // Locate ptr and update m_oper
  m_last = judy_slot(m_array, reinterpret_cast<unsigned char*>(&ptr), judy_max);
  m_oper = reinterpret_cast<uintptr_t>(this);

  // If found, remove it
  if (m_last) {
    removeLast();
  } else {
    UMPIRE_ERROR("Could not remove ptr = " << ptr);
  }
}

template <typename V>
void MemoryMap<V>::erase(Iterator iter)
{
  erase(iter->first);
}

template <typename V>
void MemoryMap<V>::erase(ConstIterator iter)
{
  erase(iter->first);
}

template <typename V>
void MemoryMap<V>::clear() noexcept
{
  // Loop over the level 0 tree
  Key key{0};
  for(m_last = judy_strt(m_array, reinterpret_cast<unsigned char*>(&key), 0);
      m_last;
      m_last = judy_strt(m_array, reinterpret_cast<unsigned char*>(&key), 0))
    removeLast();

  m_size = 0;
}

template <typename V>
std::size_t MemoryMap<V>::size() const noexcept
{
  return m_size;
}

template <typename V>
void MemoryMap<V>::removeLast()
{
  auto v = reinterpret_cast<Value*>(*m_last);
  UMPIRE_ASSERT(v != nullptr);

  UMPIRE_LOG(Debug, "value pointer = " << v);

  // Manually call destructor
  v->~Value();

  // Mark as deallocated in the pool
  m_pool.deallocate(v);

  // Delete the key and cell for the current stack entry
  judy_del(m_array);

  // Decrease size
  --m_size;
}

template <typename V>
template <bool Const>
MemoryMap<V>::Iterator_<Const>::Iterator_(Map* map, Key ptr) :
  m_map{map}, m_pair{std::make_pair(ptr, m_map->m_last ? reinterpret_cast<ValuePtr>(*m_map->m_last) : nullptr)}
{
}

template <typename V>
template <bool Const>
MemoryMap<V>::Iterator_<Const>::Iterator_(Map* map, iterator_begin) :
  m_map{map}, m_pair{}
{
  m_pair.first = nullptr;
  m_map->m_last = judy_strt(m_map->m_array, reinterpret_cast<const unsigned char*>(&m_pair.first), 0);
  judy_key(m_map->m_array, reinterpret_cast<unsigned char*>(&m_pair.first), judy_max);
  m_map->m_oper = reinterpret_cast<uintptr_t>(this);
  m_pair.second = m_map->m_last ? reinterpret_cast<ValuePtr>(*m_map->m_last) : nullptr;
}

template <typename V>
template <bool Const>
MemoryMap<V>::Iterator_<Const>::Iterator_(Map* map, iterator_end) :
  m_map{map}, m_pair{std::make_pair(nullptr, static_cast<ValuePtr>(nullptr))}
{
}

template <typename V>
template <bool Const>
template <bool OtherConst>
MemoryMap<V>::Iterator_<Const>::Iterator_(const Iterator_<OtherConst>& other) :
  m_map{other.m_map}, m_pair{other.m_pair}
{
}

template <typename V>
template <bool Const>
typename MemoryMap<V>::template Iterator_<Const>::Reference
MemoryMap<V>::Iterator_<Const>::operator*()
{
  return m_pair;
}

template <typename V>
template <bool Const>
typename MemoryMap<V>::template Iterator_<Const>::Pointer
MemoryMap<V>::Iterator_<Const>::operator->()
{
  return &m_pair;
}

template <typename V>
template <bool Const>
typename MemoryMap<V>::template Iterator_<Const>&
MemoryMap<V>::Iterator_<Const>::operator++()
{
  // Check whether this object was not the last to set the internal judy state
  if (m_pair.first && m_map->m_oper != reinterpret_cast<uintptr_t>(this)) {
    // Seek m_array internal position
    judy_slot(m_map->m_array, reinterpret_cast<const unsigned char*>(&m_pair.first), judy_max);
  }
  m_map->m_last = judy_nxt(m_map->m_array);
  m_map->m_oper = reinterpret_cast<uintptr_t>(this);

  if (!m_map->m_last) {
    // Reached end
    m_pair.first = nullptr;
  }
  else {
    // Update m_last and pair
    judy_key(m_map->m_array, reinterpret_cast<unsigned char*>(&m_pair.first), judy_max);
    m_pair.second = reinterpret_cast<ValuePtr>(*m_map->m_last);
  }

  return *this;
}

template <typename V>
template <bool Const>
typename MemoryMap<V>::template Iterator_<Const>
MemoryMap<V>::Iterator_<Const>::operator++(int)
{
  Iterator tmp{*this};
  ++(*this);
  return tmp;
}

template <typename V>
template <bool Const>
template <bool OtherConst>
bool MemoryMap<V>::Iterator_<Const>::operator==(const MemoryMap<V>::Iterator_<OtherConst>& other) const
{
  return m_map == other.m_map && m_pair.first == other.m_pair.first;
}

template <typename V>
template <bool Const>
template <bool OtherConst>
bool MemoryMap<V>::Iterator_<Const>::operator!=(const MemoryMap<V>::Iterator_<OtherConst>& other) const
{
  return !(*this == other);
}

template <typename V>
typename MemoryMap<V>::ConstIterator
MemoryMap<V>::begin() const
{
  return ConstIterator{this, iterator_begin{}};
}

template <typename V>
typename MemoryMap<V>::Iterator
MemoryMap<V>::begin()
{
  return Iterator{this, iterator_begin{}};
}

template <typename V>
typename MemoryMap<V>::ConstIterator
MemoryMap<V>::end() const
{
  return ConstIterator{this, iterator_end{}};
}

template <typename V>
typename MemoryMap<V>::Iterator
MemoryMap<V>::end()
{
  return Iterator{this, iterator_end{}};
}

} // end of namespace util
} // end of namespace umpire

#endif // UMPIRE_MemoryMap_INL