1 //===-- MsgPackDocument.cpp - MsgPack Document --------------------------*-===//
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
7 //===----------------------------------------------------------------------===//
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
15 //===----------------------------------------------------------------------===//
17 #include "llvm/BinaryFormat/MsgPackDocument.h"
18 #include "llvm/BinaryFormat/MsgPackWriter.h"
21 using namespace msgpack
;
23 // Convert this DocNode into an empty array.
24 void DocNode::convertToArray() { *this = getDocument()->getArrayNode(); }
26 // Convert this DocNode into an empty map.
27 void DocNode::convertToMap() { *this = getDocument()->getMapNode(); }
29 /// Find the key in the MapDocNode.
30 DocNode::MapTy::iterator
MapDocNode::find(StringRef S
) {
31 return find(getDocument()->getNode(S
));
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
)];
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();
49 return ItAndInserted
.first
->second
;
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());
58 return (*Array
)[Index
];
61 // A level in the document reading stack.
65 // Points to map entry when we have just processed a map key.
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
;
80 // Create the array for multiple top-level objects.
81 Root
= getArrayNode();
82 Stack
.push_back(StackLevel({Root
, (size_t)-1, nullptr}));
85 // On to next element (or key if doing a map key next).
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
93 return false; // Finished too early
95 // Convert it into a DocNode.
102 Node
= getNode(Obj
.Int
);
105 Node
= getNode(Obj
.UInt
);
108 Node
= getNode(Obj
.Bool
);
111 Node
= getNode(Obj
.Float
);
114 Node
= getNode(Obj
.Raw
);
120 Node
= getArrayNode();
123 return false; // Raw and Extension not supported
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
);
134 auto &Map
= Stack
.back().Node
.getMap();
135 if (!Stack
.back().MapEntry
) {
136 // Reading a map key.
137 Stack
.back().MapEntry
= &Map
[Node
];
139 // Reading the value for the map key read in the last iteration.
140 *Stack
.back().MapEntry
= Node
;
141 Stack
.back().MapEntry
= nullptr;
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}));
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
)
161 if (Stack
.back().MapEntry
||
162 Stack
.back().Node
.getMap().size() != Stack
.back().Length
)
167 } while (!Stack
.empty());
171 struct WriterStackLevel
{
173 DocNode::MapTy::iterator MapIt
;
174 DocNode::ArrayTy::iterator ArrayIt
;
178 /// Write a MsgPack document to a binary MsgPack blob.
179 void Document::writeToBlob(std::string
&Blob
) {
181 raw_string_ostream
OS(Blob
);
182 msgpack::Writer
MPWriter(OS
);
183 SmallVector
<WriterStackLevel
, 4> Stack
;
184 DocNode Node
= getRoot();
186 switch (Node
.getKind()) {
188 MPWriter
.writeArraySize(Node
.getArray().size());
190 {Node
, DocNode::MapTy::iterator(), Node
.getArray().begin(), false});
193 MPWriter
.writeMapSize(Node
.getMap().size());
195 {Node
, Node
.getMap().begin(), DocNode::ArrayTy::iterator(), true});
201 MPWriter
.write(Node
.getBool());
204 MPWriter
.write(Node
.getInt());
207 MPWriter
.write(Node
.getUInt());
210 MPWriter
.write(Node
.getString());
213 llvm_unreachable("unhandled msgpack object kind");
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())
221 if (Stack
.back().ArrayIt
!= Stack
.back().Node
.getArray().end())
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;
235 Node
= Stack
.back().MapIt
->second
;
236 ++Stack
.back().MapIt
;
237 Stack
.back().OnKey
= true;
240 Node
= *Stack
.back().ArrayIt
;
241 ++Stack
.back().ArrayIt
;