| //===-- MsgPackDocument.cpp - MsgPack Document --------------------------*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// This file implements a class that exposes a simple in-memory representation |
| /// of a document of MsgPack objects, that can be read from MsgPack, written to |
| /// MsgPack, and inspected and modified in memory. This is intended to be a |
| /// lighter-weight (in terms of memory allocations) replacement for |
| /// MsgPackTypes. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/BinaryFormat/MsgPackDocument.h" |
| #include "llvm/BinaryFormat/MsgPackWriter.h" |
| |
| using namespace llvm; |
| using namespace msgpack; |
| |
| // Convert this DocNode into an empty array. |
| void DocNode::convertToArray() { *this = getDocument()->getArrayNode(); } |
| |
| // Convert this DocNode into an empty map. |
| void DocNode::convertToMap() { *this = getDocument()->getMapNode(); } |
| |
| /// Find the key in the MapDocNode. |
| DocNode::MapTy::iterator MapDocNode::find(StringRef S) { |
| return find(getDocument()->getNode(S)); |
| } |
| |
| /// Member access for MapDocNode. The string data must remain valid for the |
| /// lifetime of the Document. |
| DocNode &MapDocNode::operator[](StringRef S) { |
| return (*this)[getDocument()->getNode(S)]; |
| } |
| |
| /// Member access for MapDocNode. |
| DocNode &MapDocNode::operator[](DocNode Key) { |
| assert(!Key.isEmpty()); |
| DocNode &N = (*Map)[Key]; |
| if (N.isEmpty()) { |
| // Ensure a new element has its KindAndDoc initialized. |
| N = getDocument()->getEmptyNode(); |
| } |
| return N; |
| } |
| |
| /// Member access for MapDocNode for integer key. |
| DocNode &MapDocNode::operator[](int Key) { |
| return (*this)[getDocument()->getNode(Key)]; |
| } |
| DocNode &MapDocNode::operator[](unsigned Key) { |
| return (*this)[getDocument()->getNode(Key)]; |
| } |
| DocNode &MapDocNode::operator[](int64_t Key) { |
| return (*this)[getDocument()->getNode(Key)]; |
| } |
| DocNode &MapDocNode::operator[](uint64_t Key) { |
| return (*this)[getDocument()->getNode(Key)]; |
| } |
| |
| /// Array element access. This extends the array if necessary. |
| DocNode &ArrayDocNode::operator[](size_t Index) { |
| if (size() <= Index) { |
| // Ensure new elements have their KindAndDoc initialized. |
| Array->resize(Index + 1, getDocument()->getEmptyNode()); |
| } |
| return (*Array)[Index]; |
| } |
| |
| // Convenience assignment operators. This only works if the destination |
| // DocNode has an associated Document, i.e. it was not constructed using the |
| // default constructor. The string one does not copy, so the string must |
| // remain valid for the lifetime of the Document. Use fromString to avoid |
| // that restriction. |
| DocNode &DocNode::operator=(StringRef Val) { |
| *this = getDocument()->getNode(Val); |
| return *this; |
| } |
| DocNode &DocNode::operator=(bool Val) { |
| *this = getDocument()->getNode(Val); |
| return *this; |
| } |
| DocNode &DocNode::operator=(int Val) { |
| *this = getDocument()->getNode(Val); |
| return *this; |
| } |
| DocNode &DocNode::operator=(unsigned Val) { |
| *this = getDocument()->getNode(Val); |
| return *this; |
| } |
| DocNode &DocNode::operator=(int64_t Val) { |
| *this = getDocument()->getNode(Val); |
| return *this; |
| } |
| DocNode &DocNode::operator=(uint64_t Val) { |
| *this = getDocument()->getNode(Val); |
| return *this; |
| } |
| |
| // A level in the document reading stack. |
| struct StackLevel { |
| StackLevel(DocNode Node, size_t StartIndex, size_t Length, |
| DocNode *MapEntry = nullptr) |
| : Node(Node), Index(StartIndex), End(StartIndex + Length), |
| MapEntry(MapEntry) {} |
| DocNode Node; |
| size_t Index; |
| size_t End; |
| // Points to map entry when we have just processed a map key. |
| DocNode *MapEntry; |
| DocNode MapKey; |
| }; |
| |
| // Read a document from a binary msgpack blob, merging into anything already in |
| // the Document. |
| // The blob data must remain valid for the lifetime of this Document (because a |
| // string object in the document contains a StringRef into the original blob). |
| // If Multi, then this sets root to an array and adds top-level objects to it. |
| // If !Multi, then it only reads a single top-level object, even if there are |
| // more, and sets root to that. |
| // Returns false if failed due to illegal format or merge error. |
| |
| bool Document::readFromBlob( |
| StringRef Blob, bool Multi, |
| function_ref<int(DocNode *DestNode, DocNode SrcNode, DocNode MapKey)> |
| Merger) { |
| msgpack::Reader MPReader(Blob); |
| SmallVector<StackLevel, 4> Stack; |
| if (Multi) { |
| // Create the array for multiple top-level objects. |
| Root = getArrayNode(); |
| Stack.push_back(StackLevel(Root, 0, (size_t)-1)); |
| } |
| do { |
| // On to next element (or key if doing a map key next). |
| // Read the value. |
| Object Obj; |
| if (!MPReader.read(Obj)) { |
| if (Multi && Stack.size() == 1) { |
| // OK to finish here as we've just done a top-level element with Multi |
| break; |
| } |
| return false; // Finished too early |
| } |
| // Convert it into a DocNode. |
| DocNode Node; |
| switch (Obj.Kind) { |
| case Type::Nil: |
| Node = getNode(); |
| break; |
| case Type::Int: |
| Node = getNode(Obj.Int); |
| break; |
| case Type::UInt: |
| Node = getNode(Obj.UInt); |
| break; |
| case Type::Boolean: |
| Node = getNode(Obj.Bool); |
| break; |
| case Type::Float: |
| Node = getNode(Obj.Float); |
| break; |
| case Type::String: |
| Node = getNode(Obj.Raw); |
| break; |
| case Type::Map: |
| Node = getMapNode(); |
| break; |
| case Type::Array: |
| Node = getArrayNode(); |
| break; |
| default: |
| return false; // Raw and Extension not supported |
| } |
| |
| // Store it. |
| DocNode *DestNode = nullptr; |
| if (Stack.empty()) |
| DestNode = &Root; |
| else if (Stack.back().Node.getKind() == Type::Array) { |
| // Reading an array entry. |
| auto &Array = Stack.back().Node.getArray(); |
| DestNode = &Array[Stack.back().Index++]; |
| } else { |
| auto &Map = Stack.back().Node.getMap(); |
| if (!Stack.back().MapEntry) { |
| // Reading a map key. |
| Stack.back().MapKey = Node; |
| Stack.back().MapEntry = &Map[Node]; |
| continue; |
| } |
| // Reading the value for the map key read in the last iteration. |
| DestNode = Stack.back().MapEntry; |
| Stack.back().MapEntry = nullptr; |
| ++Stack.back().Index; |
| } |
| int MergeResult = 0; |
| if (!DestNode->isEmpty()) { |
| // In a merge, there is already a value at this position. Call the |
| // callback to attempt to resolve the conflict. The resolution must result |
| // in an array or map if Node is an array or map respectively. |
| DocNode MapKey = !Stack.empty() && !Stack.back().MapKey.isEmpty() |
| ? Stack.back().MapKey |
| : getNode(); |
| MergeResult = Merger(DestNode, Node, MapKey); |
| if (MergeResult < 0) |
| return false; // Merge conflict resolution failed |
| assert(!((Node.isMap() && !DestNode->isMap()) || |
| (Node.isArray() && !DestNode->isArray()))); |
| } else |
| *DestNode = Node; |
| |
| // See if we're starting a new array or map. |
| switch (DestNode->getKind()) { |
| case msgpack::Type::Array: |
| case msgpack::Type::Map: |
| Stack.push_back(StackLevel(*DestNode, MergeResult, Obj.Length, nullptr)); |
| break; |
| default: |
| break; |
| } |
| |
| // Pop finished stack levels. |
| while (!Stack.empty()) { |
| if (Stack.back().MapEntry) |
| break; |
| if (Stack.back().Index != Stack.back().End) |
| break; |
| Stack.pop_back(); |
| } |
| } while (!Stack.empty()); |
| return true; |
| } |
| |
| struct WriterStackLevel { |
| DocNode Node; |
| DocNode::MapTy::iterator MapIt; |
| DocNode::ArrayTy::iterator ArrayIt; |
| bool OnKey; |
| }; |
| |
| /// Write a MsgPack document to a binary MsgPack blob. |
| void Document::writeToBlob(std::string &Blob) { |
| Blob.clear(); |
| raw_string_ostream OS(Blob); |
| msgpack::Writer MPWriter(OS); |
| SmallVector<WriterStackLevel, 4> Stack; |
| DocNode Node = getRoot(); |
| for (;;) { |
| switch (Node.getKind()) { |
| case Type::Array: |
| MPWriter.writeArraySize(Node.getArray().size()); |
| Stack.push_back( |
| {Node, DocNode::MapTy::iterator(), Node.getArray().begin(), false}); |
| break; |
| case Type::Map: |
| MPWriter.writeMapSize(Node.getMap().size()); |
| Stack.push_back( |
| {Node, Node.getMap().begin(), DocNode::ArrayTy::iterator(), true}); |
| break; |
| case Type::Nil: |
| MPWriter.writeNil(); |
| break; |
| case Type::Boolean: |
| MPWriter.write(Node.getBool()); |
| break; |
| case Type::Int: |
| MPWriter.write(Node.getInt()); |
| break; |
| case Type::UInt: |
| MPWriter.write(Node.getUInt()); |
| break; |
| case Type::String: |
| MPWriter.write(Node.getString()); |
| break; |
| case Type::Empty: |
| llvm_unreachable("unhandled empty msgpack node"); |
| default: |
| llvm_unreachable("unhandled msgpack object kind"); |
| } |
| // Pop finished stack levels. |
| while (!Stack.empty()) { |
| if (Stack.back().Node.getKind() == Type::Map) { |
| if (Stack.back().MapIt != Stack.back().Node.getMap().end()) |
| break; |
| } else { |
| if (Stack.back().ArrayIt != Stack.back().Node.getArray().end()) |
| break; |
| } |
| Stack.pop_back(); |
| } |
| if (Stack.empty()) |
| break; |
| // Get the next value. |
| if (Stack.back().Node.getKind() == Type::Map) { |
| if (Stack.back().OnKey) { |
| // Do the key of a key,value pair in a map. |
| Node = Stack.back().MapIt->first; |
| Stack.back().OnKey = false; |
| } else { |
| Node = Stack.back().MapIt->second; |
| ++Stack.back().MapIt; |
| Stack.back().OnKey = true; |
| } |
| } else { |
| Node = *Stack.back().ArrayIt; |
| ++Stack.back().ArrayIt; |
| } |
| } |
| } |