Index: include/llvm/BinaryFormat/MsgPackTypes.h =================================================================== --- /dev/null +++ include/llvm/BinaryFormat/MsgPackTypes.h @@ -0,0 +1,372 @@ +//===- MsgPackTypes.h - MsgPack Types ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// \file +/// This is a data structure for representing MessagePack "documents", with +/// methods to go to and from MessagePack. The types also specialize YAMLIO +/// traits in order to go to and from YAML. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/Optional.h" +#include "llvm/BinaryFormat/MsgPackReader.h" +#include "llvm/BinaryFormat/MsgPackWriter.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/YAMLTraits.h" +#include + +#ifndef LLVM_BINARYFORMAT_MSGPACKTYPES_H +#define LLVM_BINARYFORMAT_MSGPACKTYPES_H + +namespace llvm { +namespace msgpack { + +class Node; + +/// Short-hand for a Node pointer. +using NodePtr = std::shared_ptr; + +/// Short-hand for an Optional Node pointer. +using OptNodePtr = Optional; + +/// Abstract base-class which can be any MessagePack type. +class Node { +public: + enum NodeKind { + NK_Scalar, + NK_Array, + NK_Map, + }; + +private: + virtual void anchor() = 0; + const NodeKind Kind; + + static Expected readArray(Reader &MPReader, size_t Length); + static Expected readMap(Reader &MPReader, size_t Length); + +public: + NodeKind getKind() const { return Kind; } + + /// Construct a Node. Used by derived classes to track kind information. + Node(NodeKind Kind) : Kind(Kind) {} + + virtual ~Node() = default; + + /// Read from a MessagePack reader \p MPReader, returning an error if one is + /// encountered, or None if \p MPReader is at the end of stream, or some Node + /// pointer if some type is read. + static Expected read(Reader &MPReader); + + /// Write to a MessagePack writer \p MPWriter. + virtual void write(Writer &MPWriter) = 0; +}; + +/// A MessagePack scalar. +class ScalarNode : public Node { +public: + enum ScalarKind { + SK_Int, + SK_UInt, + SK_Nil, + SK_Boolean, + SK_Float, + SK_String, + SK_Binary, + }; + +private: + void anchor() override; + + void destroy(); + + ScalarKind SKind; + + union { + int64_t IntValue; + uint64_t UIntValue; + bool BoolValue; + double FloatValue; + std::string StringValue; + }; + +public: + /// Construct an Int ScalarNode. + ScalarNode(int64_t IntValue); + /// Construct an Int ScalarNode. + ScalarNode(int32_t IntValue); + /// Construct an UInt ScalarNode. + ScalarNode(uint64_t UIntValue); + /// Construct an UInt ScalarNode. + ScalarNode(uint32_t UIntValue); + /// Construct a Nil ScalarNode. + ScalarNode(); + /// Construct a Boolean ScalarNode. + ScalarNode(bool BoolValue); + /// Construct a Float ScalarNode. + ScalarNode(double FloatValue); + /// Construct a String ScalarNode. + ScalarNode(StringRef StringValue); + /// Construct a String ScalarNode. + ScalarNode(const char *StringValue); + /// Construct a String ScalarNode. + ScalarNode(std::string &&StringValue); + /// Construct a Binary ScalarNode. + ScalarNode(MemoryBufferRef BinaryValue); + + ~ScalarNode(); + + ScalarNode &operator=(const ScalarNode &RHS) = delete; + /// A ScalarNode can only be move assigned. + ScalarNode &operator=(ScalarNode &&RHS); + + /// Change the kind of this ScalarNode, zero initializing it to the new type. + void setScalarKind(ScalarKind SKind) { + switch (SKind) { + case SK_Int: + *this = int64_t(0); + break; + case SK_UInt: + *this = uint64_t(0); + break; + case SK_Boolean: + *this = false; + break; + case SK_Float: + *this = 0.0; + break; + case SK_String: + *this = StringRef(); + break; + case SK_Binary: + *this = MemoryBufferRef("", ""); + break; + case SK_Nil: + *this = ScalarNode(); + break; + } + } + + /// Get the current kind of ScalarNode. + ScalarKind getScalarKind() { return SKind; } + + /// Get the value of an Int scalar. + /// + /// \warning Assumes getScalarKind() == SK_Int + int64_t getInt() { + assert(SKind == SK_Int); + return IntValue; + } + + /// Get the value of a UInt scalar. + /// + /// \warning Assumes getScalarKind() == SK_UInt + uint64_t getUInt() { + assert(SKind == SK_UInt); + return UIntValue; + } + + /// Get the value of an Boolean scalar. + /// + /// \warning Assumes getScalarKind() == SK_Boolean + bool getBool() { + assert(SKind == SK_Boolean); + return BoolValue; + } + + /// Get the value of an Float scalar. + /// + /// \warning Assumes getScalarKind() == SK_Float + double getFloat() { + assert(SKind == SK_Float); + return FloatValue; + } + + /// Get the value of a String scalar. + /// + /// \warning Assumes getScalarKind() == SK_String + StringRef getString() { + assert(SKind == SK_String); + return StringValue; + } + + /// Get the value of a Binary scalar. + /// + /// \warning Assumes getScalarKind() == SK_Binary + StringRef getBinary() { + assert(SKind == SK_Binary); + return StringValue; + } + + static bool classof(const Node *N) { return N->getKind() == NK_Scalar; } + + void write(Writer &MPWriter) override; + + /// Parse a YAML scalar of the current ScalarKind from \p ScalarStr. + /// + /// \returns An empty string on success, otherwise an error message. + StringRef inputYAML(StringRef ScalarStr); + + /// Output a YAML scalar of the current ScalarKind into \p OS. + void outputYAML(raw_ostream &OS) const; + + /// Determine which YAML quoting type the current value would need when + /// output. + yaml::QuotingType mustQuoteYAML(StringRef ScalarStr) const; + + /// Get the YAML tag for the current ScalarKind. + StringRef getYAMLTag() const; + + /// Flag which affects how the type handles YAML tags when reading and + /// writing. + /// + /// When false, tags are used when reading and writing. When reading, the tag + /// is used to decide the ScalarKind before parsing. When writing, the tag is + /// output along with the value. + /// + /// When true, tags are ignored when reading and writing. When reading, the + /// ScalarKind is always assumed to be String. When writing, the tag is not + /// output. + bool IgnoreTag = false; + + static const char *IntTag; + static const char *NilTag; + static const char *BooleanTag; + static const char *FloatTag; + static const char *StringTag; + static const char *BinaryTag; +}; + +class ArrayNode : public Node, public std::vector { + void anchor() override; + +public: + ArrayNode() : Node(NK_Array) {} + static bool classof(const Node *N) { return N->getKind() == NK_Array; } + + void write(Writer &MPWriter) override { + MPWriter.writeArraySize(this->size()); + for (auto &N : *this) + N->write(MPWriter); + } +}; + +class MapNode : public Node, public StringMap { + void anchor() override; + +public: + MapNode() : Node(NK_Map) {} + static bool classof(const Node *N) { return N->getKind() == NK_Map; } + + void write(Writer &MPWriter) override { + MPWriter.writeMapSize(this->size()); + for (auto &N : *this) { + MPWriter.write(N.first()); + N.second->write(MPWriter); + } + } +}; + +} // end namespace msgpack + +namespace yaml { + +template <> struct PolymorphicTraits { + static NodeKind getKind(const msgpack::NodePtr &N) { + if (isa(*N)) + return NodeKind::Scalar; + if (isa(*N)) + return NodeKind::Map; + if (isa(*N)) + return NodeKind::Sequence; + llvm_unreachable("NodeKind not supported"); + } + static msgpack::ScalarNode &getAsScalar(msgpack::NodePtr &N) { + if (!N || !isa(*N)) + N.reset(new msgpack::ScalarNode()); + return *cast(N.get()); + } + static msgpack::MapNode &getAsMap(msgpack::NodePtr &N) { + if (!N || !isa(*N)) + N.reset(new msgpack::MapNode()); + return *cast(N.get()); + } + static msgpack::ArrayNode &getAsSequence(msgpack::NodePtr &N) { + if (!N || !isa(*N)) + N.reset(new msgpack::ArrayNode()); + return *cast(N.get()); + } +}; + +template <> struct TaggedScalarTraits { + static void output(const msgpack::ScalarNode &S, void *Ctxt, + raw_ostream &ScalarOS, raw_ostream &TagOS) { + if (!S.IgnoreTag) + TagOS << S.getYAMLTag(); + S.outputYAML(ScalarOS); + } + + static StringRef input(StringRef ScalarStr, StringRef Tag, void *Ctxt, + msgpack::ScalarNode &S) { + if (Tag == msgpack::ScalarNode::IntTag) { + S.setScalarKind(msgpack::ScalarNode::SK_UInt); + if (S.inputYAML(ScalarStr) == StringRef()) + return StringRef(); + S.setScalarKind(msgpack::ScalarNode::SK_Int); + return S.inputYAML(ScalarStr); + } + + if (S.IgnoreTag || Tag == msgpack::ScalarNode::StringTag || + Tag == "tag:yaml.org,2002:str") + S.setScalarKind(msgpack::ScalarNode::SK_String); + else if (Tag == msgpack::ScalarNode::NilTag) + S.setScalarKind(msgpack::ScalarNode::SK_Nil); + else if (Tag == msgpack::ScalarNode::BooleanTag) + S.setScalarKind(msgpack::ScalarNode::SK_Boolean); + else if (Tag == msgpack::ScalarNode::FloatTag) + S.setScalarKind(msgpack::ScalarNode::SK_Float); + else if (Tag == msgpack::ScalarNode::StringTag) + S.setScalarKind(msgpack::ScalarNode::SK_String); + else if (Tag == msgpack::ScalarNode::BinaryTag) + S.setScalarKind(msgpack::ScalarNode::SK_Binary); + else + return "Unsupported messagepack tag"; + + return S.inputYAML(ScalarStr); + } + + static QuotingType mustQuote(const msgpack::ScalarNode &S, StringRef Str) { + return S.mustQuoteYAML(Str); + } +}; + +template <> struct CustomMappingTraits { + static void inputOne(IO &IO, StringRef Key, msgpack::MapNode &M) { + IO.mapRequired(Key.str().c_str(), M[Key]); + } + static void output(IO &IO, msgpack::MapNode &M) { + for (auto &N : M) + IO.mapRequired(N.getKey().str().c_str(), N.getValue()); + } +}; + +template <> struct SequenceTraits { + static size_t size(IO &IO, msgpack::ArrayNode &A) { return A.size(); } + static msgpack::NodePtr &element(IO &IO, msgpack::ArrayNode &A, + size_t Index) { + if (Index >= A.size()) + A.resize(Index + 1); + return A[Index]; + } +}; + +} // end namespace yaml +} // end namespace llvm + +#endif // LLVM_BINARYFORMAT_MSGPACKTYPES_H Index: lib/BinaryFormat/CMakeLists.txt =================================================================== --- lib/BinaryFormat/CMakeLists.txt +++ lib/BinaryFormat/CMakeLists.txt @@ -2,6 +2,7 @@ Dwarf.cpp Magic.cpp MsgPackReader.cpp + MsgPackTypes.cpp MsgPackWriter.cpp Wasm.cpp Index: lib/BinaryFormat/MsgPackTypes.cpp =================================================================== --- /dev/null +++ lib/BinaryFormat/MsgPackTypes.cpp @@ -0,0 +1,303 @@ +//===- MsgPackTypes.cpp - MsgPack Types -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// \file +/// Implementation of types representing MessagePack "documents". +// +//===----------------------------------------------------------------------===// + +#include "llvm/BinaryFormat/MsgPackTypes.h" +#include "llvm/Support/Error.h" + +using namespace llvm; +using namespace msgpack; + +namespace llvm { +namespace msgpack { +void ScalarNode::anchor() {} +void ArrayNode::anchor() {} +void MapNode::anchor() {} +} +} + +Expected Node::readArray(Reader &MPReader, size_t Length) { + auto A = std::make_shared(); + for (size_t I = 0; I < Length; ++I) { + auto OptNodeOrErr = Node::read(MPReader); + if (auto Err = OptNodeOrErr.takeError()) + return std::move(Err); + if (!*OptNodeOrErr) + return make_error( + "Insufficient array elements", + std::make_error_code(std::errc::invalid_argument)); + A->push_back(std::move(**OptNodeOrErr)); + } + return OptNodePtr(std::move(A)); +} + +Expected Node::readMap(Reader &MPReader, size_t Length) { + auto M = std::make_shared(); + for (size_t I = 0; I < Length; ++I) { + auto OptKeyOrErr = Node::read(MPReader); + if (auto Err = OptKeyOrErr.takeError()) + return std::move(Err); + if (!*OptKeyOrErr) + return make_error( + "Insufficient map elements", + std::make_error_code(std::errc::invalid_argument)); + auto OptValOrErr = Node::read(MPReader); + if (auto Err = OptValOrErr.takeError()) + return std::move(Err); + if (!*OptValOrErr) + return make_error( + "Insufficient map elements", + std::make_error_code(std::errc::invalid_argument)); + auto *Key = dyn_cast((*OptKeyOrErr)->get()); + if (!Key) + return make_error( + "Only string map keys are supported", + std::make_error_code(std::errc::invalid_argument)); + if (Key->getScalarKind() != ScalarNode::SK_String) + return make_error( + "Only string map keys are supported", + std::make_error_code(std::errc::invalid_argument)); + M->try_emplace(Key->getString(), std::move(**OptValOrErr)); + } + return OptNodePtr(std::move(M)); +} + +Expected Node::read(Reader &MPReader) { + Object Obj; + + auto ContinueOrErr = MPReader.read(Obj); + if (auto Err = ContinueOrErr.takeError()) + return std::move(Err); + if (!*ContinueOrErr) + return None; + + switch (Obj.Kind) { + case Type::Int: + return OptNodePtr(std::make_shared(Obj.Int)); + case Type::UInt: + return OptNodePtr(std::make_shared(Obj.UInt)); + case Type::Nil: + return OptNodePtr(std::make_shared()); + case Type::Boolean: + return OptNodePtr(std::make_shared(Obj.Bool)); + case Type::Float: + return OptNodePtr(std::make_shared(Obj.Float)); + case Type::String: + return OptNodePtr(std::make_shared(Obj.Raw)); + case Type::Binary: + return OptNodePtr(std::make_shared(Obj.Raw)); + case Type::Array: + return Node::readArray(MPReader, Obj.Length); + case Type::Map: + return Node::readMap(MPReader, Obj.Length); + case Type::Extension: + return make_error( + "Extension types are not supported", + std::make_error_code(std::errc::invalid_argument)); + } + llvm_unreachable("msgpack::Type not handled"); +} + +void ScalarNode::destroy() { + switch (SKind) { + case SK_String: + case SK_Binary: + StringValue.~basic_string(); + break; + default: + // POD types do not require destruction + break; + } +} + +ScalarNode::ScalarNode(int64_t IntValue) + : Node(NK_Scalar), SKind(SK_Int), IntValue(IntValue) {} + +ScalarNode::ScalarNode(int32_t IntValue) + : ScalarNode(static_cast(IntValue)) {} + +ScalarNode::ScalarNode(uint64_t UIntValue) + : Node(NK_Scalar), SKind(SK_UInt), UIntValue(UIntValue) {} + +ScalarNode::ScalarNode(uint32_t IntValue) + : ScalarNode(static_cast(IntValue)) {} + +ScalarNode::ScalarNode() : Node(NK_Scalar), SKind(SK_Nil) {} + +ScalarNode::ScalarNode(bool BoolValue) + : Node(NK_Scalar), SKind(SK_Boolean), BoolValue(BoolValue) {} + +ScalarNode::ScalarNode(double FloatValue) + : Node(NK_Scalar), SKind(SK_Float), BoolValue(FloatValue) {} + +ScalarNode::ScalarNode(StringRef StringValue) + : Node(NK_Scalar), SKind(SK_String) { + new (&this->StringValue) std::string(StringValue); +} + +ScalarNode::ScalarNode(const char *StringValue) + : ScalarNode(StringRef(StringValue)) {} + +ScalarNode::ScalarNode(std::string &&StringValue) + : Node(NK_Scalar), SKind(SK_String) { + new (&this->StringValue) std::string(StringValue); +} + +ScalarNode::ScalarNode(MemoryBufferRef BinaryValue) + : Node(NK_Scalar), SKind(SK_Binary) { + new (&StringValue) std::string(BinaryValue.getBuffer()); +} + +ScalarNode::~ScalarNode() { destroy(); } + +ScalarNode &ScalarNode::operator=(ScalarNode &&RHS) { + destroy(); + switch (SKind = RHS.SKind) { + case SK_Int: + IntValue = RHS.IntValue; + break; + case SK_UInt: + UIntValue = RHS.UIntValue; + break; + case SK_Boolean: + BoolValue = RHS.BoolValue; + break; + case SK_Float: + FloatValue = RHS.FloatValue; + break; + case SK_String: + case SK_Binary: + new (&StringValue) std::string(std::move(RHS.StringValue)); + break; + case SK_Nil: + // pass + break; + } + return *this; +} + +StringRef ScalarNode::inputYAML(StringRef ScalarStr) { + switch (SKind) { + case SK_Int: + return yaml::ScalarTraits::input(ScalarStr, nullptr, IntValue); + case SK_UInt: + return yaml::ScalarTraits::input(ScalarStr, nullptr, UIntValue); + case SK_Nil: + return StringRef(); + case SK_Boolean: + return yaml::ScalarTraits::input(ScalarStr, nullptr, BoolValue); + case SK_Float: + return yaml::ScalarTraits::input(ScalarStr, nullptr, FloatValue); + case SK_Binary: + case SK_String: + return yaml::ScalarTraits::input(ScalarStr, nullptr, + StringValue); + } + llvm_unreachable("unrecognized ScalarKind"); +} + +void ScalarNode::outputYAML(raw_ostream &OS) const { + switch (SKind) { + case SK_Int: + yaml::ScalarTraits::output(IntValue, nullptr, OS); + break; + case SK_UInt: + yaml::ScalarTraits::output(UIntValue, nullptr, OS); + break; + case SK_Nil: + yaml::ScalarTraits::output("", nullptr, OS); + break; + case SK_Boolean: + yaml::ScalarTraits::output(BoolValue, nullptr, OS); + break; + case SK_Float: + yaml::ScalarTraits::output(FloatValue, nullptr, OS); + break; + case SK_Binary: + case SK_String: + yaml::ScalarTraits::output(StringValue, nullptr, OS); + break; + } +} + +yaml::QuotingType ScalarNode::mustQuoteYAML(StringRef ScalarStr) const { + switch (SKind) { + case SK_Int: + return yaml::ScalarTraits::mustQuote(ScalarStr); + case SK_UInt: + return yaml::ScalarTraits::mustQuote(ScalarStr); + case SK_Nil: + return yaml::ScalarTraits::mustQuote(ScalarStr); + case SK_Boolean: + return yaml::ScalarTraits::mustQuote(ScalarStr); + case SK_Float: + return yaml::ScalarTraits::mustQuote(ScalarStr); + case SK_Binary: + case SK_String: + return yaml::ScalarTraits::mustQuote(ScalarStr); + } + llvm_unreachable("unrecognized ScalarKind"); +} + +const char *ScalarNode::IntTag = "!int"; +const char *ScalarNode::NilTag = "!nil"; +const char *ScalarNode::BooleanTag = "!bool"; +const char *ScalarNode::FloatTag = "!float"; +const char *ScalarNode::StringTag = "!str"; +const char *ScalarNode::BinaryTag = "!bin"; + +StringRef ScalarNode::getYAMLTag() const { + switch (SKind) { + case SK_Int: + return IntTag; + case SK_UInt: + return IntTag; + case SK_Nil: + return NilTag; + case SK_Boolean: + return BooleanTag; + case SK_Float: + return FloatTag; + case SK_String: + return StringTag; + case SK_Binary: + return BinaryTag; + } + llvm_unreachable("unrecognized ScalarKind"); +} + +void ScalarNode::write(Writer &MPWriter) { + switch (SKind) { + case SK_Int: + MPWriter.write(IntValue); + break; + case SK_UInt: + MPWriter.write(UIntValue); + break; + case SK_Nil: + MPWriter.writeNil(); + break; + case SK_Boolean: + MPWriter.write(BoolValue); + break; + case SK_Float: + MPWriter.write(FloatValue); + break; + case SK_String: + MPWriter.write(StringValue); + break; + case SK_Binary: + MPWriter.write(MemoryBufferRef(StringValue, "")); + break; + } +} Index: unittests/BinaryFormat/CMakeLists.txt =================================================================== --- unittests/BinaryFormat/CMakeLists.txt +++ unittests/BinaryFormat/CMakeLists.txt @@ -6,6 +6,7 @@ DwarfTest.cpp MachOTest.cpp MsgPackReaderTest.cpp + MsgPackTypesTest.cpp MsgPackWriterTest.cpp TestFileMagic.cpp ) Index: unittests/BinaryFormat/MsgPackTypesTest.cpp =================================================================== --- /dev/null +++ unittests/BinaryFormat/MsgPackTypesTest.cpp @@ -0,0 +1,188 @@ +//===- MsgPackTypesTest.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/BinaryFormat/MsgPackTypes.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace msgpack; + +TEST(MsgPackTypes, TestReadInt) { + Reader MPReader(StringRef("\xd0\x00", 2)); + auto OptNodeOrErr = Node::read(MPReader); + ASSERT_TRUE(static_cast(OptNodeOrErr)); + ASSERT_TRUE(*OptNodeOrErr); + auto *S = dyn_cast((*OptNodeOrErr)->get()); + ASSERT_TRUE(S); + ASSERT_EQ(S->getScalarKind(), ScalarNode::SK_Int); + ASSERT_EQ(S->getInt(), 0); +} + +TEST(MsgPackTypes, TestReadArray) { + Reader MPReader(StringRef("\x92\xd0\x01\xc0")); + auto OptNodeOrErr = Node::read(MPReader); + ASSERT_TRUE(static_cast(OptNodeOrErr)); + ASSERT_TRUE(*OptNodeOrErr); + auto *A = dyn_cast((*OptNodeOrErr)->get()); + ASSERT_TRUE(A); + ASSERT_EQ(A->size(), 2u); + auto *SI = dyn_cast((*A)[0].get()); + ASSERT_TRUE(SI); + ASSERT_EQ(SI->getScalarKind(), ScalarNode::SK_Int); + ASSERT_EQ(SI->getInt(), 1); + auto *SN = dyn_cast((*A)[1].get()); + ASSERT_TRUE(SN); + ASSERT_EQ(SN->getScalarKind(), ScalarNode::SK_Nil); +} + +TEST(MsgPackTypes, TestReadMap) { + Reader MPReader(StringRef("\x82\xa3" + "foo" + "\xd0\x01\xa3" + "bar" + "\xd0\x02")); + auto OptNodeOrErr = Node::read(MPReader); + ASSERT_TRUE(static_cast(OptNodeOrErr)); + ASSERT_TRUE(*OptNodeOrErr); + auto *A = dyn_cast((*OptNodeOrErr)->get()); + ASSERT_TRUE(A); + ASSERT_EQ(A->size(), 2u); + auto *FooS = dyn_cast((*A)["foo"].get()); + ASSERT_TRUE(FooS); + ASSERT_EQ(FooS->getScalarKind(), ScalarNode::SK_Int); + ASSERT_EQ(FooS->getInt(), 1); + auto *BarS = dyn_cast((*A)["bar"].get()); + ASSERT_TRUE(BarS); + ASSERT_EQ(BarS->getScalarKind(), ScalarNode::SK_Int); + ASSERT_EQ(BarS->getInt(), 2); +} + +TEST(MsgPackTypes, TestWriteInt) { + std::string Buffer; + raw_string_ostream OStream(Buffer); + Writer MPWriter(OStream); + ScalarNode I(int64_t(1)); + I.write(MPWriter); + ASSERT_EQ(OStream.str(), "\x01"); +} + +TEST(MsgPackTypes, TestWriteArray) { + std::string Buffer; + raw_string_ostream OStream(Buffer); + Writer MPWriter(OStream); + ArrayNode A; + A.push_back(std::make_shared(int64_t(1))); + A.push_back(std::make_shared()); + A.write(MPWriter); + ASSERT_EQ(OStream.str(), "\x92\x01\xc0"); +} + +TEST(MsgPackTypes, TestWriteMap) { + std::string Buffer; + raw_string_ostream OStream(Buffer); + Writer MPWriter(OStream); + MapNode M; + M["foo"] = std::make_shared(int64_t(1)); + M["bar"] = std::make_shared(int64_t(2)); + M.write(MPWriter); + ASSERT_EQ(OStream.str(), "\x82\xa3" + "foo" + "\x01\xa3" + "bar" + "\x02"); +} + +TEST(MsgPackTypes, TestOutputYAMLArray) { + std::string Buffer; + raw_string_ostream OStream(Buffer); + yaml::Output yout(OStream); + ArrayNode A; + A.push_back(std::make_shared(int64_t(1))); + A.push_back(std::make_shared(int64_t(2))); + yout << A; + ASSERT_EQ(OStream.str(), "---\n- !int 1\n- !int 2\n...\n"); +} + +TEST(MsgPackTypes, TestInputYAMLArray) { + NodePtr RootNode; + yaml::Input yin("---\n- !int 1\n- !str 2\n...\n"); + yin >> RootNode; + auto *A = dyn_cast(RootNode.get()); + ASSERT_TRUE(A); + ASSERT_EQ(A->size(), 2u); + auto *SI = dyn_cast((*A)[0].get()); + ASSERT_TRUE(SI); + ASSERT_EQ(SI->getScalarKind(), ScalarNode::SK_UInt); + ASSERT_EQ(SI->getUInt(), 1u); + auto *SS = dyn_cast((*A)[1].get()); + ASSERT_TRUE(SS); + ASSERT_EQ(SS->getScalarKind(), ScalarNode::SK_String); + ASSERT_EQ(SS->getString(), "2"); +} + +TEST(MsgPackTypes, TestOutputYAMLMap) { + std::string Buffer; + raw_string_ostream OStream(Buffer); + yaml::Output yout(OStream); + MapNode M; + M["foo"] = std::make_shared(int64_t(1)); + M["bar"] = std::make_shared(uint64_t(2)); + auto N = std::make_shared(); + (*N)["baz"] = std::make_shared(true); + M["qux"] = std::move(N); + yout << M; + ASSERT_EQ(OStream.str(), "---\nfoo: !int 1\nbar: " + "!int 2\nqux: \n baz: " + "!bool true\n...\n"); +} + +TEST(MsgPackTypes, TestInputYAMLMap) { + NodePtr RootNode; + yaml::Input yin("---\nfoo: !int 1\nbaz: !str 2\n...\n"); + yin >> RootNode; + auto *M = dyn_cast(RootNode.get()); + ASSERT_TRUE(M); + ASSERT_EQ(M->size(), 2u); + auto *SI = dyn_cast((*M)["foo"].get()); + ASSERT_TRUE(SI); + ASSERT_EQ(SI->getScalarKind(), ScalarNode::SK_UInt); + ASSERT_EQ(SI->getUInt(), 1u); + auto *SS = dyn_cast((*M)["baz"].get()); + ASSERT_TRUE(SS); + ASSERT_EQ(SS->getScalarKind(), ScalarNode::SK_String); + ASSERT_EQ(SS->getString(), "2"); +} + +// Test that the document is parsed into a tree of shared_ptr where each node +// can have multiple owners. +TEST(MsgPackTypes, TestInputShared) { + yaml::Input yin("---\nfoo:\n bar: !int 1\n...\n"); + NodePtr InnerMap; + NodePtr IntNode; + { + { + { + NodePtr RootNode; + yin >> RootNode; + auto *M = dyn_cast(RootNode.get()); + ASSERT_TRUE(M); + ASSERT_EQ(M->size(), 1u); + InnerMap = (*M)["foo"]; + } + auto *N = dyn_cast(InnerMap.get()); + ASSERT_TRUE(N); + ASSERT_EQ(N->size(), 1u); + IntNode = (*N)["bar"]; + } + auto *S = dyn_cast(IntNode.get()); + ASSERT_TRUE(S); + ASSERT_EQ(S->getScalarKind(), ScalarNode::SK_UInt); + ASSERT_EQ(S->getUInt(), 1u); + } +}