| // 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 |
| |
| fit::result<AddressDescriptor, std::string> AddressDescriptor::Deserialize( |
| fbl::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 fit::error(error.str()); |
| } |
| |
| uint64_t magic = document["magic"].GetUint64(); |
| if (magic != kMagic) { |
| return fit::error("Invalid Magic\n"); |
| } |
| |
| if (!document.HasMember("mappings") || !document["mappings"].IsArray() || |
| document["mappings"].GetArray().Empty()) { |
| return fit::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 fit::ok(descriptor); |
| } |
| |
| fit::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 (auto 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 fit::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 fit::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 |