Skip to content

Commit

Permalink
(JSON) Add perroht_object
Browse files Browse the repository at this point in the history
  • Loading branch information
KIwabuchi committed Sep 30, 2023
1 parent b79772d commit 3d9a467
Show file tree
Hide file tree
Showing 3 changed files with 330 additions and 11 deletions.
49 changes: 38 additions & 11 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ include(FetchContent)
# -------------------------------------------------------------------------------- #
# CMake policy
# -------------------------------------------------------------------------------- #
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13")
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13")
cmake_policy(SET CMP0077 NEW)
endif()
endif ()

if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.24")
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.24")
cmake_policy(SET CMP0135 NEW)
endif()
endif ()

# -------------------------------------------------------------------------------- #
# Metall general configuration
Expand Down Expand Up @@ -92,6 +92,7 @@ option(RUN_LARGE_SCALE_TEST "Run large scale tests" OFF)
option(RUN_BUILD_AND_TEST_WITH_CI "Perform build and basic test with CI" OFF)
option(BUILD_VERIFICATION "Build verification directory" OFF)
option(USE_SORTED_BIN "Use VM space aware algorithm in the bin directory" OFF)
option(USE_PERROHT_IN_JSON "Use Perroht in the Metall JSON" OFF)

set(DEFAULT_VM_RESERVE_SIZE "0" CACHE STRING
"Set the default VM reserve size (use the internally defined value if 0 is specified)")
Expand Down Expand Up @@ -126,11 +127,11 @@ endif ()
# -------------------------------------------------------------------------------- #
if (INSTALL_HEADER_ONLY)
message(WARNING "INSTALL_HEADER_ONLY option has been replaced with JUST_INSTALL_METALL_HEADER.")
endif()
endif ()

if (JUST_INSTALL_METALL_HEADER)
return()
endif()
endif ()
# -------------------------------------------------------------------------------- #

# -------------------------------------------------------------------------------- #
Expand Down Expand Up @@ -211,6 +212,11 @@ if (USE_ANONYMOUS_NEW_MAP)
message(STATUS "Use the anonymous map for new map region")
endif ()

if (USE_PERROHT_IN_JSON)
list(APPEND METALL_DEFS "METALL_USE_PERROHT_IN_JSON")
message(STATUS "Use Perroht in the Metall JSON")
endif ()

# Requirements for GCC
if (NOT RUN_BUILD_AND_TEST_WITH_CI)
if (("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU"))
Expand Down Expand Up @@ -259,6 +265,21 @@ if (NOT Boost_FOUND)
find_package(Boost 1.64)
endif ()


# ---------- Perroht ---------- #
if (USE_PERROHT_IN_JSON)
find_package(Perroht QUIET)
if (NOT Perroht_FOUND)
set(JUST_INSTALL_PERROHT_HEADER TRUE)
FetchContent_Declare(Perroht
GIT_REPOSITORY [email protected]:LLNL/Perroht.git
GIT_TAG develop
)
FetchContent_MakeAvailable(Perroht)
endif ()
endif ()


# -------------------------------------------------------------------------------- #
# Add executable functions
# -------------------------------------------------------------------------------- #
Expand Down Expand Up @@ -304,20 +325,20 @@ function(common_setup_for_metall_executable name)
# --------------------

# ----- Compile Definitions ----- #
foreach(X IN LISTS METALL_DEFS)
foreach (X IN LISTS METALL_DEFS)
target_compile_definitions(${name} PRIVATE ${X})
endforeach()
endforeach ()
# --------------------

# ----- CXX17 Filesystem Lib----- #
# include_cxx_filesystem_library module must be executed first
if (FOUND_CXX17_FILESYSTEM_LIB)
if (REQUIRE_LIB_STDCXX_FS)
target_link_libraries(${name} PRIVATE stdc++fs)
endif()
elseif()
endif ()
elseif ()
target_compile_definitions(${name} PRIVATE "METALL_DISABLE_CXX17_FILESYSTEM_LIB")
endif()
endif ()
# --------------------

# ----- Umap----- #
Expand All @@ -329,6 +350,12 @@ function(common_setup_for_metall_executable name)
endif ()
endif ()
# --------------------

# ----- Perroht----- #
if (USE_PERROHT_IN_JSON)
target_link_libraries(${name} PRIVATE Perroht::Perroht)
endif ()
# --------------------
endfunction()

function(add_metall_executable name source)
Expand Down
278 changes: 278 additions & 0 deletions include/metall/json/details/perroht_object.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
// Copyright 2023 Lawrence Livermore National Security, LLC and other Metall
// Project Developers. See the top-level COPYRIGHT file for details.
//
// SPDX-License-Identifier: (Apache-2.0 OR MIT)

