blob: 962a0457808d51e2a6224cd412b182a154b7e082 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/storage/volume_image/address_descriptor.h"
#include <sstream>
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
namespace storage::volume_image {
namespace {
rapidjson::Value ToValue(AddressMap map, rapidjson::Document::AllocatorType* allocator) {
rapidjson::Value value = {};
value.SetObject();
value.AddMember("source", map.source, *allocator);
value.AddMember("target", map.target, *allocator);
value.AddMember("count", map.count, *allocator);
if (map.size.has_value()) {
value.AddMember("size", map.size.value(), *allocator);
}
if (!map.options.empty()) {
rapidjson::Value option_map;
option_map.SetObject();
for (const auto& [key, value] : map.options) {
rapidjson::Value value_key(key, *allocator);
option_map.AddMember(value_key, value, *allocator);
}
value.AddMember("options", option_map, *allocator);
}
return value;
}
AddressMap FromValue(const rapidjson::Value& value) {
AddressMap map = {};
map.source = value["source"].GetUint64();
map.target = value["target"].GetUint64();
map.count = value["count"].GetUint64();
map.size = std::nullopt;
if (value.HasMember("size") && value["size"].IsUint64()) {
map.size = value["size"].GetUint64();
}
if (value.HasMember("options") && value["options"].IsObject()) {
const auto& option_map = value["options"].GetObject();
for (const auto& option : option_map) {
map.options[option.name.GetString()] = option.value.GetUint64();
}
}
return map;
}
} // namespace
fpromise::result<AddressDescriptor, std::string> AddressDescriptor::Deserialize(
cpp20::span<const uint8_t> serialized) {
rapidjson::Document document;
rapidjson::ParseResult result =
document.Parse(reinterpret_cast<const char*>(serialized.data()), serialized.size());
if (result.IsError()) {
std::ostringstream error;
error << "Error parsing serialized AddressDescriptor. "
<< rapidjson::GetParseError_En(result.Code()) << std::endl;
return fpromise::error(error.str());
}
uint64_t magic = document["magic"].GetUint64();
if (magic != kMagic) {
return fpromise::error("Invalid Magic\n");
}
if (!document.HasMember("mappings") || !document["mappings"].IsArray() ||
document["mappings"].GetArray().Empty()) {
return fpromise::error("AddressDescriptor must contain a non empty array field 'mapping'.\n");
}
AddressDescriptor descriptor = {};
const auto& mapping_array = document["mappings"].GetArray();
for (auto& mapping : mapping_array) {
descriptor.mappings.push_back(FromValue(mapping));
}
return fpromise::ok(descriptor);
}
fpromise::result<std::vector<uint8_t>, std::string> AddressDescriptor::Serialize() const {
rapidjson::Document document;
document.SetObject();
document.AddMember("magic", kMagic, document.GetAllocator());
rapidjson::Value value_mappings;
value_mappings.SetArray();
for (const AddressMap& mapping : mappings) {
value_mappings.PushBack(ToValue(mapping, &document.GetAllocator()), document.GetAllocator());
}
document.AddMember("mappings", value_mappings, document.GetAllocator());
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
if (!document.Accept(writer)) {
return fpromise::error("Failed to obtain string representation of AddressDescriptor.\n");
}
const auto* serialized_content = reinterpret_cast<const uint8_t*>(buffer.GetString());
std::vector<uint8_t> data(serialized_content, serialized_content + buffer.GetLength());
data.push_back('\0');
return fpromise::ok(data);
}
std::string AddressMap::DebugString() const {
std::string debug_string =
"\n{\n"
" source: " +
std::to_string(source) + "\n" + " target: " + std::to_string(target) + "\n" +
" count: " + std::to_string(count) + "\n" +
" size: " + (size.has_value() ? std::to_string(size.value()) : "std::nullopt") + "\n" +
" options: {\n";
for (const auto& option : options) {
debug_string += " " + option.first + ": " + std::to_string(option.second) + "\n";
}
debug_string += " }\n";
debug_string += "}\n";
return debug_string;
}
} // namespace storage::volume_image