1 //===-- MsgPackDocument.h - MsgPack Document --------------------*- C++ -*-===// 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 /// \file 9 /// This file declares 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 #ifndef LLVM_BINARYFORMAT_MSGPACKDOCUMENT_H 18 #define LLVM_BINARYFORMAT_MSGPACKDOCUMENT_H 19 20 #include "llvm/BinaryFormat/MsgPackReader.h" 21 #include "llvm/Support/Compiler.h" 22 #include <map> 23 24 namespace llvm { 25 namespace msgpack { 26 27 class ArrayDocNode; 28 class Document; 29 class MapDocNode; 30 31 /// The kind of a DocNode and its owning Document. 32 struct KindAndDocument { 33 Document *Doc; 34 Type Kind; 35 }; 36 37 /// A node in a MsgPack Document. This is a simple copyable and 38 /// passable-by-value type that does not own any memory. 39 class DocNode { 40 friend Document; 41 42 public: 43 typedef std::map<DocNode, DocNode> MapTy; 44 typedef std::vector<DocNode> ArrayTy; 45 46 private: 47 // Using KindAndDocument allows us to squeeze Kind and a pointer to the 48 // owning Document into the same word. Having a pointer to the owning 49 // Document makes the API of DocNode more convenient, and allows its use in 50 // YAMLIO. 51 const KindAndDocument *KindAndDoc; 52 53 protected: 54 // The union of different values. 55 union { 56 int64_t Int; 57 uint64_t UInt; 58 bool Bool; 59 double Float; 60 StringRef Raw; 61 ArrayTy *Array; 62 MapTy *Map; 63 }; 64 65 public: 66 // Default constructor gives an empty node with no associated Document. All 67 // you can do with it is "isEmpty()". DocNode()68 DocNode() : KindAndDoc(nullptr) {} 69 70 // Type methods isMap()71 bool isMap() const { return getKind() == Type::Map; } isArray()72 bool isArray() const { return getKind() == Type::Array; } isScalar()73 bool isScalar() const { return !isMap() && !isArray(); } isString()74 bool isString() const { return getKind() == Type::String; } 75 76 // Accessors. isEmpty() returns true for both a default-constructed DocNode 77 // that has no associated Document, and the result of getEmptyNode(), which 78 // does have an associated document. isEmpty()79 bool isEmpty() const { return !KindAndDoc || getKind() == Type::Empty; } getKind()80 Type getKind() const { return KindAndDoc->Kind; } getDocument()81 Document *getDocument() const { return KindAndDoc->Doc; } 82 getInt()83 int64_t &getInt() { 84 assert(getKind() == Type::Int); 85 return Int; 86 } 87 getUInt()88 uint64_t &getUInt() { 89 assert(getKind() == Type::UInt); 90 return UInt; 91 } 92 getBool()93 bool &getBool() { 94 assert(getKind() == Type::Boolean); 95 return Bool; 96 } 97 getFloat()98 double &getFloat() { 99 assert(getKind() == Type::Float); 100 return Float; 101 } 102 getInt()103 int64_t getInt() const { 104 assert(getKind() == Type::Int); 105 return Int; 106 } 107 getUInt()108 uint64_t getUInt() const { 109 assert(getKind() == Type::UInt); 110 return UInt; 111 } 112 getBool()113 bool getBool() const { 114 assert(getKind() == Type::Boolean); 115 return Bool; 116 } 117 getFloat()118 double getFloat() const { 119 assert(getKind() == Type::Float); 120 return Float; 121 } 122 getString()123 StringRef getString() const { 124 assert(getKind() == Type::String); 125 return Raw; 126 } 127 getBinary()128 MemoryBufferRef getBinary() const { 129 assert(getKind() == Type::Binary); 130 return MemoryBufferRef(Raw, ""); 131 } 132 133 /// Get an ArrayDocNode for an array node. If Convert, convert the node to an 134 /// array node if necessary. 135 ArrayDocNode &getArray(bool Convert = false) { 136 if (getKind() != Type::Array) { 137 assert(Convert); 138 convertToArray(); 139 } 140 // This could be a static_cast, except ArrayDocNode is a forward reference. 141 return *reinterpret_cast<ArrayDocNode *>(this); 142 } 143 144 /// Get a MapDocNode for a map node. If Convert, convert the node to a map 145 /// node if necessary. 146 MapDocNode &getMap(bool Convert = false) { 147 if (getKind() != Type::Map) { 148 assert(Convert); 149 convertToMap(); 150 } 151 // This could be a static_cast, except MapDocNode is a forward reference. 152 return *reinterpret_cast<MapDocNode *>(this); 153 } 154 155 /// Comparison operator, used for map keys. 156 friend bool operator<(const DocNode &Lhs, const DocNode &Rhs) { 157 // This has to cope with one or both of the nodes being default-constructed, 158 // such that KindAndDoc is not set. 159 if (Rhs.isEmpty()) 160 return false; 161 if (Lhs.KindAndDoc != Rhs.KindAndDoc) { 162 if (Lhs.isEmpty()) 163 return true; 164 return (unsigned)Lhs.getKind() < (unsigned)Rhs.getKind(); 165 } 166 switch (Lhs.getKind()) { 167 case Type::Int: 168 return Lhs.Int < Rhs.Int; 169 case Type::UInt: 170 return Lhs.UInt < Rhs.UInt; 171 case Type::Nil: 172 return false; 173 case Type::Boolean: 174 return Lhs.Bool < Rhs.Bool; 175 case Type::Float: 176 return Lhs.Float < Rhs.Float; 177 case Type::String: 178 case Type::Binary: 179 return Lhs.Raw < Rhs.Raw; 180 default: 181 llvm_unreachable("bad map key type"); 182 } 183 } 184 185 /// Equality operator 186 friend bool operator==(const DocNode &Lhs, const DocNode &Rhs) { 187 return !(Lhs < Rhs) && !(Rhs < Lhs); 188 } 189 190 /// Inequality operator 191 friend bool operator!=(const DocNode &Lhs, const DocNode &Rhs) { 192 return !(Lhs == Rhs); 193 } 194 195 /// Convert this node to a string, assuming it is scalar. 196 LLVM_ABI std::string toString() const; 197 198 /// Convert the StringRef and use it to set this DocNode (assuming scalar). If 199 /// it is a string, copy the string into the Document's strings list so we do 200 /// not rely on S having a lifetime beyond this call. Tag is "" or a YAML tag. 201 LLVM_ABI StringRef fromString(StringRef S, StringRef Tag = ""); 202 203 /// Convenience assignment operators. This only works if the destination 204 /// DocNode has an associated Document, i.e. it was not constructed using the 205 /// default constructor. The string one does not copy, so the string must 206 /// remain valid for the lifetime of the Document. Use fromString to avoid 207 /// that restriction. 208 DocNode &operator=(const char *Val) { return *this = StringRef(Val); } 209 LLVM_ABI DocNode &operator=(StringRef Val); 210 LLVM_ABI DocNode &operator=(MemoryBufferRef Val); 211 LLVM_ABI DocNode &operator=(bool Val); 212 LLVM_ABI DocNode &operator=(int Val); 213 LLVM_ABI DocNode &operator=(unsigned Val); 214 LLVM_ABI DocNode &operator=(int64_t Val); 215 LLVM_ABI DocNode &operator=(uint64_t Val); 216 217 private: 218 // Private constructor setting KindAndDoc, used by methods in Document. DocNode(const KindAndDocument * KindAndDoc)219 DocNode(const KindAndDocument *KindAndDoc) : KindAndDoc(KindAndDoc) {} 220 221 LLVM_ABI void convertToArray(); 222 LLVM_ABI void convertToMap(); 223 }; 224 225 /// A DocNode that is a map. 226 class MapDocNode : public DocNode { 227 public: 228 MapDocNode() = default; MapDocNode(DocNode & N)229 MapDocNode(DocNode &N) : DocNode(N) { assert(getKind() == Type::Map); } 230 231 // Map access methods. size()232 size_t size() const { return Map->size(); } empty()233 bool empty() const { return !size(); } begin()234 MapTy::iterator begin() { return Map->begin(); } end()235 MapTy::iterator end() { return Map->end(); } find(DocNode Key)236 MapTy::iterator find(DocNode Key) { return Map->find(Key); } 237 LLVM_ABI MapTy::iterator find(StringRef Key); erase(MapTy::const_iterator I)238 MapTy::iterator erase(MapTy::const_iterator I) { return Map->erase(I); } erase(DocNode Key)239 size_t erase(DocNode Key) { return Map->erase(Key); } erase(MapTy::const_iterator First,MapTy::const_iterator Second)240 MapTy::iterator erase(MapTy::const_iterator First, 241 MapTy::const_iterator Second) { 242 return Map->erase(First, Second); 243 } 244 /// Member access. The string data must remain valid for the lifetime of the 245 /// Document. 246 LLVM_ABI DocNode &operator[](StringRef S); 247 /// Member access, with convenience versions for an integer key. 248 LLVM_ABI DocNode &operator[](DocNode Key); 249 LLVM_ABI DocNode &operator[](int Key); 250 LLVM_ABI DocNode &operator[](unsigned Key); 251 LLVM_ABI DocNode &operator[](int64_t Key); 252 LLVM_ABI DocNode &operator[](uint64_t Key); 253 }; 254 255 /// A DocNode that is an array. 256 class ArrayDocNode : public DocNode { 257 public: 258 ArrayDocNode() = default; ArrayDocNode(DocNode & N)259 ArrayDocNode(DocNode &N) : DocNode(N) { assert(getKind() == Type::Array); } 260 261 // Array access methods. size()262 size_t size() const { return Array->size(); } empty()263 bool empty() const { return !size(); } back()264 DocNode &back() const { return Array->back(); } begin()265 ArrayTy::iterator begin() { return Array->begin(); } end()266 ArrayTy::iterator end() { return Array->end(); } push_back(DocNode N)267 void push_back(DocNode N) { 268 assert(N.isEmpty() || N.getDocument() == getDocument()); 269 Array->push_back(N); 270 } 271 272 /// Element access. This extends the array if necessary, with empty nodes. 273 LLVM_ABI DocNode &operator[](size_t Index); 274 }; 275 276 /// Simple in-memory representation of a document of msgpack objects with 277 /// ability to find and create array and map elements. Does not currently cope 278 /// with any extension types. 279 class Document { 280 // Maps, arrays and strings used by nodes in the document. No attempt is made 281 // to free unused ones. 282 std::vector<std::unique_ptr<DocNode::MapTy>> Maps; 283 std::vector<std::unique_ptr<DocNode::ArrayTy>> Arrays; 284 std::vector<std::unique_ptr<char[]>> Strings; 285 286 // The root node of the document. 287 DocNode Root; 288 289 // The KindAndDocument structs pointed to by nodes in the document. 290 KindAndDocument KindAndDocs[size_t(Type::Empty) + 1]; 291 292 // Whether YAML output uses hex for UInt. 293 bool HexMode = false; 294 295 public: Document()296 Document() { 297 clear(); 298 for (unsigned T = 0; T != unsigned(Type::Empty) + 1; ++T) 299 KindAndDocs[T] = {this, Type(T)}; 300 } 301 302 /// Get ref to the document's root element. getRoot()303 DocNode &getRoot() { return Root; } 304 305 /// Restore the Document to an empty state. clear()306 void clear() { getRoot() = getEmptyNode(); } 307 308 /// Create an empty node associated with this Document. getEmptyNode()309 DocNode getEmptyNode() { 310 auto N = DocNode(&KindAndDocs[size_t(Type::Empty)]); 311 return N; 312 } 313 314 /// Create a nil node associated with this Document. getNode()315 DocNode getNode() { 316 auto N = DocNode(&KindAndDocs[size_t(Type::Nil)]); 317 return N; 318 } 319 320 /// Create an Int node associated with this Document. getNode(int64_t V)321 DocNode getNode(int64_t V) { 322 auto N = DocNode(&KindAndDocs[size_t(Type::Int)]); 323 N.Int = V; 324 return N; 325 } 326 327 /// Create an Int node associated with this Document. getNode(int V)328 DocNode getNode(int V) { 329 auto N = DocNode(&KindAndDocs[size_t(Type::Int)]); 330 N.Int = V; 331 return N; 332 } 333 334 /// Create a UInt node associated with this Document. getNode(uint64_t V)335 DocNode getNode(uint64_t V) { 336 auto N = DocNode(&KindAndDocs[size_t(Type::UInt)]); 337 N.UInt = V; 338 return N; 339 } 340 341 /// Create a UInt node associated with this Document. getNode(unsigned V)342 DocNode getNode(unsigned V) { 343 auto N = DocNode(&KindAndDocs[size_t(Type::UInt)]); 344 N.UInt = V; 345 return N; 346 } 347 348 /// Create a Boolean node associated with this Document. getNode(bool V)349 DocNode getNode(bool V) { 350 auto N = DocNode(&KindAndDocs[size_t(Type::Boolean)]); 351 N.Bool = V; 352 return N; 353 } 354 355 /// Create a Float node associated with this Document. getNode(double V)356 DocNode getNode(double V) { 357 auto N = DocNode(&KindAndDocs[size_t(Type::Float)]); 358 N.Float = V; 359 return N; 360 } 361 362 /// Create a String node associated with this Document. If !Copy, the passed 363 /// string must remain valid for the lifetime of the Document. 364 DocNode getNode(StringRef V, bool Copy = false) { 365 if (Copy) 366 V = addString(V); 367 auto N = DocNode(&KindAndDocs[size_t(Type::String)]); 368 N.Raw = V; 369 return N; 370 } 371 372 /// Create a String node associated with this Document. If !Copy, the passed 373 /// string must remain valid for the lifetime of the Document. 374 DocNode getNode(const char *V, bool Copy = false) { 375 return getNode(StringRef(V), Copy); 376 } 377 378 /// Create a Binary node associated with this Document. If !Copy, the passed 379 /// buffer must remain valid for the lifetime of the Document. 380 DocNode getNode(MemoryBufferRef V, bool Copy = false) { 381 auto Raw = V.getBuffer(); 382 if (Copy) 383 Raw = addString(Raw); 384 auto N = DocNode(&KindAndDocs[size_t(Type::Binary)]); 385 N.Raw = Raw; 386 return N; 387 } 388 389 /// Create an empty Map node associated with this Document. getMapNode()390 MapDocNode getMapNode() { 391 auto N = DocNode(&KindAndDocs[size_t(Type::Map)]); 392 Maps.push_back(std::make_unique<DocNode::MapTy>()); 393 N.Map = Maps.back().get(); 394 return N.getMap(); 395 } 396 397 /// Create an empty Array node associated with this Document. getArrayNode()398 ArrayDocNode getArrayNode() { 399 auto N = DocNode(&KindAndDocs[size_t(Type::Array)]); 400 Arrays.push_back(std::make_unique<DocNode::ArrayTy>()); 401 N.Array = Arrays.back().get(); 402 return N.getArray(); 403 } 404 405 /// Read a document from a binary msgpack blob, merging into anything already 406 /// in the Document. The blob data must remain valid for the lifetime of this 407 /// Document (because a string object in the document contains a StringRef 408 /// into the original blob). If Multi, then this sets root to an array and 409 /// adds top-level objects to it. If !Multi, then it only reads a single 410 /// top-level object, even if there are more, and sets root to that. Returns 411 /// false if failed due to illegal format or merge error. 412 /// 413 /// The Merger arg is a callback function that is called when the merge has a 414 /// conflict, that is, it is trying to set an item that is already set. If the 415 /// conflict cannot be resolved, the callback function returns -1. If the 416 /// conflict can be resolved, the callback returns a non-negative number and 417 /// sets *DestNode to the resolved node. The returned non-negative number is 418 /// significant only for an array node; it is then the array index to start 419 /// populating at. That allows Merger to choose whether to merge array 420 /// elements (returns 0) or append new elements (returns existing size). 421 /// 422 /// If SrcNode is an array or map, the resolution must be that *DestNode is an 423 /// array or map respectively, although it could be the array or map 424 /// (respectively) that was already there. MapKey is the key if *DestNode is a 425 /// map entry, a nil node otherwise. 426 /// 427 /// The default for Merger is to disallow any conflict. 428 LLVM_ABI bool readFromBlob( 429 StringRef Blob, bool Multi, 430 function_ref<int(DocNode *DestNode, DocNode SrcNode, DocNode MapKey)> 431 Merger = [](DocNode *DestNode, DocNode SrcNode, DocNode MapKey) { 432 return -1; 433 }); 434 435 /// Write a MsgPack document to a binary MsgPack blob. 436 LLVM_ABI void writeToBlob(std::string &Blob); 437 438 /// Copy a string into the Document's strings list, and return the copy that 439 /// is owned by the Document. addString(StringRef S)440 StringRef addString(StringRef S) { 441 Strings.push_back(std::unique_ptr<char[]>(new char[S.size()])); 442 memcpy(&Strings.back()[0], S.data(), S.size()); 443 return StringRef(&Strings.back()[0], S.size()); 444 } 445 446 /// Set whether YAML output uses hex for UInt. Default off. 447 void setHexMode(bool Val = true) { HexMode = Val; } 448 449 /// Get Hexmode flag. getHexMode()450 bool getHexMode() const { return HexMode; } 451 452 /// Convert MsgPack Document to YAML text. 453 LLVM_ABI void toYAML(raw_ostream &OS); 454 455 /// Read YAML text into the MsgPack document. Returns false on failure. 456 LLVM_ABI bool fromYAML(StringRef S); 457 }; 458 459 } // namespace msgpack 460 } // namespace llvm 461 462 #endif // LLVM_BINARYFORMAT_MSGPACKDOCUMENT_H 463