BLI: add utility methods for serialization

This simplifies the code that works with the `BLI_serialize.hh` header.
The various `lookup` methods do a linear search. If there are only a
few elements that can even be faster than building the map first.
In the future it might be nice to transparently build and cache the
map internally if necessary.
This commit is contained in:
Jacques Lucke 2023-04-28 13:30:11 +02:00
parent 5c184525ed
commit bfcc2b1c4c
4 changed files with 197 additions and 25 deletions

View File

@ -87,12 +87,7 @@ template<typename T, eValueType V> class PrimitiveValue;
using IntValue = PrimitiveValue<int64_t, eValueType::Int>;
using DoubleValue = PrimitiveValue<double, eValueType::Double>;
using BooleanValue = PrimitiveValue<bool, eValueType::Boolean>;
template<typename Container, eValueType V, typename ContainerItem = typename Container::value_type>
class ContainerValue;
/* ArrayValue stores its items as shared pointer as it shares data with a lookup table that can
* be created by calling `create_lookup`. */
using ArrayValue = ContainerValue<Vector<std::shared_ptr<Value>>, eValueType::Array>;
class ArrayValue;
/**
* Class containing a (de)serializable value.
@ -214,7 +209,7 @@ template<
eValueType V,
/** Type of the data inside the container. */
typename ContainerItem>
typename ContainerItem = typename Container::value_type>
class ContainerValue : public Value {
public:
using Items = Container;
@ -237,6 +232,18 @@ class ContainerValue : public Value {
}
};
class ArrayValue : public ContainerValue<Vector<std::shared_ptr<Value>>, eValueType::Array> {
public:
void append(std::shared_ptr<Value> value);
void append_bool(bool value);
void append_int(int value);
void append_double(double value);
void append_str(std::string value);
void append_null();
std::shared_ptr<DictionaryValue> append_dict();
std::shared_ptr<ArrayValue> append_array();
};
/**
* Internal storage type for DictionaryValue.
*
@ -260,14 +267,21 @@ class DictionaryValue
*
* The lookup is owned by the caller.
*/
const Lookup create_lookup() const
{
Lookup result;
for (const Item &item : elements()) {
result.add_as(item.first, item.second);
}
return result;
}
const Lookup create_lookup() const;
const std::shared_ptr<Value> *lookup(const StringRef key) const;
std::optional<StringRefNull> lookup_str(const StringRef key) const;
std::optional<int64_t> lookup_int(const StringRef key) const;
std::optional<double> lookup_double(const StringRef key) const;
const DictionaryValue *lookup_dict(const StringRef key) const;
const ArrayValue *lookup_array(const StringRef key) const;
void append(std::string key, std::shared_ptr<Value> value);
void append_int(std::string key, int64_t value);
void append_double(std::string key, double value);
void append_str(std::string key, std::string value);
std::shared_ptr<DictionaryValue> append_dict(std::string key);
std::shared_ptr<ArrayValue> append_array(std::string key);
};
/**
@ -300,4 +314,7 @@ class JsonFormatter : public Formatter {
std::unique_ptr<Value> deserialize(std::istream &is) override;
};
void write_json_file(StringRef path, const Value &value);
std::shared_ptr<Value> read_json_file(StringRef path);
} // namespace blender::io::serialize

View File

@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_fileops.hh"
#include "BLI_serialize.hh"
#include "json.hpp"
@ -197,6 +198,149 @@ static std::unique_ptr<Value> convert_from_json(const nlohmann::ordered_json &j)
return std::make_unique<NullValue>();
}
void ArrayValue::append(std::shared_ptr<Value> value)
{
this->elements().append(std::move(value));
}
void ArrayValue::append_bool(const bool value)
{
this->append(std::make_shared<BooleanValue>(value));
}
void ArrayValue::append_int(const int value)
{
this->append(std::make_shared<IntValue>(value));
}
void ArrayValue::append_double(const double value)
{
this->append(std::make_shared<DoubleValue>(value));
}
void ArrayValue::append_str(std::string value)
{
this->append(std::make_shared<StringValue>(std::move(value)));
}
void ArrayValue::append_null()
{
this->append(std::make_shared<NullValue>());
}
std::shared_ptr<DictionaryValue> ArrayValue::append_dict()
{
auto value = std::make_shared<DictionaryValue>();
this->append(value);
return value;
}
std::shared_ptr<ArrayValue> ArrayValue::append_array()
{
auto value = std::make_shared<ArrayValue>();
this->append(value);
return value;
}
const DictionaryValue::Lookup DictionaryValue::create_lookup() const
{
Lookup result;
for (const Item &item : elements()) {
result.add_as(item.first, item.second);
}
return result;
}
const std::shared_ptr<Value> *DictionaryValue::lookup(const StringRef key) const
{
for (const auto &item : this->elements()) {
if (item.first == key) {
return &item.second;
}
}
return nullptr;
}
std::optional<StringRefNull> DictionaryValue::lookup_str(const StringRef key) const
{
if (const std::shared_ptr<Value> *value = this->lookup(key)) {
if (const StringValue *str_value = (*value)->as_string_value()) {
return StringRefNull(str_value->value());
}
}
return std::nullopt;
}
std::optional<int64_t> DictionaryValue::lookup_int(const StringRef key) const
{
if (const std::shared_ptr<Value> *value = this->lookup(key)) {
if (const IntValue *int_value = (*value)->as_int_value()) {
return int_value->value();
}
}
return std::nullopt;
}
std::optional<double> DictionaryValue::lookup_double(const StringRef key) const
{
if (const std::shared_ptr<Value> *value = this->lookup(key)) {
if (const DoubleValue *double_value = (*value)->as_double_value()) {
return double_value->value();
}
}
return std::nullopt;
}
const DictionaryValue *DictionaryValue::lookup_dict(const StringRef key) const
{
if (const std::shared_ptr<Value> *value = this->lookup(key)) {
return (*value)->as_dictionary_value();
}
return nullptr;
}
const ArrayValue *DictionaryValue::lookup_array(const StringRef key) const
{
if (const std::shared_ptr<Value> *value = this->lookup(key)) {
return (*value)->as_array_value();
}
return nullptr;
}
void DictionaryValue::append(std::string key, std::shared_ptr<Value> value)
{
this->elements().append({std::move(key), std::move(value)});
}
void DictionaryValue::append_int(std::string key, const int64_t value)
{
this->append(std::move(key), std::make_shared<IntValue>(value));
}
void DictionaryValue::append_double(std::string key, const double value)
{
this->append(std::move(key), std::make_shared<DoubleValue>(value));
}
void DictionaryValue::append_str(std::string key, const std::string value)
{
this->append(std::move(key), std::make_shared<StringValue>(value));
}
std::shared_ptr<DictionaryValue> DictionaryValue::append_dict(std::string key)
{
auto value = std::make_shared<DictionaryValue>();
this->append(std::move(key), value);
return value;
}
std::shared_ptr<ArrayValue> DictionaryValue::append_array(std::string key)
{
auto value = std::make_shared<ArrayValue>();
this->append(std::move(key), value);
return value;
}
void JsonFormatter::serialize(std::ostream &os, const Value &value)
{
nlohmann::ordered_json j;
@ -216,4 +360,18 @@ std::unique_ptr<Value> JsonFormatter::deserialize(std::istream &is)
return convert_from_json(j);
}
void write_json_file(const StringRef path, const Value &value)
{
JsonFormatter formatter;
fstream stream(path, std::ios::out);
formatter.serialize(stream, value);
}
std::shared_ptr<Value> read_json_file(const StringRef path)
{
JsonFormatter formatter;
fstream stream(path, std::ios::in);
return formatter.deserialize(stream);
}
} // namespace blender::io::serialize

View File

@ -78,12 +78,11 @@ TEST(serialize, array_to_json)
JsonFormatter json;
std::stringstream out;
ArrayValue value_array;
ArrayValue::Items &array = value_array.elements();
array.append_as(new IntValue(42));
array.append_as(new StringValue("Hello JSON"));
array.append_as(new NullValue);
array.append_as(new BooleanValue(false));
array.append_as(new BooleanValue(true));
value_array.append_int(42);
value_array.append_str("Hello JSON");
value_array.append_null();
value_array.append_bool(false);
value_array.append_bool(true);
json.serialize(out, value_array);
EXPECT_EQ(out.str(), "[42,\"Hello JSON\",null,false,true]");
@ -94,8 +93,7 @@ TEST(serialize, object_to_json)
JsonFormatter json;
std::stringstream out;
DictionaryValue value_object;
DictionaryValue::Items &attributes = value_object.elements();
attributes.append_as(std::pair(std::string("best_number"), new IntValue(42)));
value_object.append_int("best_number", 42);
json.serialize(out, value_object);
EXPECT_EQ(out.str(), "{\"best_number\":42}");

View File

@ -657,8 +657,7 @@ struct AssetIndex {
AssetIndex(const FileIndexerEntries &indexer_entries)
{
std::unique_ptr<DictionaryValue> root = std::make_unique<DictionaryValue>();
DictionaryValue::Items &root_attributes = root->elements();
root_attributes.append_as(std::pair(ATTRIBUTE_VERSION, new IntValue(CURRENT_VERSION)));
root->append_int(ATTRIBUTE_VERSION, CURRENT_VERSION);
init_value_from_file_indexer_entries(*root, indexer_entries);
contents = std::move(root);