25#include "palimpsest/mpack/eigen.h"
29using exceptions::KeyError;
30using exceptions::PalimpsestError;
31using exceptions::TypeError;
32using mpack::mpack_node_matrix3d;
33using mpack::mpack_node_quaterniond;
34using mpack::mpack_node_vector2d;
35using mpack::mpack_node_vector3d;
36using mpack::mpack_node_vectorXd;
51 mpack_tree_init_data(&tree, data,
size);
52 mpack_tree_parse(&tree);
53 const auto status = mpack_tree_error(&tree);
54 if (status != mpack_ok) {
55 spdlog::error(
"MPack tree error: \"{}\", skipping Dictionary::deserialize",
56 mpack_error_to_string(status));
59 deserialize_(mpack_tree_root(&tree));
60 mpack_tree_destroy(&tree);
63void Dictionary::deserialize_(mpack_node_t node) {
64 if (mpack_node_type(node) == mpack_type_nil) {
74 if (mpack_node_type(node) != mpack_type_map) {
75 throw TypeError(__FILE__, __LINE__,
76 std::string(
"Expecting a map, not ") +
77 mpack_type_to_string(mpack_node_type(node)));
80 for (
size_t i = 0; i < mpack_node_map_count(node); ++i) {
81 const mpack_node_t key_node = mpack_node_map_key_at(node, i);
82 const mpack_node_t value_node = mpack_node_map_value_at(node, i);
83 const std::string key = {mpack_node_str(key_node),
84 mpack_node_strlen(key_node)};
85 auto it =
map_.find(key);
86 if (it ==
map_.end()) {
87 this->insert_at_key_(key, value_node);
90 it->second->deserialize_(value_node);
91 }
catch (
const TypeError &e) {
92 throw TypeError(e,
" ← at key \"" + key +
"\"");
98void Dictionary::insert_at_key_(
const std::string &key,
99 const mpack_node_t &value) {
100 switch (mpack_node_type(value)) {
101 case mpack_type_bool:
102 this->insert<bool>(key, mpack_node_bool(value));
105 this->insert<int>(key, mpack_node_int(value));
107 case mpack_type_uint:
108 this->insert<unsigned>(key, mpack_node_uint(value));
110 case mpack_type_float:
111 this->insert<float>(key, mpack_node_float(value));
113 case mpack_type_double:
114 this->insert<double>(key, mpack_node_double(value));
117 this->insert<std::string>(
118 key, std::string{mpack_node_str(value), mpack_node_strlen(value)});
120 case mpack_type_array: {
121 size_t length = mpack_node_array_length(value);
123 throw TypeError(__FILE__, __LINE__,
124 std::string(
"Cannot deserialize an empty list "
125 "(precludes type inference) at key \"") +
128 mpack_node_t first_item = mpack_node_array_at(value, 0);
129 mpack_type_t array_type = mpack_node_type(first_item);
130 if (array_type == mpack_type_double) {
133 this->insert<Eigen::Vector2d>(key, mpack_node_vector2d(value));
136 this->insert<Eigen::Vector3d>(key, mpack_node_vector3d(value));
139 this->insert<Eigen::Quaterniond>(key,
140 mpack_node_quaterniond(value));
143 this->insert<Eigen::Matrix3d>(key, mpack_node_matrix3d(value));
146 this->insert<Eigen::VectorXd>(key, mpack_node_vectorXd(value));
149 }
else if (array_type == mpack_type_array) {
152 this->insert<std::vector<Eigen::VectorXd>>(key, length);
153 for (
unsigned index = 0; index < length; ++index) {
154 mpack_node_t sub_array = mpack_node_array_at(value, index);
155 mpack_type_t sub_type = mpack_node_type(sub_array);
156 if (sub_type != mpack_type_array) {
157 throw TypeError(__FILE__, __LINE__,
158 std::string(
"Encountered non-array item ") +
159 mpack_type_to_string(sub_type) +
160 " while parsing array of arrays at key \"" +
163 unsigned sub_length = mpack_node_array_length(sub_array);
164 Eigen::VectorXd &vector = new_vec_vec[index];
165 vector.resize(sub_length);
166 for (Eigen::Index j = 0; j < sub_length; ++j) {
167 vector(j) = mpack_node_double(mpack_node_array_at(sub_array, j));
171 throw TypeError(__FILE__, __LINE__,
172 std::string(
"Unsupported array of ") +
173 mpack_type_to_string(array_type) +
174 " elements encountered at key \"" + key +
"\"");
184 throw TypeError(__FILE__, __LINE__,
185 std::string(
"Cannot insert values of type ") +
186 mpack_type_to_string(mpack_node_type(value)));
202 std::vector<char> this_buffer, other_buffer;
203 size_t this_size = this->
serialize(this_buffer);
204 size_t other_size = other.
serialize(other_buffer);
207 if (this_size != other_size ||
208 std::memcmp(this_buffer.data(), other_buffer.data(), this_size) !=
222 for (
const auto &key_child :
map_) {
223 const std::string &key = key_child.first;
224 const Dictionary &this_child = *key_child.second;
227 auto other_it = other.
map_.find(key);
228 if (other_it == other.
map_.end()) {
230 result(key).
update(this_child);
233 const Dictionary &other_child = *other_it->second;
238 result(key) = std::move(child_diff);
247 std::vector<std::string> out;
248 out.reserve(
map_.size());
249 for (
const auto &key_child :
map_) {
250 out.push_back(key_child.first);
255std::vector<std::pair<std::string, std::reference_wrapper<const Dictionary>>>
257 std::vector<std::pair<std::string, std::reference_wrapper<const Dictionary>>>
259 out.reserve(
map_.size());
260 for (
const auto &key_child :
map_) {
261 out.emplace_back(key_child.first, std::cref(*key_child.second));
268 std::vector<std::reference_wrapper<const Dictionary>> out;
269 out.reserve(
map_.size());
270 for (
const auto &key_child :
map_) {
271 out.emplace_back(std::cref(*key_child.second));
281 "Cannot pop an item from non-dictionary object of type \"") +
285 throw KeyError(
"", __FILE__, __LINE__,
"popitem(): dictionary is empty");
289 auto it =
map_.begin();
290 std::string key = it->first;
296 return std::make_pair(std::move(key), std::move(value));
300 auto it = map_.find(key);
301 if (it == map_.end()) {
302 spdlog::error(
"[Dictionary::remove] No key to remove at \"{}\"", key);
311 "Cannot look up at key \"" + key +
312 "\" in non-dictionary object of type \"" +
315 auto [it, _] =
map_.try_emplace(key, std::make_unique<Dictionary>());
322 "Cannot lookup at key \"" + key +
323 "\" in non-dictionary object of type \"" +
326 const auto it =
map_.find(key);
327 if (it ==
map_.end()) {
328 throw KeyError(key, __FILE__, __LINE__,
329 "Since the dictionary is const it cannot be created.");
336 input.open(filename, std::ifstream::binary | std::ios::ate);
337 std::streamsize
size = input.tellg();
338 input.seekg(0, std::ios::beg);
339 std::vector<char> buffer(
size);
340 input.read(buffer.data(),
size);
346 std::vector<char> buffer;
349 std::ofstream output;
350 output.open(filename, std::ofstream::binary);
351 output.write(buffer.data(),
static_cast<int>(
size));
356 mpack::Writer writer(buffer);
358 return writer.finish();
361void Dictionary::serialize_(mpack::Writer &writer)
const {
367 writer.start_map(
size);
368 for (
const auto &key_child :
map_) {
369 const auto &key = key_child.first;
370 const auto &child = *key_child.second;
372 child.serialize_(writer);
377const Value &Dictionary::get_child_value_(
const std::string &key)
const {
378 const auto it =
map_.find(key);
379 if (it ==
map_.end()) {
380 throw KeyError(key, __FILE__, __LINE__,
"");
381 }
else if (!it->second->is_value()) {
382 throw TypeError(__FILE__, __LINE__,
383 "Child at key \"" + key +
"\" is not a value");
385 return it->second->value_;
395 bool is_first =
true;
396 for (
const auto &key_child : dict.
map_) {
397 const auto &key = key_child.first;
398 const auto &child = key_child.second;
404 stream <<
"\"" << key <<
"\": " << *child;
433 for (
const auto &key_child : other.
map_) {
434 const std::string &key = key_child.first;
435 const Dictionary &other_child = *key_child.second;
437 auto it =
map_.find(key);
438 if (it ==
map_.end()) {
440 map_[key] = std::make_unique<Dictionary>();
441 map_[key]->update(other_child);
445 it->second->update(other_child);
Dictionary of values and sub-dictionaries.
Value value_
Internal value, used if we are a value.
std::unordered_map< std::string, std::unique_ptr< Dictionary > > map_
Key-value map, used if we are a map.
void read(const std::string &filename)
Update dictionary from a MessagePack binary file.
bool is_map() const noexcept
We are a (potentially empty) map if and only if the value is empty.
void deserialize(const char *data, size_t size)
Update dictionary from raw MessagePack data.
unsigned size() const noexcept
Return the number of keys in the dictionary.
std::pair< std::string, Dictionary > popitem()
Remove and return a (key, value) pair from the dictionary.
bool is_empty() const noexcept
We are empty if and only if we are a dictionary with no element.
static Dictionary deepcopy(const Dictionary &other)
Create a deep copy of an existing dictionary.
Dictionary()=default
Default constructor.
void remove(const std::string &key) noexcept
Remove a key-value pair from the dictionary.
void update(const Dictionary &other)
Update dictionary from another dictionary.
Dictionary & operator()(const std::string &key)
Return a reference to the dictionary at key, performing an insertion if such a key does not already e...
size_t serialize(std::vector< char > &buffer) const
Serialize to raw MessagePack data.
std::vector< std::string > keys() const noexcept
Return the list of keys of the dictionary.
std::vector< std::pair< std::string, std::reference_wrapper< const Dictionary > > > items() const noexcept
Return an iterable view of the dictionary's (key, value) pairs.
void write(const std::string &filename) const
Write MessagePack serialization to a binary file.
void clear() noexcept
Remove all entries from the dictionary.
Dictionary difference(const Dictionary &other) const
Compute the difference between this dictionary and another.
std::vector< std::reference_wrapper< const Dictionary > > values() const noexcept
Return an iterable view of the dictionary's values.
bool is_value() const noexcept
We are a value if and only if the internal value is non-empty.
void deserialize(mpack_node_t node)
Update value from an MPack node.
void print(std::ostream &stream) const
Print value to an output stream;.
void serialize(mpack::Writer &writer) const
Serialize value to a MessagePack writer.
const char *(* type_name)()
Function returning the name of the object's type.
Requested dictionary key is not found.
Requested type doesn't match the one already in the dictionary.
std::ostream & operator<<(std::ostream &stream, const Dictionary &dict)