#ifndef METALL_CONTAINER_EXPERIMENT_JSON_DETAILS_PERROHT_OBJECT_HPP
#define METALL_CONTAINER_EXPERIMENT_JSON_DETAILS_PERROHT_OBJECT_HPP

#include <iostream>
#include <memory>
#include <utility>
#include <string_view>
#include <algorithm>

#ifdef METALL_USE_PERROHT_IN_JSON
#include <perroht/unordered_set.hpp>
#include <perroht/utilities/hash.hpp>
#include <perroht/utilities/equal.hpp>
#endif

#include <metall/json/json_fwd.hpp>
#include <metall/container/scoped_allocator.hpp>
#include <metall/container/vector.hpp>
#include <metall/utility/hash.hpp>

namespace metall::json::jsndtl {

namespace {
namespace mc = metall::container;
}

// Forward declarations
template <typename Alloc = std::allocator<std::byte>>
class perroht_object;

template <typename allocator_type, typename other_object_type>
bool general_perroht_object_equal(
const perroht_object<allocator_type> &object,
const other_object_type &other_object) noexcept;

/// \brief JSON object implementation.
/// This class is designed to use a small amount of memory even sacrificing the
/// look-up performance.
template <typename Alloc>
class perroht_object {
public:
using allocator_type = Alloc;
using value_type =
key_value_pair<char, std::char_traits<char>, allocator_type>;
using key_type = std::basic_string_view<
char, std::char_traits<char>>; // typename value_type::key_type;
using mapped_type = value<allocator_type>; // typename
// value_type::value_type;

private:
template <typename alloc, typename T>
using other_scoped_allocator = mc::scoped_allocator_adaptor<
typename std::allocator_traits<alloc>::template rebind_alloc<T>>;

struct Hasher {
std::size_t operator()(const value_type &value) const noexcept {
return perroht::StringHash{}(value.key());
}
std::size_t operator()(const key_type &key) const noexcept {
return perroht::StringHash{}(key);
}
};

struct Equal {
bool operator()(const value_type &lhs,
const value_type &rhs) const noexcept {
return perroht::StringEqual{}(lhs.key(), rhs.key());
}
bool operator()(const value_type &lhs, const key_type &rhs) const noexcept {
return perroht::StringEqual{}(lhs.key(), rhs);
}
bool operator()(const key_type &lhs, const value_type &rhs) const noexcept {
return perroht::StringEqual{}(lhs, rhs.key());
}
bool operator()(const key_type &lhs, const key_type &rhs) const noexcept {
return perroht::StringEqual{}(lhs, rhs);
}
};
using value_storage_alloc_type =
other_scoped_allocator<allocator_type, value_type>;
using value_storage_type =
perroht::unordered_flat_set<value_type, Hasher, Equal,
value_storage_alloc_type>;

// Value: the position of the corresponding item in the value_storage
using value_postion_type = typename value_storage_type::size_type;

public:
using iterator = typename value_storage_type::iterator;
using const_iterator = typename value_storage_type::const_iterator;

/// \brief Constructor.
perroht_object() {}

/// \brief Constructor.
/// \param alloc An allocator object.
explicit perroht_object(const allocator_type &alloc)
: m_value_storage(alloc) {}

/// \brief Copy constructor
perroht_object(const perroht_object &) = default;

/// \brief Allocator-extended copy constructor
perroht_object(const perroht_object &other, const allocator_type &alloc)
: m_value_storage(other.m_value_storage, alloc) {}

/// \brief Move constructor
perroht_object(perroht_object &&) noexcept = default;

/// \brief Allocator-extended move constructor
perroht_object(perroht_object &&other, const allocator_type &alloc) noexcept
: m_value_storage(std::move(other.m_value_storage), alloc) {}

/// \brief Copy assignment operator
perroht_object &operator=(const perroht_object &) = default;

/// \brief Move assignment operator
perroht_object &operator=(perroht_object &&) noexcept = default;

/// \brief Swap contents.
void swap(perroht_object &other) noexcept {
using std::swap;
swap(m_value_storage, other.m_value_storage);
}

/// \brief Access a mapped value with a key.
/// If there is no mapped value that is associated with 'key', allocates it
/// first. \param key The key of the mapped value to access. \return A
/// reference to the mapped value associated with 'key'.
mapped_type &operator[](const key_type &key) {
auto itr = find(key);
if (itr != end()) {
return itr->value();
}
auto ret = m_value_storage.emplace(key, mapped_type{get_allocator()});
assert(ret.second);
return ret.first->value();
}

/// \brief Access a mapped value.
/// \param key The key of the mapped value to access.
/// \return A reference to the mapped value associated with 'key'.
const mapped_type &operator[](const key_type &key) const {
return find(key)->value();
}

/// \brief Return true if the key is found.
/// \return True if found; otherwise, false.
bool contains(const key_type &key) const { return count(key) > 0; }

/// \brief Count the number of elements with a specific key.
/// \return The number elements with a specific key.
std::size_t count(const key_type &key) const {
if (find(key) != end()) {
return 1;
}
return 0;
}

/// \brief Access a mapped value.
/// \param key The key of the mapped value to access.
/// \return A reference to the mapped value associated with 'key'.
mapped_type &at(const key_type &key) { return find(key)->value(); }

/// \brief Access a mapped value.
/// \param key The key of the mapped value to access.
/// \return A reference to the mapped value associated with 'key'.
const mapped_type &at(const key_type &key) const {
return find(key)->value();
}

iterator find(const key_type &key) { return m_value_storage.find(key); }

const_iterator find(const key_type &key) const {
return m_value_storage.find(key);
}

/// \brief Returns an iterator that is at the beginning of the objects.
/// \return An iterator that is at the beginning of the objects.
iterator begin() { return m_value_storage.begin(); }

/// \brief Returns an iterator that is at the beginning of the objects.
/// \return A const iterator that is at the beginning of the objects.
const_iterator begin() const { return m_value_storage.begin(); }

/// \brief Returns an iterator that is at the end of the objects.
/// \return An iterator that is at the end of the objects.
iterator end() { return m_value_storage.end(); }

/// \brief Returns an iterator that is at the end of the objects.
/// \return A const iterator that is at the end of the objects.
const_iterator end() const { return m_value_storage.end(); }

/// \brief Returns the number of key-value pairs.
/// \return The number of key-values pairs.
std::size_t size() const { return m_value_storage.size(); }

/// \brief Erases the element at 'position'.
/// \param position The position of the element to erase.
/// \return Iterator following the removed element.
/// If 'position' refers to the last element, then the end() iterator is
/// returned.
iterator erase(iterator position) { return m_value_storage.erase(position); }

/// \brief Erases the element at 'position'.
/// \param position The position of the element to erase.
/// \return Iterator following the removed element.
/// If 'position' refers to the last element, then the end() iterator is
/// returned.
iterator erase(const_iterator position) {
return m_value_storage.erase(position);
}

/// \brief Erases the element associated with 'key'.
/// \param key The key of the element to erase.
/// \return Iterator following the removed element.
/// If 'position' refers to the last element, then the end() iterator is
/// returned.
iterator erase(const key_type &key) {
return m_value_storage.erase(find(key));
}

/// \brief Return `true` if two objects are equal.
/// \param lhs An object to compare.
/// \param rhs An object to compare.
/// \return True if two objects are equal. Otherwise, false.
friend bool operator==(const perroht_object &lhs,
const perroht_object &rhs) noexcept {
return jsndtl::general_perroht_object_equal(lhs, rhs);
}

/// \brief Return `true` if two objects are not equal.
/// \param lhs An object to compare.
/// \param rhs An object to compare.
/// \return True if two objects are not equal. Otherwise, false.
friend bool operator!=(const perroht_object &lhs,
const perroht_object &rhs) noexcept {
return !(lhs == rhs);
}

/// \brief Return an allocator object.
allocator_type get_allocator() const noexcept {
return m_value_storage.get_allocator();
}

private:
value_storage_type m_value_storage{allocator_type{}};
};

/// \brief Swap value instances.
template <typename allocator_type>
inline void swap(perroht_object<allocator_type> &lhd,
perroht_object<allocator_type> &rhd) noexcept {
lhd.swap(rhd);
}

/// \brief Provides 'equal' calculation for other object types that have the
/// same interface as the object class.
template <typename allocator_type, typename other_object_type>
inline bool general_perroht_object_equal(
const perroht_object<allocator_type> &object,
const other_object_type &other_object) noexcept {
return object.size() == other_object.size() &&
std::equal(object.begin(), object.end(), other_object.begin(),
other_object.end(), [](const auto &lhs, const auto &rhs) {
return lhs.key() == rhs.key() &&
lhs.value() == rhs.value();
});
}

} // namespace metall::json::jsndtl

#endif // METALL_CONTAINER_EXPERIMENT_JSON_DETAILS_PERROHT_OBJECT_HPP
Loading

0 comments on commit 3d9a467

Please sign in to comment.