xref: /freebsd/contrib/llvm-project/llvm/lib/BinaryFormat/MsgPackDocument.cpp (revision 62cfcf62f627e5093fb37026a6d8c98e4d2ef04c)
1 //===-- MsgPackDocument.cpp - MsgPack Document --------------------------*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// This file implements a class that exposes a simple in-memory representation
10 /// of a document of MsgPack objects, that can be read from MsgPack, written to
11 /// MsgPack, and inspected and modified in memory. This is intended to be a
12 /// lighter-weight (in terms of memory allocations) replacement for
13 /// MsgPackTypes.
14 ///
15 //===----------------------------------------------------------------------===//
16 
17 #include "llvm/BinaryFormat/MsgPackDocument.h"
18 #include "llvm/BinaryFormat/MsgPackWriter.h"
19 
20 using namespace llvm;
21 using namespace msgpack;
22 
23 // Convert this DocNode into an empty array.
24 void DocNode::convertToArray() { *this = getDocument()->getArrayNode(); }
25 
26 // Convert this DocNode into an empty map.
27 void DocNode::convertToMap() { *this = getDocument()->getMapNode(); }
28 
29 /// Find the key in the MapDocNode.
30 DocNode::MapTy::iterator MapDocNode::find(StringRef S) {
31   return find(getDocument()->getNode(S));
32 }
33 
34 /// Member access for MapDocNode. The string data must remain valid for the
35 /// lifetime of the Document.
36 DocNode &MapDocNode::operator[](StringRef S) {
37   return (*this)[getDocument()->getNode(S)];
38 }
39 
40 /// Member access for MapDocNode.
41 DocNode &MapDocNode::operator[](DocNode Key) {
42   assert(!Key.isEmpty());
43   MapTy::value_type Entry(Key, DocNode());
44   auto ItAndInserted = Map->insert(Entry);
45   if (ItAndInserted.second) {
46     // Ensure a new element has its KindAndDoc initialized.
47     ItAndInserted.first->second = getDocument()->getNode();
48   }
49   return ItAndInserted.first->second;
50 }
51 
52 /// Array element access. This extends the array if necessary.
53 DocNode &ArrayDocNode::operator[](size_t Index) {
54   if (size() <= Index) {
55     // Ensure new elements have their KindAndDoc initialized.
56     Array->resize(Index + 1, getDocument()->getNode());
57   }
58   return (*Array)[Index];
59 }
60 
61 // A level in the document reading stack.
62 struct StackLevel {
63   DocNode Node;
64   size_t Length;
65   // Points to map entry when we have just processed a map key.
66   DocNode *MapEntry;
67 };
68 
69 // Read a document from a binary msgpack blob.
70 // The blob data must remain valid for the lifetime of this Document (because a
71 // string object in the document contains a StringRef into the original blob).
72 // If Multi, then this sets root to an array and adds top-level objects to it.
73 // If !Multi, then it only reads a single top-level object, even if there are
74 // more, and sets root to that.
75 // Returns false if failed due to illegal format.
76 bool Document::readFromBlob(StringRef Blob, bool Multi) {
77   msgpack::Reader MPReader(Blob);
78   SmallVector<StackLevel, 4> Stack;
79   if (Multi) {
80     // Create the array for multiple top-level objects.
81     Root = getArrayNode();
82     Stack.push_back(StackLevel({Root, (size_t)-1, nullptr}));
83   }
84   do {
85     // On to next element (or key if doing a map key next).
86     // Read the value.
87     Object Obj;
88     if (!MPReader.read(Obj)) {
89       if (Multi && Stack.size() == 1) {
90         // OK to finish here as we've just done a top-level element with Multi
91         break;
92       }
93       return false; // Finished too early
94     }
95     // Convert it into a DocNode.
96     DocNode Node;
97     switch (Obj.Kind) {
98     case Type::Nil:
99       Node = getNode();
100       break;
101     case Type::Int:
102       Node = getNode(Obj.Int);
103       break;
104     case Type::UInt:
105       Node = getNode(Obj.UInt);
106       break;
107     case Type::Boolean:
108       Node = getNode(Obj.Bool);
109       break;
110     case Type::Float:
111       Node = getNode(Obj.Float);
112       break;
113     case Type::String:
114       Node = getNode(Obj.Raw);
115       break;
116     case Type::Map:
117       Node = getMapNode();
118       break;
119     case Type::Array:
120       Node = getArrayNode();
121       break;
122     default:
123       return false; // Raw and Extension not supported
124     }
125 
126     // Store it.
127     if (Stack.empty())
128       Root = Node;
129     else if (Stack.back().Node.getKind() == Type::Array) {
130       // Reading an array entry.
131       auto &Array = Stack.back().Node.getArray();
132       Array.push_back(Node);
133     } else {
134       auto &Map = Stack.back().Node.getMap();
135       if (!Stack.back().MapEntry) {
136         // Reading a map key.
137         Stack.back().MapEntry = &Map[Node];
138       } else {
139         // Reading the value for the map key read in the last iteration.
140         *Stack.back().MapEntry = Node;
141         Stack.back().MapEntry = nullptr;
142       }
143     }
144 
145     // See if we're starting a new array or map.
146     switch (Node.getKind()) {
147     case msgpack::Type::Array:
148     case msgpack::Type::Map:
149       Stack.push_back(StackLevel({Node, Obj.Length, nullptr}));
150       break;
151     default:
152       break;
153     }
154 
155     // Pop finished stack levels.
156     while (!Stack.empty()) {
157       if (Stack.back().Node.getKind() == msgpack::Type::Array) {
158         if (Stack.back().Node.getArray().size() != Stack.back().Length)
159           break;
160       } else {
161         if (Stack.back().MapEntry ||
162             Stack.back().Node.getMap().size() != Stack.back().Length)
163           break;
164       }
165       Stack.pop_back();
166     }
167   } while (!Stack.empty());
168   return true;
169 }
170 
171 struct WriterStackLevel {
172   DocNode Node;
173   DocNode::MapTy::iterator MapIt;
174   DocNode::ArrayTy::iterator ArrayIt;
175   bool OnKey;
176 };
177 
178 /// Write a MsgPack document to a binary MsgPack blob.
179 void Document::writeToBlob(std::string &Blob) {
180   Blob.clear();
181   raw_string_ostream OS(Blob);
182   msgpack::Writer MPWriter(OS);
183   SmallVector<WriterStackLevel, 4> Stack;
184   DocNode Node = getRoot();
185   for (;;) {
186     switch (Node.getKind()) {
187     case Type::Array:
188       MPWriter.writeArraySize(Node.getArray().size());
189       Stack.push_back(
190           {Node, DocNode::MapTy::iterator(), Node.getArray().begin(), false});
191       break;
192     case Type::Map:
193       MPWriter.writeMapSize(Node.getMap().size());
194       Stack.push_back(
195           {Node, Node.getMap().begin(), DocNode::ArrayTy::iterator(), true});
196       break;
197     case Type::Nil:
198       MPWriter.writeNil();
199       break;
200     case Type::Boolean:
201       MPWriter.write(Node.getBool());
202       break;
203     case Type::Int:
204       MPWriter.write(Node.getInt());
205       break;
206     case Type::UInt:
207       MPWriter.write(Node.getUInt());
208       break;
209     case Type::String:
210       MPWriter.write(Node.getString());
211       break;
212     default:
213       llvm_unreachable("unhandled msgpack object kind");
214     }
215     // Pop finished stack levels.
216     while (!Stack.empty()) {
217       if (Stack.back().Node.getKind() == Type::Map) {
218         if (Stack.back().MapIt != Stack.back().Node.getMap().end())
219           break;
220       } else {
221         if (Stack.back().ArrayIt != Stack.back().Node.getArray().end())
222           break;
223       }
224       Stack.pop_back();
225     }
226     if (Stack.empty())
227       break;
228     // Get the next value.
229     if (Stack.back().Node.getKind() == Type::Map) {
230       if (Stack.back().OnKey) {
231         // Do the key of a key,value pair in a map.
232         Node = Stack.back().MapIt->first;
233         Stack.back().OnKey = false;
234       } else {
235         Node = Stack.back().MapIt->second;
236         ++Stack.back().MapIt;
237         Stack.back().OnKey = true;
238       }
239     } else {
240       Node = *Stack.back().ArrayIt;
241       ++Stack.back().ArrayIt;
242     }
243   }
244 }
245 
246