99 lines
3.9 KiB
C++
99 lines
3.9 KiB
C++
// Copyright 2022 Memgraph Ltd.
|
|
//
|
|
// Use of this software is governed by the Business Source License
|
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
|
// License, and you may not use this file except in compliance with the Business Source License.
|
|
//
|
|
// As of the Change Date specified in that file, in accordance with
|
|
// the Business Source License, use of this software will be governed
|
|
// by the Apache License, Version 2.0, included in the file
|
|
// licenses/APL.txt.
|
|
|
|
#pragma once
|
|
|
|
#include <atomic>
|
|
#include <string>
|
|
#include <string_view>
|
|
|
|
#include "utils/logging.hpp"
|
|
#include "utils/skip_list.hpp"
|
|
|
|
namespace memgraph::storage {
|
|
|
|
class NameIdMapper final {
|
|
private:
|
|
struct MapNameToId {
|
|
std::string name;
|
|
uint64_t id;
|
|
|
|
bool operator<(const MapNameToId &other) { return name < other.name; }
|
|
bool operator==(const MapNameToId &other) { return name == other.name; }
|
|
|
|
bool operator<(const std::string_view other) const { return name < other; }
|
|
bool operator==(const std::string_view other) const { return name == other; }
|
|
};
|
|
|
|
struct MapIdToName {
|
|
uint64_t id;
|
|
std::string name;
|
|
|
|
bool operator<(const MapIdToName &other) { return id < other.id; }
|
|
bool operator==(const MapIdToName &other) { return id == other.id; }
|
|
|
|
bool operator<(uint64_t other) { return id < other; }
|
|
bool operator==(uint64_t other) { return id == other; }
|
|
};
|
|
|
|
public:
|
|
/// @throw std::bad_alloc if unable to insert a new mapping
|
|
uint64_t NameToId(const std::string_view name) {
|
|
auto name_to_id_acc = name_to_id_.access();
|
|
auto found = name_to_id_acc.find(name);
|
|
uint64_t id;
|
|
if (found == name_to_id_acc.end()) {
|
|
uint64_t new_id = counter_.fetch_add(1, std::memory_order_acq_rel);
|
|
// Try to insert the mapping with the `new_id`, but use the id that is in
|
|
// the object itself. The object that cointains the mapping is designed to
|
|
// be a map, so that if the inserted name already exists `insert` will
|
|
// return an iterator to the existing item. This prevents assignment of
|
|
// two IDs to the same name when the mapping is being inserted
|
|
// concurrently from two threads. One ID is wasted in that case, though.
|
|
id = name_to_id_acc.insert({std::string(name), new_id}).first->id;
|
|
} else {
|
|
id = found->id;
|
|
}
|
|
auto id_to_name_acc = id_to_name_.access();
|
|
// We have to try to insert the ID to name mapping even if we are not the
|
|
// one who assigned the ID because we have to make sure that after this
|
|
// method returns that both mappings exist.
|
|
if (id_to_name_acc.find(id) == id_to_name_acc.end()) {
|
|
// We first try to find the `id` in the map to avoid making an unnecessary
|
|
// temporary memory allocation when the object already exists.
|
|
id_to_name_acc.insert({id, std::string(name)});
|
|
}
|
|
return id;
|
|
}
|
|
|
|
// NOTE: Currently this function returns a `const std::string &` instead of a
|
|
// `std::string` to avoid making unnecessary copies of the string.
|
|
// Usually, this wouldn't be correct because the accessor to the
|
|
// `utils::SkipList` is destroyed in this function and that removes the
|
|
// guarantee that the reference to the value contained in the list will be
|
|
// valid.
|
|
// Currently, we never delete anything from the `utils::SkipList` so the
|
|
// references will always be valid. If you change this class to remove unused
|
|
// names, be sure to change the signature of this function.
|
|
const std::string &IdToName(uint64_t id) const {
|
|
auto id_to_name_acc = id_to_name_.access();
|
|
auto result = id_to_name_acc.find(id);
|
|
MG_ASSERT(result != id_to_name_acc.end(), "Trying to get a name for an invalid ID!");
|
|
return result->name;
|
|
}
|
|
|
|
private:
|
|
std::atomic<uint64_t> counter_{0};
|
|
utils::SkipList<MapNameToId> name_to_id_;
|
|
utils::SkipList<MapIdToName> id_to_name_;
|
|
};
|
|
} // namespace memgraph::storage
|