* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* openUBMC is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#include <mc/dict.h>
#include <mc/dict/entry.h>
#include <mc/json.h>
#include <mc/variant.h>
#include <mc/variant/copy_context.h>
#include <mc/variant/variant_reference.h>
#include <stdexcept>
#include <unordered_map>
namespace mc {
dict::dict(const dict& other) {
other.ensure_data();
m_data = other.m_data;
}
dict& dict::operator=(const dict& other) {
other.ensure_data();
m_data = other.m_data;
return *this;
}
dict::dict(const std::vector<entry>& entries)
: m_data(mc::make_shared<data_t>()) {
for (auto&& entry_val : entries) {
auto it = m_data->index.find(entry_val.key, m_data->index.hash_function(), m_data->index.key_eq());
if (it != m_data->index.end()) {
const_cast<entry&>(*it).value = entry_val.value;
} else {
entry* new_entry = new entry(std::move(entry_val.key), entry_val.value);
m_data->entries.push_back(*new_entry);
m_data->index.insert(*new_entry);
}
}
}
dict::dict(std::initializer_list<std::pair<variant, variant>> init)
: m_data(mc::make_shared<data_t>()) {
for (const auto& pair : init) {
auto it = m_data->index.find(pair.first, m_data->index.hash_function(), m_data->index.key_eq());
if (it != m_data->index.end()) {
const_cast<entry&>(*it).value = pair.second;
} else {
entry* new_entry = new entry(pair.first, pair.second);
m_data->entries.push_back(*new_entry);
m_data->index.insert(*new_entry);
}
}
}
const dict::entry* dict::find_entry(const std::string& key) const {
return find_entry(std::string_view(key));
}
const dict::entry* dict::find_entry(std::string_view key) const {
if (!m_data) {
return nullptr;
}
auto it = m_data->index.find(key, m_data->index.hash_function(), m_data->index.key_eq());
if (it != m_data->index.end()) {
return &(*it);
}
return nullptr;
}
const dict::entry* dict::find_entry(const char* key) const {
if (key == nullptr) {
throw std::invalid_argument("键不能为空指针");
}
return find_entry(std::string_view(key));
}
const dict::entry* dict::find_entry(const variant& key) const {
if (!m_data) {
return nullptr;
}
auto it = m_data->index.find(key, m_data->index.hash_function(), m_data->index.key_eq());
if (it != m_data->index.end()) {
return &(*it);
}
return nullptr;
}
const variant& dict::operator[](const std::string& key) const {
return (*this)[std::string_view(key)];
}
const variant& dict::operator[](std::string_view key) const {
const auto* e = find_entry(key);
if (e) {
return e->value;
}
throw std::out_of_range("字典中不存在键: " + std::string(key));
}
const variant& dict::operator[](const char* key) const {
if (key == nullptr) {
throw std::invalid_argument("键不能为空指针");
}
return (*this)[std::string_view(key)];
}
const variant& dict::operator[](const variant& key) const {
const auto* e = find_entry(key);
if (e) {
return e->value;
}
throw std::out_of_range("字典中不存在键: " + key.to_string());
}
const variant& dict::get(const std::string& key, const variant& default_value) const {
return get(std::string_view(key), default_value);
}
const variant& dict::get(std::string_view key, const variant& default_value) const {
const auto* e = find_entry(key);
if (e) {
return e->value;
}
return default_value;
}
const variant& dict::get(const char* key, const variant& default_value) const {
if (key == nullptr) {
throw std::invalid_argument("键不能为空指针");
}
return get(std::string_view(key), default_value);
}
const variant& dict::get(const variant& key, const variant& default_value) const {
const auto* e = find_entry(key);
if (e) {
return e->value;
}
return default_value;
}
bool dict::contains(const std::string& key) const {
return contains(std::string_view(key));
}
bool dict::contains(std::string_view key) const {
return find_entry(key) != nullptr;
}
bool dict::contains(const char* key) const {
if (key == nullptr) {
throw std::invalid_argument("键不能为空指针");
}
return contains(std::string_view(key));
}
bool dict::contains(const variant& key) const {
return find_entry(key) != nullptr;
}
size_t dict::size() const {
if (!m_data) {
return 0;
}
return m_data->entries.size();
}
bool dict::empty() const {
if (!m_data) {
return true;
}
return m_data->entries.empty();
}
dict::const_iterator dict::begin() const {
if (!m_data) {
return {};
}
return m_data->entries.cbegin();
}
dict::const_iterator dict::end() const {
if (!m_data) {
return {};
}
return m_data->entries.cend();
}
dict::const_reverse_iterator dict::rbegin() const {
if (!m_data) {
return {};
}
return dict::const_reverse_iterator(m_data->entries.rbegin());
}
dict::const_reverse_iterator dict::rend() const {
if (!m_data) {
return {};
}
return dict::const_reverse_iterator(m_data->entries.rend());
}
std::vector<variant> dict::keys() const {
if (!m_data) {
return {};
}
std::vector<variant> result;
result.reserve(size());
for (const auto& item : m_data->entries) {
result.push_back(item.key);
}
return result;
}
std::vector<variant> dict::values() const {
if (!m_data) {
return {};
}
std::vector<variant> result;
result.reserve(size());
for (const auto& item : m_data->entries) {
result.push_back(item.value);
}
return result;
}
const dict::entry& dict::at_index(size_t index) const {
if (!m_data || index >= size()) {
throw std::out_of_range("字典索引越界");
}
auto it = m_data->entries.begin();
std::advance(it, index);
return *it;
}
const variant& dict::at(const std::string& key) const {
return at(std::string_view(key));
}
const variant& dict::at(std::string_view key) const {
const auto* e = find_entry(key);
if (!e) {
throw std::out_of_range("字典中不存在键: " + std::string(key));
}
return e->value;
}
const variant& dict::at(const char* key) const {
if (key == nullptr) {
throw std::invalid_argument("键不能为空指针");
}
return at(std::string_view(key));
}
const variant& dict::at(const variant& key) const {
const auto* e = find_entry(key);
if (!e) {
throw std::out_of_range("字典中不存在键: " + key.to_string());
}
return e->value;
}
const variant& dict::at(std::size_t index) const {
const auto* e = find_entry(index);
if (e) {
return e->value;
}
return this->at_index(index).value;
}
int dict::find_entry_index(const entry* e) const {
if (!e || !m_data) {
return -1;
}
int index = 0;
for (auto it = m_data->entries.begin(); it != m_data->entries.end(); ++it, ++index) {
if (&(*it) == e) {
return index;
}
}
return -1;
}
int dict::find_index(const std::string& key) const {
return find_entry_index(find_entry(key));
}
int dict::find_index(std::string_view key) const {
return find_entry_index(find_entry(key));
}
int dict::find_index(const char* key) const {
if (key == nullptr) {
throw std::invalid_argument("键不能为空指针");
}
return find_entry_index(find_entry(key));
}
int dict::find_index(const variant& key) const {
return find_entry_index(find_entry(key));
}
bool dict::operator==(const dict& other) const {
if (m_data == other.m_data) {
return true;
}
if (!m_data || !other.m_data) {
return false;
}
if (size() != other.size()) {
return false;
}
for (const auto& item : m_data->entries) {
const auto* other_entry = other.find_entry(item.key);
if (!other_entry) {
return false;
}
if (!(item.value == other_entry->value)) {
return false;
}
}
return true;
}
dict dict::operator+(const dict& other) const {
dict result;
for (const auto& item : m_data->entries) {
result[item.key] = item.value;
}
for (const auto& item : other.m_data->entries) {
result[item.key] = item.value;
}
return result;
}
void dict::clear() {
if (!m_data) {
return;
}
m_data->index.clear();
m_data->entries.clear_and_dispose([](dict_types::entry* p) {
delete p;
});
}
dict::const_iterator dict::find(const std::string& key) const {
return find(std::string_view(key));
}
dict::const_iterator dict::find(std::string_view key) const {
const auto* e = find_entry(key);
if (!e) {
return end();
}
auto base_it = m_data->entries.iterator_to(*const_cast<entry*>(e));
return const_iterator(iterator(base_it));
}
dict::const_iterator dict::find(const char* key) const {
if (key == nullptr) {
throw std::invalid_argument("键不能为空指针");
}
return find(std::string_view(key));
}
dict::const_iterator dict::find(const variant& key) const {
const auto* e = find_entry(key);
if (!e) {
return end();
}
auto base_it = m_data->entries.iterator_to(*const_cast<entry*>(e));
return const_iterator(iterator(base_it));
}
dict::const_iterator dict::find(std::size_t index) const {
const auto* e = find_entry(index);
if (e) {
auto base_it = m_data->entries.iterator_to(*const_cast<entry*>(e));
return const_iterator(iterator(base_it));
}
if (index >= size()) {
return end();
}
auto base_it = m_data->entries.begin();
std::advance(base_it, index);
return const_iterator(iterator(base_it));
}
variant to_variant(const dict& d) {
variant result;
to_variant(d, result);
return result;
}
size_t dict::hash() const {
if (empty()) {
return 0;
}
size_t h = 0x9e3779b9 ^ size();
const size_t step = (size() >> 5) + 1;
for (size_t l1 = size(); l1 >= step; l1 -= step) {
const auto& e = at_index(l1 - 1);
size_t entry_hash = e.key.hash() ^ e.value.hash();
h = h ^ ((h << 5) + (h >> 2) + entry_hash);
}
return h;
}
std::string dict::to_string() const {
return json::json_encode(*this);
}
dict dict::copy() const {
dict result;
result.m_data = mc::make_shared<data_t>();
for (const auto& entry : *this) {
dict_types::entry* new_entry = new dict_types::entry(entry.key, entry.value);
result.m_data->entries.push_back(*new_entry);
result.m_data->index.insert(*new_entry);
}
return result;
}
dict dict::deep_copy(mc::detail::copy_context* ctx) const {
if (!m_data) {
return dict();
} else if (!ctx) {
mc::detail::copy_context local_ctx;
return deep_copy(&local_ctx);
}
if (ctx->has_copied(m_data.get())) {
dict result;
result.m_data = ctx->get_copied(m_data.get());
return result;
}
dict result;
result.m_data = mc::make_shared<data_t>();
ctx->record_copied(m_data.get(), result.m_data);
for (const auto& entry : *this) {
mc::variant copied_key = entry.key.deep_copy(ctx);
mc::variant copied_value = entry.value.deep_copy(ctx);
dict_types::entry* new_entry = new dict_types::entry(std::move(copied_key), std::move(copied_value));
result.m_data->entries.push_back(*new_entry);
result.m_data->index.insert(*new_entry);
}
return result;
}
}