Path: blob/main/contrib/llvm-project/llvm/lib/BinaryFormat/MsgPackDocument.cpp
35232 views
//===-- MsgPackDocument.cpp - MsgPack Document --------------------------*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// This file implements a class that exposes a simple in-memory representation9/// of a document of MsgPack objects, that can be read from MsgPack, written to10/// MsgPack, and inspected and modified in memory. This is intended to be a11/// lighter-weight (in terms of memory allocations) replacement for12/// MsgPackTypes.13///14//===----------------------------------------------------------------------===//1516#include "llvm/BinaryFormat/MsgPackDocument.h"17#include "llvm/BinaryFormat/MsgPackWriter.h"1819using namespace llvm;20using namespace msgpack;2122// Convert this DocNode into an empty array.23void DocNode::convertToArray() { *this = getDocument()->getArrayNode(); }2425// Convert this DocNode into an empty map.26void DocNode::convertToMap() { *this = getDocument()->getMapNode(); }2728/// Find the key in the MapDocNode.29DocNode::MapTy::iterator MapDocNode::find(StringRef S) {30return find(getDocument()->getNode(S));31}3233/// Member access for MapDocNode. The string data must remain valid for the34/// lifetime of the Document.35DocNode &MapDocNode::operator[](StringRef S) {36return (*this)[getDocument()->getNode(S)];37}3839/// Member access for MapDocNode.40DocNode &MapDocNode::operator[](DocNode Key) {41assert(!Key.isEmpty());42DocNode &N = (*Map)[Key];43if (N.isEmpty()) {44// Ensure a new element has its KindAndDoc initialized.45N = getDocument()->getEmptyNode();46}47return N;48}4950/// Member access for MapDocNode for integer key.51DocNode &MapDocNode::operator[](int Key) {52return (*this)[getDocument()->getNode(Key)];53}54DocNode &MapDocNode::operator[](unsigned Key) {55return (*this)[getDocument()->getNode(Key)];56}57DocNode &MapDocNode::operator[](int64_t Key) {58return (*this)[getDocument()->getNode(Key)];59}60DocNode &MapDocNode::operator[](uint64_t Key) {61return (*this)[getDocument()->getNode(Key)];62}6364/// Array element access. This extends the array if necessary.65DocNode &ArrayDocNode::operator[](size_t Index) {66if (size() <= Index) {67// Ensure new elements have their KindAndDoc initialized.68Array->resize(Index + 1, getDocument()->getEmptyNode());69}70return (*Array)[Index];71}7273// Convenience assignment operators. This only works if the destination74// DocNode has an associated Document, i.e. it was not constructed using the75// default constructor. The string one does not copy, so the string must76// remain valid for the lifetime of the Document. Use fromString to avoid77// that restriction.78DocNode &DocNode::operator=(StringRef Val) {79*this = getDocument()->getNode(Val);80return *this;81}82DocNode &DocNode::operator=(MemoryBufferRef Val) {83*this = getDocument()->getNode(Val);84return *this;85}86DocNode &DocNode::operator=(bool Val) {87*this = getDocument()->getNode(Val);88return *this;89}90DocNode &DocNode::operator=(int Val) {91*this = getDocument()->getNode(Val);92return *this;93}94DocNode &DocNode::operator=(unsigned Val) {95*this = getDocument()->getNode(Val);96return *this;97}98DocNode &DocNode::operator=(int64_t Val) {99*this = getDocument()->getNode(Val);100return *this;101}102DocNode &DocNode::operator=(uint64_t Val) {103*this = getDocument()->getNode(Val);104return *this;105}106107// A level in the document reading stack.108struct StackLevel {109StackLevel(DocNode Node, size_t StartIndex, size_t Length,110DocNode *MapEntry = nullptr)111: Node(Node), Index(StartIndex), End(StartIndex + Length),112MapEntry(MapEntry) {}113DocNode Node;114size_t Index;115size_t End;116// Points to map entry when we have just processed a map key.117DocNode *MapEntry;118DocNode MapKey;119};120121// Read a document from a binary msgpack blob, merging into anything already in122// the Document.123// The blob data must remain valid for the lifetime of this Document (because a124// string object in the document contains a StringRef into the original blob).125// If Multi, then this sets root to an array and adds top-level objects to it.126// If !Multi, then it only reads a single top-level object, even if there are127// more, and sets root to that.128// Returns false if failed due to illegal format or merge error.129130bool Document::readFromBlob(131StringRef Blob, bool Multi,132function_ref<int(DocNode *DestNode, DocNode SrcNode, DocNode MapKey)>133Merger) {134msgpack::Reader MPReader(Blob);135SmallVector<StackLevel, 4> Stack;136if (Multi) {137// Create the array for multiple top-level objects.138Root = getArrayNode();139Stack.push_back(StackLevel(Root, 0, (size_t)-1));140}141do {142// On to next element (or key if doing a map key next).143// Read the value.144Object Obj;145Expected<bool> ReadObj = MPReader.read(Obj);146if (!ReadObj) {147// FIXME: Propagate the Error to the caller.148consumeError(ReadObj.takeError());149return false;150}151if (!ReadObj.get()) {152if (Multi && Stack.size() == 1) {153// OK to finish here as we've just done a top-level element with Multi154break;155}156return false; // Finished too early157}158// Convert it into a DocNode.159DocNode Node;160switch (Obj.Kind) {161case Type::Nil:162Node = getNode();163break;164case Type::Int:165Node = getNode(Obj.Int);166break;167case Type::UInt:168Node = getNode(Obj.UInt);169break;170case Type::Boolean:171Node = getNode(Obj.Bool);172break;173case Type::Float:174Node = getNode(Obj.Float);175break;176case Type::String:177Node = getNode(Obj.Raw);178break;179case Type::Binary:180Node = getNode(MemoryBufferRef(Obj.Raw, ""));181break;182case Type::Map:183Node = getMapNode();184break;185case Type::Array:186Node = getArrayNode();187break;188default:189return false; // Raw and Extension not supported190}191192// Store it.193DocNode *DestNode = nullptr;194if (Stack.empty())195DestNode = &Root;196else if (Stack.back().Node.getKind() == Type::Array) {197// Reading an array entry.198auto &Array = Stack.back().Node.getArray();199DestNode = &Array[Stack.back().Index++];200} else {201auto &Map = Stack.back().Node.getMap();202if (!Stack.back().MapEntry) {203// Reading a map key.204Stack.back().MapKey = Node;205Stack.back().MapEntry = &Map[Node];206continue;207}208// Reading the value for the map key read in the last iteration.209DestNode = Stack.back().MapEntry;210Stack.back().MapEntry = nullptr;211++Stack.back().Index;212}213int MergeResult = 0;214if (!DestNode->isEmpty()) {215// In a merge, there is already a value at this position. Call the216// callback to attempt to resolve the conflict. The resolution must result217// in an array or map if Node is an array or map respectively.218DocNode MapKey = !Stack.empty() && !Stack.back().MapKey.isEmpty()219? Stack.back().MapKey220: getNode();221MergeResult = Merger(DestNode, Node, MapKey);222if (MergeResult < 0)223return false; // Merge conflict resolution failed224assert(!((Node.isMap() && !DestNode->isMap()) ||225(Node.isArray() && !DestNode->isArray())));226} else227*DestNode = Node;228229// See if we're starting a new array or map.230switch (DestNode->getKind()) {231case msgpack::Type::Array:232case msgpack::Type::Map:233Stack.push_back(StackLevel(*DestNode, MergeResult, Obj.Length, nullptr));234break;235default:236break;237}238239// Pop finished stack levels.240while (!Stack.empty()) {241if (Stack.back().MapEntry)242break;243if (Stack.back().Index != Stack.back().End)244break;245Stack.pop_back();246}247} while (!Stack.empty());248return true;249}250251struct WriterStackLevel {252DocNode Node;253DocNode::MapTy::iterator MapIt;254DocNode::ArrayTy::iterator ArrayIt;255bool OnKey;256};257258/// Write a MsgPack document to a binary MsgPack blob.259void Document::writeToBlob(std::string &Blob) {260Blob.clear();261raw_string_ostream OS(Blob);262msgpack::Writer MPWriter(OS);263SmallVector<WriterStackLevel, 4> Stack;264DocNode Node = getRoot();265for (;;) {266switch (Node.getKind()) {267case Type::Array:268MPWriter.writeArraySize(Node.getArray().size());269Stack.push_back(270{Node, DocNode::MapTy::iterator(), Node.getArray().begin(), false});271break;272case Type::Map:273MPWriter.writeMapSize(Node.getMap().size());274Stack.push_back(275{Node, Node.getMap().begin(), DocNode::ArrayTy::iterator(), true});276break;277case Type::Nil:278MPWriter.writeNil();279break;280case Type::Boolean:281MPWriter.write(Node.getBool());282break;283case Type::Int:284MPWriter.write(Node.getInt());285break;286case Type::UInt:287MPWriter.write(Node.getUInt());288break;289case Type::String:290MPWriter.write(Node.getString());291break;292case Type::Binary:293MPWriter.write(Node.getBinary());294break;295case Type::Empty:296llvm_unreachable("unhandled empty msgpack node");297default:298llvm_unreachable("unhandled msgpack object kind");299}300// Pop finished stack levels.301while (!Stack.empty()) {302if (Stack.back().Node.getKind() == Type::Map) {303if (Stack.back().MapIt != Stack.back().Node.getMap().end())304break;305} else {306if (Stack.back().ArrayIt != Stack.back().Node.getArray().end())307break;308}309Stack.pop_back();310}311if (Stack.empty())312break;313// Get the next value.314if (Stack.back().Node.getKind() == Type::Map) {315if (Stack.back().OnKey) {316// Do the key of a key,value pair in a map.317Node = Stack.back().MapIt->first;318Stack.back().OnKey = false;319} else {320Node = Stack.back().MapIt->second;321++Stack.back().MapIt;322Stack.back().OnKey = true;323}324} else {325Node = *Stack.back().ArrayIt;326++Stack.back().ArrayIt;327}328}329}330331332