xref: /llvm-project/llvm/lib/BinaryFormat/MsgPackDocument.cpp (revision 6c62f7cbfb3a4dc12cee2f6c02191b83047321d9)
14ced8de1STim Renouf //===-- MsgPackDocument.cpp - MsgPack Document --------------------------*-===//
24ced8de1STim Renouf //
34ced8de1STim Renouf // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44ced8de1STim Renouf // See https://llvm.org/LICENSE.txt for license information.
54ced8de1STim Renouf // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64ced8de1STim Renouf //
74ced8de1STim Renouf //===----------------------------------------------------------------------===//
84ced8de1STim Renouf ///
94ced8de1STim Renouf /// This file implements a class that exposes a simple in-memory representation
104ced8de1STim Renouf /// of a document of MsgPack objects, that can be read from MsgPack, written to
114ced8de1STim Renouf /// MsgPack, and inspected and modified in memory. This is intended to be a
124ced8de1STim Renouf /// lighter-weight (in terms of memory allocations) replacement for
134ced8de1STim Renouf /// MsgPackTypes.
144ced8de1STim Renouf ///
154ced8de1STim Renouf //===----------------------------------------------------------------------===//
164ced8de1STim Renouf 
174ced8de1STim Renouf #include "llvm/BinaryFormat/MsgPackDocument.h"
184ced8de1STim Renouf #include "llvm/BinaryFormat/MsgPackWriter.h"
194ced8de1STim Renouf 
204ced8de1STim Renouf using namespace llvm;
214ced8de1STim Renouf using namespace msgpack;
224ced8de1STim Renouf 
234ced8de1STim Renouf // Convert this DocNode into an empty array.
convertToArray()244ced8de1STim Renouf void DocNode::convertToArray() { *this = getDocument()->getArrayNode(); }
254ced8de1STim Renouf 
264ced8de1STim Renouf // Convert this DocNode into an empty map.
convertToMap()274ced8de1STim Renouf void DocNode::convertToMap() { *this = getDocument()->getMapNode(); }
284ced8de1STim Renouf 
294ced8de1STim Renouf /// Find the key in the MapDocNode.
find(StringRef S)304ced8de1STim Renouf DocNode::MapTy::iterator MapDocNode::find(StringRef S) {
314ced8de1STim Renouf   return find(getDocument()->getNode(S));
324ced8de1STim Renouf }
334ced8de1STim Renouf 
344ced8de1STim Renouf /// Member access for MapDocNode. The string data must remain valid for the
354ced8de1STim Renouf /// lifetime of the Document.
operator [](StringRef S)364ced8de1STim Renouf DocNode &MapDocNode::operator[](StringRef S) {
374ced8de1STim Renouf   return (*this)[getDocument()->getNode(S)];
384ced8de1STim Renouf }
394ced8de1STim Renouf 
404ced8de1STim Renouf /// Member access for MapDocNode.
operator [](DocNode Key)414ced8de1STim Renouf DocNode &MapDocNode::operator[](DocNode Key) {
424ced8de1STim Renouf   assert(!Key.isEmpty());
43e79d0023STim Renouf   DocNode &N = (*Map)[Key];
44e79d0023STim Renouf   if (N.isEmpty()) {
454ced8de1STim Renouf     // Ensure a new element has its KindAndDoc initialized.
46e79d0023STim Renouf     N = getDocument()->getEmptyNode();
474ced8de1STim Renouf   }
48e79d0023STim Renouf   return N;
494ced8de1STim Renouf }
504ced8de1STim Renouf 
51db16eb33STim Renouf /// Member access for MapDocNode for integer key.
operator [](int Key)52db16eb33STim Renouf DocNode &MapDocNode::operator[](int Key) {
53db16eb33STim Renouf   return (*this)[getDocument()->getNode(Key)];
54db16eb33STim Renouf }
operator [](unsigned Key)55db16eb33STim Renouf DocNode &MapDocNode::operator[](unsigned Key) {
56db16eb33STim Renouf   return (*this)[getDocument()->getNode(Key)];
57db16eb33STim Renouf }
operator [](int64_t Key)58db16eb33STim Renouf DocNode &MapDocNode::operator[](int64_t Key) {
59db16eb33STim Renouf   return (*this)[getDocument()->getNode(Key)];
60db16eb33STim Renouf }
operator [](uint64_t Key)61db16eb33STim Renouf DocNode &MapDocNode::operator[](uint64_t Key) {
62db16eb33STim Renouf   return (*this)[getDocument()->getNode(Key)];
63db16eb33STim Renouf }
64db16eb33STim Renouf 
654ced8de1STim Renouf /// Array element access. This extends the array if necessary.
operator [](size_t Index)664ced8de1STim Renouf DocNode &ArrayDocNode::operator[](size_t Index) {
674ced8de1STim Renouf   if (size() <= Index) {
684ced8de1STim Renouf     // Ensure new elements have their KindAndDoc initialized.
69e79d0023STim Renouf     Array->resize(Index + 1, getDocument()->getEmptyNode());
704ced8de1STim Renouf   }
714ced8de1STim Renouf   return (*Array)[Index];
724ced8de1STim Renouf }
734ced8de1STim Renouf 
74db16eb33STim Renouf // Convenience assignment operators. This only works if the destination
75db16eb33STim Renouf // DocNode has an associated Document, i.e. it was not constructed using the
76db16eb33STim Renouf // default constructor. The string one does not copy, so the string must
77db16eb33STim Renouf // remain valid for the lifetime of the Document. Use fromString to avoid
78db16eb33STim Renouf // that restriction.
operator =(StringRef Val)79db16eb33STim Renouf DocNode &DocNode::operator=(StringRef Val) {
80db16eb33STim Renouf   *this = getDocument()->getNode(Val);
81db16eb33STim Renouf   return *this;
82db16eb33STim Renouf }
operator =(MemoryBufferRef Val)839d52f69aSMartin Dinkov DocNode &DocNode::operator=(MemoryBufferRef Val) {
849d52f69aSMartin Dinkov   *this = getDocument()->getNode(Val);
859d52f69aSMartin Dinkov   return *this;
869d52f69aSMartin Dinkov }
operator =(bool Val)87db16eb33STim Renouf DocNode &DocNode::operator=(bool Val) {
88db16eb33STim Renouf   *this = getDocument()->getNode(Val);
89db16eb33STim Renouf   return *this;
90db16eb33STim Renouf }
operator =(int Val)91db16eb33STim Renouf DocNode &DocNode::operator=(int Val) {
92db16eb33STim Renouf   *this = getDocument()->getNode(Val);
93db16eb33STim Renouf   return *this;
94db16eb33STim Renouf }
operator =(unsigned Val)95db16eb33STim Renouf DocNode &DocNode::operator=(unsigned Val) {
96db16eb33STim Renouf   *this = getDocument()->getNode(Val);
97db16eb33STim Renouf   return *this;
98db16eb33STim Renouf }
operator =(int64_t Val)99db16eb33STim Renouf DocNode &DocNode::operator=(int64_t Val) {
100db16eb33STim Renouf   *this = getDocument()->getNode(Val);
101db16eb33STim Renouf   return *this;
102db16eb33STim Renouf }
operator =(uint64_t Val)103db16eb33STim Renouf DocNode &DocNode::operator=(uint64_t Val) {
104db16eb33STim Renouf   *this = getDocument()->getNode(Val);
105db16eb33STim Renouf   return *this;
106db16eb33STim Renouf }
107db16eb33STim Renouf 
1084ced8de1STim Renouf // A level in the document reading stack.
1094ced8de1STim Renouf struct StackLevel {
StackLevelStackLevel110e79d0023STim Renouf   StackLevel(DocNode Node, size_t StartIndex, size_t Length,
111e79d0023STim Renouf              DocNode *MapEntry = nullptr)
112e79d0023STim Renouf       : Node(Node), Index(StartIndex), End(StartIndex + Length),
113e79d0023STim Renouf         MapEntry(MapEntry) {}
1144ced8de1STim Renouf   DocNode Node;
115e79d0023STim Renouf   size_t Index;
116e79d0023STim Renouf   size_t End;
1174ced8de1STim Renouf   // Points to map entry when we have just processed a map key.
1184ced8de1STim Renouf   DocNode *MapEntry;
119e79d0023STim Renouf   DocNode MapKey;
1204ced8de1STim Renouf };
1214ced8de1STim Renouf 
122e79d0023STim Renouf // Read a document from a binary msgpack blob, merging into anything already in
123e79d0023STim Renouf // the Document.
1244ced8de1STim Renouf // The blob data must remain valid for the lifetime of this Document (because a
1254ced8de1STim Renouf // string object in the document contains a StringRef into the original blob).
1264ced8de1STim Renouf // If Multi, then this sets root to an array and adds top-level objects to it.
1274ced8de1STim Renouf // If !Multi, then it only reads a single top-level object, even if there are
1284ced8de1STim Renouf // more, and sets root to that.
129e79d0023STim Renouf // Returns false if failed due to illegal format or merge error.
130e79d0023STim Renouf 
readFromBlob(StringRef Blob,bool Multi,function_ref<int (DocNode * DestNode,DocNode SrcNode,DocNode MapKey)> Merger)131e79d0023STim Renouf bool Document::readFromBlob(
132e79d0023STim Renouf     StringRef Blob, bool Multi,
133e79d0023STim Renouf     function_ref<int(DocNode *DestNode, DocNode SrcNode, DocNode MapKey)>
134e79d0023STim Renouf         Merger) {
1354ced8de1STim Renouf   msgpack::Reader MPReader(Blob);
1364ced8de1STim Renouf   SmallVector<StackLevel, 4> Stack;
1374ced8de1STim Renouf   if (Multi) {
1384ced8de1STim Renouf     // Create the array for multiple top-level objects.
1394ced8de1STim Renouf     Root = getArrayNode();
140e79d0023STim Renouf     Stack.push_back(StackLevel(Root, 0, (size_t)-1));
1414ced8de1STim Renouf   }
1424ced8de1STim Renouf   do {
1434ced8de1STim Renouf     // On to next element (or key if doing a map key next).
1444ced8de1STim Renouf     // Read the value.
1454ced8de1STim Renouf     Object Obj;
146*6c62f7cbSEmma Pilkington     Expected<bool> ReadObj = MPReader.read(Obj);
147*6c62f7cbSEmma Pilkington     if (!ReadObj) {
148*6c62f7cbSEmma Pilkington       // FIXME: Propagate the Error to the caller.
149*6c62f7cbSEmma Pilkington       consumeError(ReadObj.takeError());
150*6c62f7cbSEmma Pilkington       return false;
151*6c62f7cbSEmma Pilkington     }
152*6c62f7cbSEmma Pilkington     if (!ReadObj.get()) {
1534ced8de1STim Renouf       if (Multi && Stack.size() == 1) {
1544ced8de1STim Renouf         // OK to finish here as we've just done a top-level element with Multi
1554ced8de1STim Renouf         break;
1564ced8de1STim Renouf       }
1574ced8de1STim Renouf       return false; // Finished too early
1584ced8de1STim Renouf     }
1594ced8de1STim Renouf     // Convert it into a DocNode.
1604ced8de1STim Renouf     DocNode Node;
1614ced8de1STim Renouf     switch (Obj.Kind) {
1624ced8de1STim Renouf     case Type::Nil:
1634ced8de1STim Renouf       Node = getNode();
1644ced8de1STim Renouf       break;
1654ced8de1STim Renouf     case Type::Int:
1664ced8de1STim Renouf       Node = getNode(Obj.Int);
1674ced8de1STim Renouf       break;
1684ced8de1STim Renouf     case Type::UInt:
1694ced8de1STim Renouf       Node = getNode(Obj.UInt);
1704ced8de1STim Renouf       break;
1714ced8de1STim Renouf     case Type::Boolean:
1724ced8de1STim Renouf       Node = getNode(Obj.Bool);
1734ced8de1STim Renouf       break;
1744ced8de1STim Renouf     case Type::Float:
1754ced8de1STim Renouf       Node = getNode(Obj.Float);
1764ced8de1STim Renouf       break;
1774ced8de1STim Renouf     case Type::String:
1784ced8de1STim Renouf       Node = getNode(Obj.Raw);
1794ced8de1STim Renouf       break;
1809d52f69aSMartin Dinkov     case Type::Binary:
1819d52f69aSMartin Dinkov       Node = getNode(MemoryBufferRef(Obj.Raw, ""));
1829d52f69aSMartin Dinkov       break;
1834ced8de1STim Renouf     case Type::Map:
1844ced8de1STim Renouf       Node = getMapNode();
1854ced8de1STim Renouf       break;
1864ced8de1STim Renouf     case Type::Array:
1874ced8de1STim Renouf       Node = getArrayNode();
1884ced8de1STim Renouf       break;
1894ced8de1STim Renouf     default:
1904ced8de1STim Renouf       return false; // Raw and Extension not supported
1914ced8de1STim Renouf     }
1924ced8de1STim Renouf 
1934ced8de1STim Renouf     // Store it.
194e79d0023STim Renouf     DocNode *DestNode = nullptr;
1954ced8de1STim Renouf     if (Stack.empty())
196e79d0023STim Renouf       DestNode = &Root;
1974ced8de1STim Renouf     else if (Stack.back().Node.getKind() == Type::Array) {
1984ced8de1STim Renouf       // Reading an array entry.
1994ced8de1STim Renouf       auto &Array = Stack.back().Node.getArray();
200e79d0023STim Renouf       DestNode = &Array[Stack.back().Index++];
2014ced8de1STim Renouf     } else {
2024ced8de1STim Renouf       auto &Map = Stack.back().Node.getMap();
2034ced8de1STim Renouf       if (!Stack.back().MapEntry) {
2044ced8de1STim Renouf         // Reading a map key.
205e79d0023STim Renouf         Stack.back().MapKey = Node;
2064ced8de1STim Renouf         Stack.back().MapEntry = &Map[Node];
207e79d0023STim Renouf         continue;
208e79d0023STim Renouf       }
2094ced8de1STim Renouf       // Reading the value for the map key read in the last iteration.
210e79d0023STim Renouf       DestNode = Stack.back().MapEntry;
2114ced8de1STim Renouf       Stack.back().MapEntry = nullptr;
212e79d0023STim Renouf       ++Stack.back().Index;
2134ced8de1STim Renouf     }
214e79d0023STim Renouf     int MergeResult = 0;
215e79d0023STim Renouf     if (!DestNode->isEmpty()) {
216e79d0023STim Renouf       // In a merge, there is already a value at this position. Call the
217e79d0023STim Renouf       // callback to attempt to resolve the conflict. The resolution must result
218e79d0023STim Renouf       // in an array or map if Node is an array or map respectively.
219e79d0023STim Renouf       DocNode MapKey = !Stack.empty() && !Stack.back().MapKey.isEmpty()
220e79d0023STim Renouf                            ? Stack.back().MapKey
221e79d0023STim Renouf                            : getNode();
222e79d0023STim Renouf       MergeResult = Merger(DestNode, Node, MapKey);
223e79d0023STim Renouf       if (MergeResult < 0)
224e79d0023STim Renouf         return false; // Merge conflict resolution failed
225e79d0023STim Renouf       assert(!((Node.isMap() && !DestNode->isMap()) ||
226e79d0023STim Renouf                (Node.isArray() && !DestNode->isArray())));
227e79d0023STim Renouf     } else
228e79d0023STim Renouf       *DestNode = Node;
2294ced8de1STim Renouf 
2304ced8de1STim Renouf     // See if we're starting a new array or map.
231e79d0023STim Renouf     switch (DestNode->getKind()) {
2324ced8de1STim Renouf     case msgpack::Type::Array:
2334ced8de1STim Renouf     case msgpack::Type::Map:
234e79d0023STim Renouf       Stack.push_back(StackLevel(*DestNode, MergeResult, Obj.Length, nullptr));
2354ced8de1STim Renouf       break;
2364ced8de1STim Renouf     default:
2374ced8de1STim Renouf       break;
2384ced8de1STim Renouf     }
2394ced8de1STim Renouf 
2404ced8de1STim Renouf     // Pop finished stack levels.
2414ced8de1STim Renouf     while (!Stack.empty()) {
242e79d0023STim Renouf       if (Stack.back().MapEntry)
2434ced8de1STim Renouf         break;
244e79d0023STim Renouf       if (Stack.back().Index != Stack.back().End)
2454ced8de1STim Renouf         break;
2464ced8de1STim Renouf       Stack.pop_back();
2474ced8de1STim Renouf     }
2484ced8de1STim Renouf   } while (!Stack.empty());
2494ced8de1STim Renouf   return true;
2504ced8de1STim Renouf }
2514ced8de1STim Renouf 
2524ced8de1STim Renouf struct WriterStackLevel {
2534ced8de1STim Renouf   DocNode Node;
2544ced8de1STim Renouf   DocNode::MapTy::iterator MapIt;
2554ced8de1STim Renouf   DocNode::ArrayTy::iterator ArrayIt;
2564ced8de1STim Renouf   bool OnKey;
2574ced8de1STim Renouf };
2584ced8de1STim Renouf 
2594ced8de1STim Renouf /// Write a MsgPack document to a binary MsgPack blob.
writeToBlob(std::string & Blob)2604ced8de1STim Renouf void Document::writeToBlob(std::string &Blob) {
2614ced8de1STim Renouf   Blob.clear();
2624ced8de1STim Renouf   raw_string_ostream OS(Blob);
2634ced8de1STim Renouf   msgpack::Writer MPWriter(OS);
2644ced8de1STim Renouf   SmallVector<WriterStackLevel, 4> Stack;
2654ced8de1STim Renouf   DocNode Node = getRoot();
2664ced8de1STim Renouf   for (;;) {
2674ced8de1STim Renouf     switch (Node.getKind()) {
2684ced8de1STim Renouf     case Type::Array:
2694ced8de1STim Renouf       MPWriter.writeArraySize(Node.getArray().size());
2704ced8de1STim Renouf       Stack.push_back(
2714ced8de1STim Renouf           {Node, DocNode::MapTy::iterator(), Node.getArray().begin(), false});
2724ced8de1STim Renouf       break;
2734ced8de1STim Renouf     case Type::Map:
2744ced8de1STim Renouf       MPWriter.writeMapSize(Node.getMap().size());
2754ced8de1STim Renouf       Stack.push_back(
2764ced8de1STim Renouf           {Node, Node.getMap().begin(), DocNode::ArrayTy::iterator(), true});
2774ced8de1STim Renouf       break;
2784ced8de1STim Renouf     case Type::Nil:
2794ced8de1STim Renouf       MPWriter.writeNil();
2804ced8de1STim Renouf       break;
2814ced8de1STim Renouf     case Type::Boolean:
2824ced8de1STim Renouf       MPWriter.write(Node.getBool());
2834ced8de1STim Renouf       break;
2844ced8de1STim Renouf     case Type::Int:
2854ced8de1STim Renouf       MPWriter.write(Node.getInt());
2864ced8de1STim Renouf       break;
2874ced8de1STim Renouf     case Type::UInt:
2884ced8de1STim Renouf       MPWriter.write(Node.getUInt());
2894ced8de1STim Renouf       break;
2904ced8de1STim Renouf     case Type::String:
2914ced8de1STim Renouf       MPWriter.write(Node.getString());
2924ced8de1STim Renouf       break;
2939d52f69aSMartin Dinkov     case Type::Binary:
2949d52f69aSMartin Dinkov       MPWriter.write(Node.getBinary());
2959d52f69aSMartin Dinkov       break;
296673f2f70SSebastian Neubauer     case Type::Empty:
297673f2f70SSebastian Neubauer       llvm_unreachable("unhandled empty msgpack node");
2984ced8de1STim Renouf     default:
2994ced8de1STim Renouf       llvm_unreachable("unhandled msgpack object kind");
3004ced8de1STim Renouf     }
3014ced8de1STim Renouf     // Pop finished stack levels.
3024ced8de1STim Renouf     while (!Stack.empty()) {
3034ced8de1STim Renouf       if (Stack.back().Node.getKind() == Type::Map) {
3044ced8de1STim Renouf         if (Stack.back().MapIt != Stack.back().Node.getMap().end())
3054ced8de1STim Renouf           break;
3064ced8de1STim Renouf       } else {
3074ced8de1STim Renouf         if (Stack.back().ArrayIt != Stack.back().Node.getArray().end())
3084ced8de1STim Renouf           break;
3094ced8de1STim Renouf       }
3104ced8de1STim Renouf       Stack.pop_back();
3114ced8de1STim Renouf     }
3124ced8de1STim Renouf     if (Stack.empty())
3134ced8de1STim Renouf       break;
3144ced8de1STim Renouf     // Get the next value.
3154ced8de1STim Renouf     if (Stack.back().Node.getKind() == Type::Map) {
3164ced8de1STim Renouf       if (Stack.back().OnKey) {
3174ced8de1STim Renouf         // Do the key of a key,value pair in a map.
3184ced8de1STim Renouf         Node = Stack.back().MapIt->first;
3194ced8de1STim Renouf         Stack.back().OnKey = false;
3204ced8de1STim Renouf       } else {
3214ced8de1STim Renouf         Node = Stack.back().MapIt->second;
3224ced8de1STim Renouf         ++Stack.back().MapIt;
3234ced8de1STim Renouf         Stack.back().OnKey = true;
3244ced8de1STim Renouf       }
3254ced8de1STim Renouf     } else {
3264ced8de1STim Renouf       Node = *Stack.back().ArrayIt;
3274ced8de1STim Renouf       ++Stack.back().ArrayIt;
3284ced8de1STim Renouf     }
3294ced8de1STim Renouf   }
3304ced8de1STim Renouf }
331