Index: include/llvm/BinaryFormat/MsgPack.h =================================================================== --- /dev/null +++ include/llvm/BinaryFormat/MsgPack.h @@ -0,0 +1,93 @@ +//===-- MsgPack.h - MessagePack Constants -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains constants used for implementing MessagePack support. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_BINARYFORMAT_MSGPACK_H +#define LLVM_BINARYFORMAT_MSGPACK_H + +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/Endian.h" + +namespace llvm { +namespace msgpack { + +/// The endianness of all multi-byte encoded values in MessagePack. +constexpr support::endianness Endianness = support::big; + +/// The first byte identifiers of MessagePack object formats. +namespace FirstByte { +#define HANDLE_MP_FIRST_BYTE(ID, NAME) constexpr uint8_t NAME = ID; +#include "llvm/BinaryFormat/MsgPack.def" +} + +/// Most significant bits used to identify "Fix" variants in MessagePack. +/// +/// For example, FixStr objects encode their size in the five least significant +/// bits of their first byte, which is identified by the bit pattern "101" in +/// the three most significant bits. So FixBits::String contains 0b10100000. +/// +/// A corresponding mask of the bit pattern is found in \c FixBitsMask. +namespace FixBits { +#define HANDLE_MP_FIX_BITS(ID, NAME) constexpr uint8_t NAME = ID; +#include "llvm/BinaryFormat/MsgPack.def" +} + +/// Mask of bits used to identify "Fix" variants in MessagePack. +/// +/// For example, FixStr objects encode their size in the five least significant +/// bits of their first byte, which is identified by the bit pattern "101" in +/// the three most significant bytes. So FixBitsMask::String contains +/// 0b11100000. +/// +/// The corresponding bit pattern to mask for is found in FixBits. +namespace FixBitsMask { +#define HANDLE_MP_FIX_BITS_MASK(ID, NAME) constexpr uint8_t NAME = ID; +#include "llvm/BinaryFormat/MsgPack.def" +} + +/// The maximum value or size encodable in "Fix" variants of formats. +/// +/// For example, FixStr objects encode their size in the five least significant +/// bits of their first byte, so the largest encodable size is 0b00011111. +namespace FixMax { +#define HANDLE_MP_FIX_MAX(ID, NAME) constexpr uint8_t NAME = ID; +#include "llvm/BinaryFormat/MsgPack.def" +} + +/// The exact size encodable in "Fix" variants of formats. +/// +/// The only objects for which an exact size makes sense are of Extension type. +/// +/// For example, FixExt4 stores an extension type containing exactly four bytes. +namespace FixLen { +#define HANDLE_MP_FIX_LEN(ID, NAME) constexpr uint8_t NAME = ID; +#include "llvm/BinaryFormat/MsgPack.def" +} + +/// The minimum value or size encodable in "Fix" variants of formats. +/// +/// The only object for which a minimum makes sense is a negative FixNum. +/// +/// Negative FixNum objects encode their signed integer value in one byte, but +/// they must have the pattern "111" as their three most significant bits. This +/// means all values are negative, and the smallest representable value is +/// 0b11100000. +namespace FixMin { +#define HANDLE_MP_FIX_MIN(ID, NAME) constexpr int8_t NAME = ID; +#include "llvm/BinaryFormat/MsgPack.def" +} + +} // end namespace msgpack +} // end namespace llvm + +#endif // LLVM_BINARYFORMAT_MSGPACK_H Index: include/llvm/BinaryFormat/MsgPack.def =================================================================== --- /dev/null +++ include/llvm/BinaryFormat/MsgPack.def @@ -0,0 +1,108 @@ +//===- MsgPack.def - MessagePack definitions --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Macros for running through MessagePack enumerators. +/// +//===----------------------------------------------------------------------===// + +#if !( \ + defined HANDLE_MP_FIRST_BYTE || defined HANDLE_MP_FIX_BITS || \ + defined HANDLE_MP_FIX_BITS_MASK || defined HANDLE_MP_FIX_MAX || \ + defined HANDLE_MP_FIX_LEN || defined HANDLE_MP_FIX_MIN) +#error "Missing macro definition of HANDLE_MP*" +#endif + +#ifndef HANDLE_MP_FIRST_BYTE +#define HANDLE_MP_FIRST_BYTE(ID, NAME) +#endif + +#ifndef HANDLE_MP_FIX_BITS +#define HANDLE_MP_FIX_BITS(ID, NAME) +#endif + +#ifndef HANDLE_MP_FIX_BITS_MASK +#define HANDLE_MP_FIX_BITS_MASK(ID, NAME) +#endif + +#ifndef HANDLE_MP_FIX_MAX +#define HANDLE_MP_FIX_MAX(ID, NAME) +#endif + +#ifndef HANDLE_MP_FIX_LEN +#define HANDLE_MP_FIX_LEN(ID, NAME) +#endif + +#ifndef HANDLE_MP_FIX_MIN +#define HANDLE_MP_FIX_MIN(ID, NAME) +#endif + +HANDLE_MP_FIRST_BYTE(0xc0, Nil) +HANDLE_MP_FIRST_BYTE(0xc2, False) +HANDLE_MP_FIRST_BYTE(0xc3, True) +HANDLE_MP_FIRST_BYTE(0xc4, Bin8) +HANDLE_MP_FIRST_BYTE(0xc5, Bin16) +HANDLE_MP_FIRST_BYTE(0xc6, Bin32) +HANDLE_MP_FIRST_BYTE(0xc7, Ext8) +HANDLE_MP_FIRST_BYTE(0xc8, Ext16) +HANDLE_MP_FIRST_BYTE(0xc9, Ext32) +HANDLE_MP_FIRST_BYTE(0xca, Float32) +HANDLE_MP_FIRST_BYTE(0xcb, Float64) +HANDLE_MP_FIRST_BYTE(0xcc, UInt8) +HANDLE_MP_FIRST_BYTE(0xcd, UInt16) +HANDLE_MP_FIRST_BYTE(0xce, UInt32) +HANDLE_MP_FIRST_BYTE(0xcf, UInt64) +HANDLE_MP_FIRST_BYTE(0xd0, Int8) +HANDLE_MP_FIRST_BYTE(0xd1, Int16) +HANDLE_MP_FIRST_BYTE(0xd2, Int32) +HANDLE_MP_FIRST_BYTE(0xd3, Int64) +HANDLE_MP_FIRST_BYTE(0xd4, FixExt1) +HANDLE_MP_FIRST_BYTE(0xd5, FixExt2) +HANDLE_MP_FIRST_BYTE(0xd6, FixExt4) +HANDLE_MP_FIRST_BYTE(0xd7, FixExt8) +HANDLE_MP_FIRST_BYTE(0xd8, FixExt16) +HANDLE_MP_FIRST_BYTE(0xd9, Str8) +HANDLE_MP_FIRST_BYTE(0xda, Str16) +HANDLE_MP_FIRST_BYTE(0xdb, Str32) +HANDLE_MP_FIRST_BYTE(0xdc, Array16) +HANDLE_MP_FIRST_BYTE(0xdd, Array32) +HANDLE_MP_FIRST_BYTE(0xde, Map16) +HANDLE_MP_FIRST_BYTE(0xdf, Map32) + +HANDLE_MP_FIX_BITS(0x00, PositiveInt) +HANDLE_MP_FIX_BITS(0x80, Map) +HANDLE_MP_FIX_BITS(0x90, Array) +HANDLE_MP_FIX_BITS(0xa0, String) +HANDLE_MP_FIX_BITS(0xe0, NegativeInt) + +HANDLE_MP_FIX_BITS_MASK(0x80, PositiveInt) +HANDLE_MP_FIX_BITS_MASK(0xf0, Map) +HANDLE_MP_FIX_BITS_MASK(0xf0, Array) +HANDLE_MP_FIX_BITS_MASK(0xe0, String) +HANDLE_MP_FIX_BITS_MASK(0xe0, NegativeInt) + +HANDLE_MP_FIX_MAX(0x7f, PositiveInt) +HANDLE_MP_FIX_MAX(0x0f, Map) +HANDLE_MP_FIX_MAX(0x0f, Array) +HANDLE_MP_FIX_MAX(0x1f, String) + +HANDLE_MP_FIX_LEN(0x01, Ext1) +HANDLE_MP_FIX_LEN(0x02, Ext2) +HANDLE_MP_FIX_LEN(0x04, Ext4) +HANDLE_MP_FIX_LEN(0x08, Ext8) +HANDLE_MP_FIX_LEN(0x10, Ext16) + +HANDLE_MP_FIX_MIN(-0x20, NegativeInt) + +#undef HANDLE_MP_FIRST_BYTE +#undef HANDLE_MP_FIX_BITS +#undef HANDLE_MP_FIX_BITS_MASK +#undef HANDLE_MP_FIX_MAX +#undef HANDLE_MP_FIX_LEN +#undef HANDLE_MP_FIX_MIN Index: include/llvm/BinaryFormat/MsgPackReader.h =================================================================== --- /dev/null +++ include/llvm/BinaryFormat/MsgPackReader.h @@ -0,0 +1,146 @@ +//===- MsgPackReader.h - Simple MsgPack reader ------------------*- 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 MessagePack reader. +/// +/// See https://github.com/msgpack/msgpack/blob/master/spec.md for the full +/// standard. +/// +/// Typical usage: +/// \code +/// StringRef input = GetInput(); +/// msgpack::Reader MPReader(input); +/// msgpack::Object Obj; +/// +/// while (MPReader.Read(Obj)) { +/// switch (Obj.Kind) { +/// case msgpack::Type::Int: +// // Use Obj.Int +/// break; +/// // ... +/// } +/// } +/// \endcode +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_MSGPACKREADER_H +#define LLVM_SUPPORT_MSGPACKREADER_H + +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include + +namespace llvm { +namespace msgpack { + +/// MessagePack types as defined in the standard, with the exception of Int +/// being divided into a signed and unsigned variant in order to map directly +/// to C++ types. +/// +/// The types map onto corresponding union members of the \c Object struct. +enum class Type : uint8_t { + Int, + UInt, + Nil, + Boolean, + Float, + String, + Binary, + Array, + Map, + Extension, +}; + +/// MessagePack object, represented as a tagged union of C++ types. +/// +/// All types except \c Type::Nil (which has only one value, and so is +/// completely represented by the \c Kind itself) map to a exactly one union +/// member. +struct Object { + Type Kind; + union { + /// Value for \c Type::Int. + int64_t Int; + /// Value for \c Type::Uint. + uint64_t UInt; + /// Value for \c Type::Boolean. + bool Bool; + /// Value for \c Type::Float. + double Float; + /// Value for \c Type::String and \c Type::Binary. + StringRef Raw; + /// Value for \c Type::Array and \c Type::Map. + size_t Length; + /// Value for \c Type::Extension. + struct { + /// User-defined extension type. + int8_t Type; + /// Raw bytes of the extension object. + StringRef Bytes; + } Extension; + }; + + Object() : Kind(Type::Int), Int(0) {} +}; + +/// Reads MessagePack objects from memory, one at a time. +class Reader { +public: + /// Construct a reader, keeping a reference to the \p InputBuffer. + Reader(MemoryBufferRef InputBuffer); + /// Construct a reader, keeping a reference to the \p Input. + Reader(StringRef Input); + + Reader(const Reader &) = delete; + Reader &operator=(const Reader &) = delete; + + /// Read one object from the input buffer, advancing past it. + /// + /// The \p Obj is updated with the kind of the object read, and the + /// corresponding union member is updated. + /// + /// For the collection objects (Array and Map), only the length is read, and + /// the caller must make and additional \c N calls (in the case of Array) or + /// \c N*2 calls (in the case of Map) to \c Read to retrieve the collection + /// elements. + /// + /// \param [out] Obj filled with next object on success. + /// + /// \returns true when object successfully read, otherwise false. + bool Read(Object &Obj); + +private: + MemoryBufferRef InputBuffer; + StringRef::iterator Current; + StringRef::iterator End; + bool Failed = false; + + void SetError(const Twine &Message) { + if (Current >= End) + Current = End - 1; + if (!Failed) + errs() << Message << '\n'; + Failed = true; + } + + template bool ReadRaw(Object &Obj); + template bool ReadInt(Object &Obj); + template bool ReadUInt(Object &Obj); + template bool ReadLength(Object &Obj); + template bool ReadExt(Object &Obj); + bool CreateRaw(Object &Obj, uint32_t Size); + bool CreateExt(Object &Obj, uint32_t Size); +}; + +} // end namespace msgpack +} // end namespace llvm + +#endif // LLVM_SUPPORT_MSGPACKREADER_H Index: include/llvm/BinaryFormat/MsgPackWriter.h =================================================================== --- /dev/null +++ include/llvm/BinaryFormat/MsgPackWriter.h @@ -0,0 +1,131 @@ +//===- MsgPackWriter.h - Simple MsgPack writer ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains a MessagePack writer. +/// +/// See https://github.com/msgpack/msgpack/blob/master/spec.md for the full +/// specification. +/// +/// Typical usage: +/// \code +/// raw_ostream output = GetOutputStream(); +/// msgpack::Writer MPWriter(output); +/// MPWriter.WriteNil(); +/// MPWriter.Write(false); +/// MPWriter.Write("string"); +/// // ... +/// \endcode +/// +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_MSGPACKPARSER_H +#define LLVM_SUPPORT_MSGPACKPARSER_H + +#include "llvm/BinaryFormat/MsgPack.h" +#include "llvm/Support/EndianStream.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace msgpack { + +/// Writes MessagePack objects to an output stream, one at a time. +class Writer { +public: + /// Construct a writer, optionally enabling "Compatibility Mode" as defined + /// in the MessagePack specification. + /// + /// When in \p Compatible mode, the writer will write \c Str16 formats + /// instead of \c Str8 formats, and will refuse to write any \c Bin formats. + /// + /// \param OS stream to output MessagePack objects to. + /// \param Compatible when set, write in "Compatibility Mode". + Writer(raw_ostream &OS, bool Compatible = false); + + Writer(const Writer &) = delete; + Writer &operator=(const Writer &) = delete; + + /// Write a \em Nil to the output stream. + /// + /// The output will be the \em nil format. + void WriteNil(); + + /// Write a \em Boolean to the output stream. + /// + /// The output will be a \em bool format. + void Write(bool b); + + /// Write a signed integer to the output stream. + /// + /// The output will be in the smallest possible \em int format. + /// + /// The format chosen may be for an unsigned integer. + void Write(int64_t i); + + /// Write an unsigned integer to the output stream. + /// + /// The output will be in the smallest possible \em int format. + void Write(uint64_t u); + + /// Write a floating point number to the output stream. + /// + /// The output will be in the smallest possible \em float format. + void Write(double d); + + /// Write a string to the output stream. + /// + /// The output will be in the smallest possible \em str format. + void Write(StringRef s); + + /// Write a memory buffer to the output stream. + /// + /// The output will be in the smallest possible \em bin format. + /// + /// \warning Do not use this overload if in \c Compatible mode. + void Write(MemoryBufferRef Buffer); + + /// Write the header for an \em Array of the given size. + /// + /// The output will be in the smallest possible \em array format. + // + /// The header contains an identifier for the \em array format used, as well + /// as an encoding of the size of the array. + /// + /// N.B. The caller must subsequently call \c Write an additional \p Size + /// times to complete the array. + void WriteArraySize(uint32_t Size); + + /// Write the header for a \em Map of the given size. + /// + /// The output will be in the smallest possible \em map format. + // + /// The header contains an identifier for the \em map format used, as well + /// as an encoding of the size of the map. + /// + /// N.B. The caller must subsequently call \c Write and additional \c Size*2 + /// times to complete the map. Each even numbered call to \c Write defines a + /// new key, and each odd numbered call defines the previous key's value. + void WriteMapSize(uint32_t Size); + + /// Write a typed memory buffer (an extension type) to the output stream. + /// + /// The output will be in the smallest possible \em ext format. + void WriteExt(int8_t Type, MemoryBufferRef Buffer); + +private: + support::endian::Writer EW; + bool Compatible; +}; + +} // end namespace msgpack +} // end namespace llvm + +#endif // LLVM_SUPPORT_MSGPACKPARSER_H Index: lib/BinaryFormat/CMakeLists.txt =================================================================== --- lib/BinaryFormat/CMakeLists.txt +++ lib/BinaryFormat/CMakeLists.txt @@ -1,8 +1,9 @@ add_llvm_library(LLVMBinaryFormat Dwarf.cpp Magic.cpp + MsgPackReader.cpp + MsgPackWriter.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/BinaryFormat ) - \ No newline at end of file Index: lib/BinaryFormat/MsgPackReader.cpp =================================================================== --- /dev/null +++ lib/BinaryFormat/MsgPackReader.cpp @@ -0,0 +1,255 @@ +//===- MsgPackReader.cpp - Simple MsgPack reader ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements a MessagePack reader. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/BinaryFormat/MsgPackReader.h" +#include "llvm/BinaryFormat/MsgPack.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::support; +using namespace msgpack; + +Reader::Reader(MemoryBufferRef InputBuffer) + : InputBuffer(InputBuffer), Current(InputBuffer.getBufferStart()), + End(InputBuffer.getBufferEnd()) {} + +Reader::Reader(StringRef Input) : Reader({Input, "MsgPack"}) {} + +bool Reader::Read(Object &Obj) { + if (Current == End) { + SetError("Attempted to read at end of stream"); + return false; + } + + uint8_t FB = static_cast(*Current++); + + switch (FB) { + case FirstByte::Nil: + Obj.Kind = Type::Nil; + return true; + case FirstByte::True: + Obj.Kind = Type::Boolean; + Obj.Bool = true; + return true; + case FirstByte::False: + Obj.Kind = Type::Boolean; + Obj.Bool = false; + return true; + case FirstByte::Int8: + Obj.Kind = Type::Int; + return ReadInt(Obj); + case FirstByte::Int16: + Obj.Kind = Type::Int; + return ReadInt(Obj); + case FirstByte::Int32: + Obj.Kind = Type::Int; + return ReadInt(Obj); + case FirstByte::Int64: + Obj.Kind = Type::Int; + return ReadInt(Obj); + case FirstByte::UInt8: + Obj.Kind = Type::UInt; + return ReadUInt(Obj); + case FirstByte::UInt16: + Obj.Kind = Type::UInt; + return ReadUInt(Obj); + case FirstByte::UInt32: + Obj.Kind = Type::UInt; + return ReadUInt(Obj); + case FirstByte::UInt64: + Obj.Kind = Type::UInt; + return ReadUInt(Obj); + case FirstByte::Float32: + Obj.Kind = Type::Float; + if (Current + sizeof(float) > End) { + SetError("Invalid Float32 with insufficient payload"); + return false; + } + Obj.Float = BitsToFloat(endian::read(Current)); + Current += sizeof(float); + return true; + case FirstByte::Float64: + Obj.Kind = Type::Float; + if (Current + sizeof(double) > End) { + SetError("Invalid Float64 with insufficient payload"); + return false; + } + Obj.Float = BitsToDouble(endian::read(Current)); + Current += sizeof(double); + return true; + case FirstByte::Str8: + Obj.Kind = Type::String; + return ReadRaw(Obj); + case FirstByte::Str16: + Obj.Kind = Type::String; + return ReadRaw(Obj); + case FirstByte::Str32: + Obj.Kind = Type::String; + return ReadRaw(Obj); + case FirstByte::Bin8: + Obj.Kind = Type::Binary; + return ReadRaw(Obj); + case FirstByte::Bin16: + Obj.Kind = Type::Binary; + return ReadRaw(Obj); + case FirstByte::Bin32: + Obj.Kind = Type::Binary; + return ReadRaw(Obj); + case FirstByte::Array16: + Obj.Kind = Type::Array; + return ReadLength(Obj); + case FirstByte::Array32: + Obj.Kind = Type::Array; + return ReadLength(Obj); + case FirstByte::Map16: + Obj.Kind = Type::Map; + return ReadLength(Obj); + case FirstByte::Map32: + Obj.Kind = Type::Map; + return ReadLength(Obj); + case FirstByte::FixExt1: + Obj.Kind = Type::Extension; + return CreateExt(Obj, FixLen::Ext1); + case FirstByte::FixExt2: + Obj.Kind = Type::Extension; + return CreateExt(Obj, FixLen::Ext2); + case FirstByte::FixExt4: + Obj.Kind = Type::Extension; + return CreateExt(Obj, FixLen::Ext4); + case FirstByte::FixExt8: + Obj.Kind = Type::Extension; + return CreateExt(Obj, FixLen::Ext8); + case FirstByte::FixExt16: + Obj.Kind = Type::Extension; + return CreateExt(Obj, FixLen::Ext16); + case FirstByte::Ext8: + Obj.Kind = Type::Extension; + return ReadExt(Obj); + case FirstByte::Ext16: + Obj.Kind = Type::Extension; + return ReadExt(Obj); + case FirstByte::Ext32: + Obj.Kind = Type::Extension; + return ReadExt(Obj); + } + + if ((FB & FixBitsMask::NegativeInt) == FixBits::NegativeInt) { + Obj.Kind = Type::Int; + // TODO: Better way to reinterpret bits in FB? + Obj.Int = *reinterpret_cast(&FB); + return true; + } + + if ((FB & FixBitsMask::PositiveInt) == FixBits::PositiveInt) { + Obj.Kind = Type::UInt; + Obj.UInt = static_cast(FB); + return true; + } + + if ((FB & FixBitsMask::String) == FixBits::String) { + Obj.Kind = Type::String; + uint8_t Size = FB & ~FixBitsMask::String; + return CreateRaw(Obj, Size); + } + + if ((FB & FixBitsMask::Array) == FixBits::Array) { + Obj.Kind = Type::Array; + Obj.Length = FB & ~FixBitsMask::Array; + return true; + } + + if ((FB & FixBitsMask::Map) == FixBits::Map) { + Obj.Kind = Type::Map; + Obj.Length = FB & ~FixBitsMask::Map; + return true; + } + + SetError("Invalid first byte"); + return false; +} + +template bool Reader::ReadRaw(Object &Obj) { + if (Current + sizeof(T) > End) { + SetError("Invalid Raw with invalid size"); + return false; + } + T Size = endian::read(Current); + Current += sizeof(T); + return CreateRaw(Obj, Size); +} + +template bool Reader::ReadInt(Object &Obj) { + if (Current + sizeof(T) > End) { + SetError("Invalid Int with insufficient payload"); + return false; + } + Obj.Int = static_cast(endian::read(Current)); + Current += sizeof(T); + return true; +} + +template bool Reader::ReadUInt(Object &Obj) { + if (Current + sizeof(T) > End) { + SetError("Invalid Int with insufficient payload"); + return false; + } + Obj.UInt = static_cast(endian::read(Current)); + Current += sizeof(T); + return true; +} + +template bool Reader::ReadLength(Object &Obj) { + if (Current + sizeof(T) > End) { + SetError("Invalid Map/Array with invalid length"); + return false; + } + Obj.Length = static_cast(endian::read(Current)); + Current += sizeof(T); + return true; +} + +template bool Reader::ReadExt(Object &Obj) { + if (Current + sizeof(T) > End) { + SetError("Invalid Ext with invalid length"); + return false; + } + T Size = endian::read(Current); + Current += sizeof(T); + return CreateExt(Obj, Size); +} + +bool Reader::CreateRaw(Object &Obj, uint32_t Size) { + Obj.Raw = StringRef(Current, Size); + Current += Size; + if (Current > End) { + SetError("Invalid Raw with insufficient payload"); + return false; + } + return true; +} + +bool Reader::CreateExt(Object &Obj, uint32_t Size) { + if (Current == End) { + SetError("Invalid Ext with no type"); + return false; + } + Obj.Extension.Type = *Current++; + Obj.Extension.Bytes = StringRef(Current, Size); + Current += Size; + if (Current > End) { + SetError("Invalid Ext with insufficient payload"); + return false; + } + return true; +} Index: lib/BinaryFormat/MsgPackWriter.cpp =================================================================== --- /dev/null +++ lib/BinaryFormat/MsgPackWriter.cpp @@ -0,0 +1,208 @@ +//===- MsgPackWriter.cpp - Simple MsgPack writer ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements a MessagePack writer. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/BinaryFormat/MsgPackWriter.h" +#include "llvm/BinaryFormat/MsgPack.h" + +using namespace llvm; +using namespace msgpack; + +Writer::Writer(raw_ostream &OS, bool Compatible) + : EW(OS), Compatible(Compatible) {} + +void Writer::WriteNil() { EW.write(FirstByte::Nil); } + +void Writer::Write(bool b) { EW.write(b ? FirstByte::True : FirstByte::False); } + +void Writer::Write(int64_t i) { + if (i >= 0) { + Write(static_cast(i)); + return; + } + + if (i >= FixMin::NegativeInt) { + EW.write(static_cast(i)); + return; + } + + if (i >= INT8_MIN) { + EW.write(FirstByte::Int8); + EW.write(static_cast(i)); + return; + } + + if (i >= INT16_MIN) { + EW.write(FirstByte::Int16); + EW.write(static_cast(i)); + return; + } + + if (i >= INT32_MIN) { + EW.write(FirstByte::Int32); + EW.write(static_cast(i)); + return; + } + + EW.write(FirstByte::Int64); + EW.write(i); +} + +void Writer::Write(uint64_t u) { + if (u <= FixMax::PositiveInt) { + EW.write(static_cast(u)); + return; + } + + if (u <= UINT8_MAX) { + EW.write(FirstByte::UInt8); + EW.write(static_cast(u)); + return; + } + + if (u <= UINT16_MAX) { + EW.write(FirstByte::UInt16); + EW.write(static_cast(u)); + return; + } + + if (u <= UINT32_MAX) { + EW.write(FirstByte::UInt32); + EW.write(static_cast(u)); + return; + } + + EW.write(FirstByte::UInt64); + EW.write(u); +} + +void Writer::Write(double d) { + // If no loss of precision, encode as a Float32. + float f = static_cast(d); + if (static_cast(f) == d) { + EW.write(FirstByte::Float32); + EW.write(f); + } else { + EW.write(FirstByte::Float64); + EW.write(d); + } +} + +void Writer::Write(StringRef s) { + size_t Size = s.size(); + + if (Size <= FixMax::String) + EW.write(static_cast(FixBits::String | Size)); + else if (!Compatible && Size <= UINT8_MAX) { + EW.write(FirstByte::Str8); + EW.write(static_cast(Size)); + } else if (Size <= UINT16_MAX) { + EW.write(FirstByte::Str16); + EW.write(static_cast(Size)); + } else { + assert(Size <= UINT32_MAX && "String object too long to be encoded"); + EW.write(FirstByte::Str32); + EW.write(static_cast(Size)); + } + + EW.OS << s; +} + +void Writer::Write(MemoryBufferRef Buffer) { + assert(!Compatible && "Attempt to write Bin format in compatible mode"); + + size_t Size = Buffer.getBufferSize(); + + if (Size <= UINT8_MAX) { + EW.write(FirstByte::Bin8); + EW.write(static_cast(Size)); + } else if (Size <= UINT16_MAX) { + EW.write(FirstByte::Bin16); + EW.write(static_cast(Size)); + } else { + assert(Size <= UINT32_MAX && "Binary object too long to be encoded"); + EW.write(FirstByte::Bin32); + EW.write(static_cast(Size)); + } + + EW.OS.write(Buffer.getBufferStart(), Size); +} + +void Writer::WriteArraySize(uint32_t Size) { + if (Size <= FixMax::Array) { + EW.write(static_cast(FixBits::Array | Size)); + return; + } + + if (Size <= UINT16_MAX) { + EW.write(FirstByte::Array16); + EW.write(static_cast(Size)); + return; + } + + EW.write(FirstByte::Array32); + EW.write(Size); +} + +void Writer::WriteMapSize(uint32_t Size) { + if (Size <= FixMax::Map) { + EW.write(static_cast(FixBits::Map | Size)); + return; + } + + if (Size <= UINT16_MAX) { + EW.write(FirstByte::Map16); + EW.write(static_cast(Size)); + return; + } + + EW.write(FirstByte::Map32); + EW.write(Size); +} + +void Writer::WriteExt(int8_t Type, MemoryBufferRef Buffer) { + size_t Size = Buffer.getBufferSize(); + + switch (Size) { + case FixLen::Ext1: + EW.write(FirstByte::FixExt1); + break; + case FixLen::Ext2: + EW.write(FirstByte::FixExt2); + break; + case FixLen::Ext4: + EW.write(FirstByte::FixExt4); + break; + case FixLen::Ext8: + EW.write(FirstByte::FixExt8); + break; + case FixLen::Ext16: + EW.write(FirstByte::FixExt16); + break; + default: + if (Size <= UINT8_MAX) { + EW.write(FirstByte::Ext8); + EW.write(static_cast(Size)); + } else if (Size <= UINT16_MAX) { + EW.write(FirstByte::Ext16); + EW.write(static_cast(Size)); + } else { + assert(Size <= UINT32_MAX && "Ext size too large to be encoded"); + EW.write(FirstByte::Ext32); + EW.write(static_cast(Size)); + } + } + + EW.write(Type); + EW.OS.write(Buffer.getBufferStart(), Size); +} Index: unittests/BinaryFormat/CMakeLists.txt =================================================================== --- unittests/BinaryFormat/CMakeLists.txt +++ unittests/BinaryFormat/CMakeLists.txt @@ -4,6 +4,8 @@ add_llvm_unittest(BinaryFormatTests DwarfTest.cpp + MsgPackReaderTest.cpp + MsgPackWriterTest.cpp TestFileMagic.cpp ) Index: unittests/BinaryFormat/MsgPackReaderTest.cpp =================================================================== --- /dev/null +++ unittests/BinaryFormat/MsgPackReaderTest.cpp @@ -0,0 +1,753 @@ +//===- MsgPackReaderTest.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/MsgPackReader.h" +#include "llvm/BinaryFormat/MsgPack.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::msgpack; + +struct MsgPackReader : public ::testing::Test { + std::string Buffer; + Object Obj; +}; + +TEST_F(MsgPackReader, TestReadNil) { + Buffer = "\xc0"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); +} + +TEST_F(MsgPackReader, TestReadBool) { + Buffer = "\xc2\xc3"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Boolean); + EXPECT_EQ(Obj.Bool, false); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Boolean); + EXPECT_EQ(Obj.Bool, true); +} + +TEST_F(MsgPackReader, TestReadFixNegativeInt) { + // Positive values will be written in a UInt form, so max FixNegativeInt is -1 + // + // FixNegativeInt form bitpattern starts with 111, so min FixNegativeInt + // is 11100000 = -32 + for (int8_t i = -1; i >= -32; --i) { + Buffer.assign(1, static_cast(i)); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Int); + EXPECT_EQ(Obj.Int, i); + } +} + +TEST_F(MsgPackReader, TestReadInt8Max) { + Buffer = "\xd0\x7f"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Int); + EXPECT_EQ(Obj.Int, INT8_MAX); +} + +TEST_F(MsgPackReader, TestReadInt8Zero) { + Buffer.assign("\xd0\x00", 2); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Int); + EXPECT_EQ(Obj.Int, 0); +} + +TEST_F(MsgPackReader, TestReadInt8Min) { + Buffer = "\xd0\x80"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Int); + EXPECT_EQ(Obj.Int, INT8_MIN); +} + +TEST_F(MsgPackReader, TestReadInt16Max) { + Buffer = "\xd1\x7f\xff"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Int); + EXPECT_EQ(Obj.Int, INT16_MAX); +} + +TEST_F(MsgPackReader, TestReadInt16Zero) { + Buffer.assign("\xd1\x00\x00", 3); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Int); + EXPECT_EQ(Obj.Int, 0); +} + +TEST_F(MsgPackReader, TestReadInt16Min) { + Buffer.assign("\xd1\x80\x00", 3); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Int); + EXPECT_EQ(Obj.Int, INT16_MIN); +} + +TEST_F(MsgPackReader, TestReadInt32Max) { + Buffer = "\xd2\x7f\xff\xff\xff"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Int); + EXPECT_EQ(Obj.Int, INT32_MAX); +} + +TEST_F(MsgPackReader, TestReadInt32Zero) { + Buffer.assign("\xd2\x00\x00\x00\x00", 5); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Int); + EXPECT_EQ(Obj.Int, 0); +} + +TEST_F(MsgPackReader, TestReadInt32Min) { + Buffer.assign("\xd2\x80\x00\x00\x00", 5); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Int); + EXPECT_EQ(Obj.Int, INT32_MIN); +} + +TEST_F(MsgPackReader, TestReadInt64Max) { + Buffer = "\xd3\x7f\xff\xff\xff\xff\xff\xff\xff"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Int); + EXPECT_EQ(Obj.Int, INT64_MAX); +} + +TEST_F(MsgPackReader, TestReadInt64Zero) { + Buffer.assign("\xd3\x00\x00\x00\x00\x00\x00\x00\x00", 9); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Int); + EXPECT_EQ(Obj.Int, 0); +} + +TEST_F(MsgPackReader, TestReadInt64Min) { + Buffer.assign("\xd3\x80\x00\x00\x00\x00\x00\x00\x00", 9); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Int); + EXPECT_EQ(Obj.Int, INT64_MIN); +} + +TEST_F(MsgPackReader, TestReadFixPositiveInt) { + // FixPositiveInt form bitpattern starts with 0, so max FixPositiveInt + // is 01111111 = 127 + for (uint64_t u = 0; u <= 127; ++u) { + Buffer.assign(1, static_cast(u)); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::UInt); + EXPECT_EQ(Obj.UInt, u); + } +} + +TEST_F(MsgPackReader, TestReadUInt8Zero) { + Buffer.assign("\xcc\x00", 2); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::UInt); + EXPECT_EQ(Obj.UInt, 0u); +} + +TEST_F(MsgPackReader, TestReadUInt8One) { + Buffer = "\xcc\x01"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::UInt); + EXPECT_EQ(Obj.UInt, 1u); +} + +TEST_F(MsgPackReader, TestReadUInt8Max) { + Buffer = "\xcc\xff"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::UInt); + EXPECT_EQ(Obj.UInt, static_cast(UINT8_MAX)); +} + +TEST_F(MsgPackReader, TestReadUInt16Zero) { + Buffer.assign("\xcd\x00\x00", 3); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::UInt); + EXPECT_EQ(Obj.UInt, 0u); +} + +TEST_F(MsgPackReader, TestReadUInt16One) { + Buffer.assign("\xcd\x00\x01", 3); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::UInt); + EXPECT_EQ(Obj.UInt, 1u); +} + +TEST_F(MsgPackReader, TestReadUInt16Max) { + Buffer = "\xcd\xff\xff"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::UInt); + EXPECT_EQ(Obj.UInt, static_cast(UINT16_MAX)); +} + +TEST_F(MsgPackReader, TestReadUInt32Zero) { + Buffer.assign("\xce\x00\x00\x00\x00", 5); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::UInt); + EXPECT_EQ(Obj.UInt, 0u); +} + +TEST_F(MsgPackReader, TestReadUInt32One) { + Buffer.assign("\xce\x00\x00\x00\x01", 5); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::UInt); + EXPECT_EQ(Obj.UInt, 1u); +} + +TEST_F(MsgPackReader, TestReadUInt32Max) { + Buffer = "\xce\xff\xff\xff\xff"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::UInt); + EXPECT_EQ(Obj.UInt, static_cast(UINT32_MAX)); +} + +TEST_F(MsgPackReader, TestReadUInt64Zero) { + Buffer.assign("\xcf\x00\x00\x00\x00\x00\x00\x00\x00", 9); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::UInt); + EXPECT_EQ(Obj.UInt, 0u); +} + +TEST_F(MsgPackReader, TestReadUInt64One) { + Buffer.assign("\xcf\x00\x00\x00\x00\x00\x00\x00\x01", 9); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::UInt); + EXPECT_EQ(Obj.UInt, 1u); +} + +TEST_F(MsgPackReader, TestReadUInt64Max) { + Buffer = "\xcf\xff\xff\xff\xff\xff\xff\xff\xff"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::UInt); + EXPECT_EQ(Obj.UInt, static_cast(UINT64_MAX)); +} + +TEST_F(MsgPackReader, TestReadFloat32) { + float f = -3.6973142664068907e+28; + Buffer = "\xca\xee\xee\xee\xef"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Float); + EXPECT_EQ(Obj.Float, f); +} + +TEST_F(MsgPackReader, TestReadFloat64) { + double d = -2.2899894549927042e+226; + Buffer = "\xcb\xee\xee\xee\xee\xee\xee\xee\xef"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Float); + EXPECT_EQ(Obj.Float, d); +} + +TEST_F(MsgPackReader, TestReadFixStrZero) { + std::string Result; + Buffer = "\xa0"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::String); + EXPECT_EQ(Obj.Raw, StringRef()); +} + +TEST_F(MsgPackReader, TestReadFixStrOne) { + std::string Result; + Result.assign(1, 'a'); + Buffer = "\xa1"; + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::String); + EXPECT_EQ(Obj.Raw, Result); +} + +TEST_F(MsgPackReader, TestReadFixStrMax) { + std::string Result; + // FixStr format's size is a 5 bit unsigned integer, so max is 11111 = 31 + Result.assign(31, 'a'); + Buffer = "\xbf"; + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::String); + EXPECT_EQ(Obj.Raw, Result); +} + +TEST_F(MsgPackReader, TestReadStr8Zero) { + Buffer.assign("\xd9\x00", 2); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::String); + EXPECT_EQ(Obj.Raw, StringRef()); +} + +TEST_F(MsgPackReader, TestReadStr8One) { + std::string Result; + Result.assign(1, 'a'); + Buffer = "\xd9\x01"; + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::String); + EXPECT_EQ(Obj.Raw, Result); +} + +TEST_F(MsgPackReader, TestReadStr8Max) { + std::string Result; + Result.assign(UINT8_MAX, 'a'); + Buffer = "\xd9\xff"; + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::String); + EXPECT_EQ(Obj.Raw, Result); +} + +TEST_F(MsgPackReader, TestReadStr16Zero) { + Buffer.assign("\xda\x00\x00", 3); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::String); + EXPECT_EQ(Obj.Raw, StringRef()); +} + +TEST_F(MsgPackReader, TestReadStr16One) { + std::string Result; + Result.assign(1, 'a'); + Buffer.assign("\xda\x00\x01", 3); + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::String); + EXPECT_EQ(Obj.Raw, Result); +} + +TEST_F(MsgPackReader, TestReadStr16Max) { + std::string Result; + Result.assign(UINT16_MAX, 'a'); + Buffer = "\xda\xff\xff"; + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::String); + EXPECT_EQ(Obj.Raw, Result); +} + +TEST_F(MsgPackReader, TestReadStr32Zero) { + Buffer.assign("\xdb\x00\x00\x00\x00", 5); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::String); + EXPECT_EQ(Obj.Raw, StringRef()); +} + +TEST_F(MsgPackReader, TestReadStr32One) { + std::string Result; + Result.assign(1, 'a'); + Buffer.assign("\xdb\x00\x00\x00\x01", 5); + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::String); + EXPECT_EQ(Obj.Raw, Result); +} + +TEST_F(MsgPackReader, TestReadStr32Max) { + std::string Result; + Result.assign(static_cast(UINT16_MAX) + 1, 'a'); + Buffer.assign("\xdb\x00\x01\x00\x00", 5); + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::String); + EXPECT_EQ(Obj.Raw, Result); +} + +TEST_F(MsgPackReader, TestReadBin8Zero) { + Buffer.assign("\xc4\x00", 2); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Binary); + EXPECT_EQ(Obj.Raw, StringRef()); +} + +TEST_F(MsgPackReader, TestReadBin8One) { + std::string Result; + Result.assign(1, 'a'); + Buffer = "\xc4\x01"; + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Binary); + EXPECT_EQ(Obj.Raw, Result); +} + +TEST_F(MsgPackReader, TestReadBin8Max) { + std::string Result; + Result.assign(UINT8_MAX, 'a'); + Buffer = "\xc4\xff"; + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Binary); + EXPECT_EQ(Obj.Raw, Result); +} + +TEST_F(MsgPackReader, TestReadBin16Zero) { + Buffer.assign("\xc5\x00\x00", 3); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Binary); + EXPECT_EQ(Obj.Raw, StringRef()); +} + +TEST_F(MsgPackReader, TestReadBin16One) { + std::string Result; + Result.assign(1, 'a'); + Buffer.assign("\xc5\x00\x01", 3); + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Binary); + EXPECT_EQ(Obj.Raw, Result); +} + +TEST_F(MsgPackReader, TestReadBin16Max) { + std::string Result; + Result.assign(UINT16_MAX, 'a'); + Buffer = "\xc5\xff\xff"; + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Binary); + EXPECT_EQ(Obj.Raw, Result); +} + +TEST_F(MsgPackReader, TestReadBin32Zero) { + Buffer.assign("\xc6\x00\x00\x00\x00", 5); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Binary); + EXPECT_EQ(Obj.Raw, StringRef()); +} + +TEST_F(MsgPackReader, TestReadBin32One) { + std::string Result; + Result.assign(1, 'a'); + Buffer.assign("\xc6\x00\x00\x00\x01", 5); + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Binary); + EXPECT_EQ(Obj.Raw, Result); +} + +TEST_F(MsgPackReader, TestReadBin32Max) { + std::string Result; + Result.assign(static_cast(UINT16_MAX) + 1, 'a'); + Buffer.assign("\xc6\x00\x01\x00\x00", 5); + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Binary); + EXPECT_EQ(Obj.Raw, Result); +} + +TEST_F(MsgPackReader, TestReadFixArrayZero) { + Buffer = "\x90"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Array); + EXPECT_EQ(Obj.Length, 0u); +} + +TEST_F(MsgPackReader, TestReadFixArrayOne) { + Buffer = "\x91"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Array); + EXPECT_EQ(Obj.Length, 1u); +} + +TEST_F(MsgPackReader, TestReadFixArrayMax) { + Buffer = "\x9f"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Array); + // FixArray format's size is a 4 bit unsigned integer, so max is 1111 = 15 + EXPECT_EQ(Obj.Length, 15u); +} + +TEST_F(MsgPackReader, TestReadArray16Zero) { + Buffer.assign("\xdc\x00\x00", 3); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Array); + EXPECT_EQ(Obj.Length, 0u); +} + +TEST_F(MsgPackReader, TestReadArray16One) { + Buffer.assign("\xdc\x00\x01", 3); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Array); + EXPECT_EQ(Obj.Length, 1u); +} + +TEST_F(MsgPackReader, TestReadArray16Max) { + Buffer = "\xdc\xff\xff"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Array); + EXPECT_EQ(Obj.Length, static_cast(UINT16_MAX)); +} + +TEST_F(MsgPackReader, TestReadArray32Zero) { + Buffer.assign("\xdd\x00\x00\x00\x00", 5); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Array); + EXPECT_EQ(Obj.Length, 0u); +} + +TEST_F(MsgPackReader, TestReadArray32One) { + Buffer.assign("\xdd\x00\x00\x00\x01", 5); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Array); + EXPECT_EQ(Obj.Length, 1u); +} + +TEST_F(MsgPackReader, TestReadArray32Max) { + Buffer = "\xdd\xff\xff\xff\xff"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Array); + EXPECT_EQ(Obj.Length, static_cast(UINT32_MAX)); +} + +TEST_F(MsgPackReader, TestReadFixMapZero) { + Buffer = "\x80"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Map); + EXPECT_EQ(Obj.Length, 0u); +} + +TEST_F(MsgPackReader, TestReadFixMapOne) { + Buffer = "\x81"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Map); + EXPECT_EQ(Obj.Length, 1u); +} + +TEST_F(MsgPackReader, TestReadFixMapMax) { + Buffer = "\x8f"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Map); + // FixMap format's size is a 4 bit unsigned integer, so max is 1111 = 15 + EXPECT_EQ(Obj.Length, 15u); +} + +TEST_F(MsgPackReader, TestReadMap16Zero) { + Buffer.assign("\xde\x00\x00", 3); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Map); + EXPECT_EQ(Obj.Length, 0u); +} + +TEST_F(MsgPackReader, TestReadMap16One) { + Buffer.assign("\xde\x00\x01", 3); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Map); + EXPECT_EQ(Obj.Length, 1u); +} + +TEST_F(MsgPackReader, TestReadMap16Max) { + Buffer = "\xde\xff\xff"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Map); + EXPECT_EQ(Obj.Length, static_cast(UINT16_MAX)); +} + +TEST_F(MsgPackReader, TestReadMap32Zero) { + Buffer.assign("\xdf\x00\x00\x00\x00", 5); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Map); + EXPECT_EQ(Obj.Length, 0u); +} + +TEST_F(MsgPackReader, TestReadMap32One) { + Buffer.assign("\xdf\x00\x00\x00\x01", 5); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Map); + EXPECT_EQ(Obj.Length, 1u); +} + +TEST_F(MsgPackReader, TestReadMap32Max) { + Buffer = "\xdf\xff\xff\xff\xff"; + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Map); + EXPECT_EQ(Obj.Length, static_cast(UINT32_MAX)); +} + +// FixExt formats are only available for these specific lengths: 1, 2, 4, 8, 16 + +TEST_F(MsgPackReader, TestReadFixExt1) { + std::string Result; + Result.assign(1, 'a'); + Buffer = "\xd4\x01"; + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Extension); + EXPECT_EQ(Obj.Extension.Type, 0x01); + EXPECT_EQ(Obj.Extension.Bytes, Result); +} + +TEST_F(MsgPackReader, TestReadFixExt2) { + std::string Result; + Result.assign(2, 'a'); + Buffer = "\xd5\x01"; + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Extension); + EXPECT_EQ(Obj.Extension.Type, 0x01); + EXPECT_EQ(Obj.Extension.Bytes, Result); +} + +TEST_F(MsgPackReader, TestReadFixExt4) { + std::string Result; + Result.assign(4, 'a'); + Buffer = "\xd6\x01"; + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Extension); + EXPECT_EQ(Obj.Extension.Type, 0x01); + EXPECT_EQ(Obj.Extension.Bytes, Result); +} + +TEST_F(MsgPackReader, TestReadFixExt8) { + std::string Result; + Result.assign(8, 'a'); + Buffer = "\xd7\x01"; + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Extension); + EXPECT_EQ(Obj.Extension.Type, 0x01); + EXPECT_EQ(Obj.Extension.Bytes, Result); +} + +TEST_F(MsgPackReader, TestReadFixExt16) { + std::string Result; + Result.assign(16, 'a'); + Buffer = "\xd8\x01"; + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Extension); + EXPECT_EQ(Obj.Extension.Type, 0x01); + EXPECT_EQ(Obj.Extension.Bytes, Result); +} + +TEST_F(MsgPackReader, TestReadExt8Min) { + std::string Result; + // There are fix variants for sizes 1 and 2 + Buffer.assign("\xc7\x00\x01", 3); + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Extension); + EXPECT_EQ(Obj.Extension.Type, 0x01); + EXPECT_EQ(Obj.Extension.Bytes, Result); +} + +TEST_F(MsgPackReader, TestReadExt8Max) { + std::string Result; + Result.assign(UINT8_MAX, 'a'); + Buffer.assign("\xc7\xff\x01", 3); + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Extension); + EXPECT_EQ(Obj.Extension.Type, 0x01); + EXPECT_EQ(Obj.Extension.Bytes, Result); +} + +TEST_F(MsgPackReader, TestReadExt16Min) { + std::string Result; + Result.assign(static_cast(UINT8_MAX) + 1, 'a'); + Buffer.assign("\xc8\x01\x00\x01", 4); + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Extension); + EXPECT_EQ(Obj.Extension.Type, 0x01); + EXPECT_EQ(Obj.Extension.Bytes, Result); +} + +TEST_F(MsgPackReader, TestReadExt16Max) { + std::string Result; + Result.assign(UINT16_MAX, 'a'); + Buffer = "\xc8\xff\xff\x01"; + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Extension); + EXPECT_EQ(Obj.Extension.Type, 0x01); + EXPECT_EQ(Obj.Extension.Bytes, Result); +} + +TEST_F(MsgPackReader, TestReadExt32Min) { + std::string Result; + Result.assign(static_cast(UINT16_MAX) + 1, 'a'); + Buffer.assign("\xc9\x00\x01\x00\x00\x01", 6); + Buffer.append(Result); + Reader MPReader(Buffer); + EXPECT_EQ(MPReader.Read(Obj), true); + EXPECT_EQ(Obj.Kind, Type::Extension); + EXPECT_EQ(Obj.Extension.Type, 0x01); + EXPECT_EQ(Obj.Extension.Bytes, Result); +} Index: unittests/BinaryFormat/MsgPackWriterTest.cpp =================================================================== --- /dev/null +++ unittests/BinaryFormat/MsgPackWriterTest.cpp @@ -0,0 +1,604 @@ +//===- MsgPackWriterTest.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/MsgPackWriter.h" +#include "llvm/BinaryFormat/MsgPack.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::msgpack; + +struct MsgPackWriter : public ::testing::Test { + std::string Buffer; + llvm::raw_string_ostream OStream; + Writer MPWriter; + + MsgPackWriter() : OStream(Buffer), MPWriter(OStream) {} +}; + +TEST_F(MsgPackWriter, TestWriteNil) { + MPWriter.WriteNil(); + EXPECT_EQ(OStream.str(), "\xc0"); +} + +TEST_F(MsgPackWriter, TestWriteBool) { + MPWriter.Write(true); + MPWriter.Write(false); + EXPECT_EQ(OStream.str(), "\xc3\xc2"); +} + +TEST_F(MsgPackWriter, TestWriteFixPositiveInt) { + // FixPositiveInt form bitpattern starts with 0, so max FixPositiveInt + // is 01111111 = 127 + for (uint64_t u = 0; u <= 127; ++u) { + Buffer.clear(); + MPWriter.Write(u); + std::string Output = OStream.str(); + EXPECT_EQ(Output.size(), 1u); + EXPECT_EQ(Output.data()[0], static_cast(u)); + } +} + +TEST_F(MsgPackWriter, TestWriteUInt8Min) { + // See TestWriteFixPositiveInt for why 128 is the min non-fix Int8 + uint64_t u = 128; + MPWriter.Write(u); + EXPECT_EQ(OStream.str(), "\xcc\x80"); +} + +TEST_F(MsgPackWriter, TestWriteUInt8) { + uint64_t u = 221; + MPWriter.Write(u); + EXPECT_EQ(OStream.str(), "\xcc\xdd"); +} + +TEST_F(MsgPackWriter, TestWriteUInt8Max) { + uint64_t u = UINT8_MAX; + MPWriter.Write(u); + EXPECT_EQ(OStream.str(), "\xcc\xff"); +} + +TEST_F(MsgPackWriter, TestWriteUInt16Min) { + uint64_t u = static_cast(UINT8_MAX) + 1; + MPWriter.Write(u); + EXPECT_EQ(OStream.str(), std::string("\xcd\x01\x00", 3)); +} + +TEST_F(MsgPackWriter, TestWriteUInt16) { + uint64_t u = 43981; + MPWriter.Write(u); + EXPECT_EQ(OStream.str(), "\xcd\xab\xcd"); +} + +TEST_F(MsgPackWriter, TestWriteUInt16Max) { + uint64_t u = UINT16_MAX; + MPWriter.Write(u); + EXPECT_EQ(OStream.str(), "\xcd\xff\xff"); +} + +TEST_F(MsgPackWriter, TestWriteUInt32Min) { + uint64_t u = static_cast(UINT16_MAX) + 1; + MPWriter.Write(u); + EXPECT_EQ(OStream.str(), std::string("\xce\x00\x01\x00\x00", 5)); +} + +TEST_F(MsgPackWriter, TestWriteUInt32) { + uint64_t u = 2882400186; + MPWriter.Write(u); + EXPECT_EQ(OStream.str(), "\xce\xab\xcd\xef\xba"); +} + +TEST_F(MsgPackWriter, TestWriteUInt32Max) { + uint64_t u = UINT32_MAX; + MPWriter.Write(u); + EXPECT_EQ(OStream.str(), "\xce\xff\xff\xff\xff"); +} + +TEST_F(MsgPackWriter, TestWriteUInt64Min) { + uint64_t u = static_cast(UINT32_MAX) + 1; + MPWriter.Write(u); + EXPECT_EQ(OStream.str(), + std::string("\xcf\x00\x00\x00\x01\x00\x00\x00\x00", 9)); +} + +TEST_F(MsgPackWriter, TestWriteUInt64) { + uint64_t u = 0x010203040506074a; + MPWriter.Write(u); + EXPECT_EQ(OStream.str(), "\xcf\x01\x02\x03\x04\x05\x06\x07\x4a"); +} + +TEST_F(MsgPackWriter, TestWriteUInt64Max) { + uint64_t u = UINT64_MAX; + MPWriter.Write(u); + EXPECT_EQ(OStream.str(), "\xcf\xff\xff\xff\xff\xff\xff\xff\xff"); +} + +TEST_F(MsgPackWriter, TestWriteFixNegativeInt) { + // Positive values will be written in a UInt form, so max FixNegativeInt is -1 + // + // FixNegativeInt form bitpattern starts with 111, so min FixNegativeInt + // is 11100000 = -32 + for (int64_t i = -1; i >= -32; --i) { + Buffer.clear(); + MPWriter.Write(i); + std::string Output = OStream.str(); + EXPECT_EQ(Output.size(), 1u); + EXPECT_EQ(Output.data()[0], static_cast(i)); + } +} + +TEST_F(MsgPackWriter, TestWriteInt8Max) { + // See TestWriteFixNegativeInt for why -33 is the max non-fix Int8 + int64_t i = -33; + MPWriter.Write(i); + EXPECT_EQ(OStream.str(), "\xd0\xdf"); +} + +TEST_F(MsgPackWriter, TestWriteInt8) { + int64_t i = -40; + MPWriter.Write(i); + EXPECT_EQ(OStream.str(), "\xd0\xd8"); +} + +TEST_F(MsgPackWriter, TestWriteInt8Min) { + int64_t i = INT8_MIN; + MPWriter.Write(i); + EXPECT_EQ(OStream.str(), "\xd0\x80"); +} + +TEST_F(MsgPackWriter, TestWriteInt16Max) { + int64_t i = static_cast(INT8_MIN) - 1; + MPWriter.Write(i); + EXPECT_EQ(OStream.str(), "\xd1\xff\x7f"); +} + +TEST_F(MsgPackWriter, TestWriteInt16) { + int64_t i = -4369; + MPWriter.Write(i); + EXPECT_EQ(OStream.str(), "\xd1\xee\xef"); +} + +TEST_F(MsgPackWriter, TestWriteInt16Min) { + int64_t i = INT16_MIN; + MPWriter.Write(i); + EXPECT_EQ(OStream.str(), std::string("\xd1\x80\x00", 3)); +} + +TEST_F(MsgPackWriter, TestWriteInt32Max) { + int64_t i = static_cast(INT16_MIN) - 1; + MPWriter.Write(i); + EXPECT_EQ(OStream.str(), "\xd2\xff\xff\x7f\xff"); +} + +TEST_F(MsgPackWriter, TestWriteInt32) { + int64_t i = -286331153; + MPWriter.Write(i); + EXPECT_EQ(OStream.str(), "\xd2\xee\xee\xee\xef"); +} + +TEST_F(MsgPackWriter, TestWriteInt32Min) { + int64_t i = INT32_MIN; + MPWriter.Write(i); + EXPECT_EQ(OStream.str(), std::string("\xd2\x80\x00\x00\x00", 5)); +} + +TEST_F(MsgPackWriter, TestWriteInt64Max) { + int64_t i = static_cast(INT32_MIN) - 1; + MPWriter.Write(i); + EXPECT_EQ(OStream.str(), "\xd3\xff\xff\xff\xff\x7f\xff\xff\xff"); +} + +TEST_F(MsgPackWriter, TestWriteInt64) { + int64_t i = -1229782938247303441; + MPWriter.Write(i); + EXPECT_EQ(OStream.str(), "\xd3\xee\xee\xee\xee\xee\xee\xee\xef"); +} + +TEST_F(MsgPackWriter, TestWriteInt64Min) { + int64_t i = INT64_MIN; + MPWriter.Write(i); + EXPECT_EQ(OStream.str(), + std::string("\xd3\x80\x00\x00\x00\x00\x00\x00\x00", 9)); +} + +TEST_F(MsgPackWriter, TestWriteFloat32) { + float f = -3.6973142664068907e+28; + MPWriter.Write(f); + EXPECT_EQ(OStream.str(), "\xca\xee\xee\xee\xef"); +} + +TEST_F(MsgPackWriter, TestWriteFloat64) { + double d = -2.2899894549927042e+226; + MPWriter.Write(d); + EXPECT_EQ(OStream.str(), "\xcb\xee\xee\xee\xee\xee\xee\xee\xef"); +} + +TEST_F(MsgPackWriter, TestWriteFixStrMin) { + std::string s; + MPWriter.Write(s); + EXPECT_EQ(OStream.str(), "\xa0"); +} + +TEST_F(MsgPackWriter, TestWriteFixStr) { + std::string s = "foo"; + MPWriter.Write(s); + EXPECT_EQ(OStream.str(), "\xa3" + "foo"); +} + +TEST_F(MsgPackWriter, TestWriteFixStrMax) { + std::string s; + // FixStr format's size is a 5 bit unsigned integer, so max is 11111 = 31 + s.assign(31, 'a'); + MPWriter.Write(s); + s.insert(0, "\xbf"); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteStr8Min) { + std::string s; + // See TestWriteFixStrMax for why 32 is the min non-fix Str8 + s.assign(32, 'a'); + MPWriter.Write(s); + s.insert(0, "\xd9\x20"); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteStr8) { + std::string s; + s.assign(33, 'a'); + MPWriter.Write(s); + s.insert(0, "\xd9\x21"); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteStr8Max) { + std::string s; + s.assign(UINT8_MAX, 'a'); + MPWriter.Write(s); + s.insert(0, "\xd9\xff"); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteStr16Min) { + std::string s; + s.assign(static_cast(UINT8_MAX) + 1, 'a'); + MPWriter.Write(s); + s.insert(0, std::string("\xda\x01\x00", 3)); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteStr16) { + std::string s; + s.assign(511, 'a'); + MPWriter.Write(s); + s.insert(0, "\xda\x01\xff"); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteStr16Max) { + std::string s; + s.assign(UINT16_MAX, 'a'); + MPWriter.Write(s); + s.insert(0, "\xda\xff\xff"); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteStr32Min) { + std::string s; + s.assign(static_cast(UINT16_MAX) + 1, 'a'); + MPWriter.Write(s); + s.insert(0, std::string("\xdb\x00\x01\x00\x00", 5)); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteStr32) { + std::string s; + s.assign(131071, 'a'); + MPWriter.Write(s); + s.insert(0, std::string("\xdb\x00\x01\xff\xff", 5)); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteBin8Min) { + std::string s; + MemoryBufferRef Ref = MemoryBufferRef(s, ""); + MPWriter.Write(Ref); + s.insert(0, std::string("\xc4\x00", 2)); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteBin8) { + std::string s; + s.assign(5, 'a'); + MemoryBufferRef Ref = MemoryBufferRef(s, ""); + MPWriter.Write(Ref); + s.insert(0, "\xc4\x05"); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteBin8Max) { + std::string s; + s.assign(UINT8_MAX, 'a'); + MemoryBufferRef Ref = MemoryBufferRef(s, ""); + MPWriter.Write(Ref); + s.insert(0, "\xc4\xff"); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteBin16Min) { + std::string s; + s.assign(static_cast(UINT8_MAX) + 1, 'a'); + MemoryBufferRef Ref = MemoryBufferRef(s, ""); + MPWriter.Write(Ref); + s.insert(0, std::string("\xc5\x01\x00", 3)); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteBin16) { + std::string s; + s.assign(511, 'a'); + MemoryBufferRef Ref = MemoryBufferRef(s, ""); + MPWriter.Write(Ref); + s.insert(0, "\xc5\x01\xff"); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteBin16Max) { + std::string s; + s.assign(UINT16_MAX, 'a'); + MemoryBufferRef Ref = MemoryBufferRef(s, ""); + MPWriter.Write(Ref); + s.insert(0, "\xc5\xff\xff"); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteBin32Min) { + std::string s; + s.assign(static_cast(UINT16_MAX) + 1, 'a'); + MemoryBufferRef Ref = MemoryBufferRef(s, ""); + MPWriter.Write(Ref); + s.insert(0, std::string("\xc6\x00\x01\x00\x00", 5)); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteBin32) { + std::string s; + s.assign(131071, 'a'); + MemoryBufferRef Ref = MemoryBufferRef(s, ""); + MPWriter.Write(Ref); + s.insert(0, std::string("\xc6\x00\x01\xff\xff", 5)); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteFixArrayMin) { + MPWriter.WriteArraySize(0); + EXPECT_EQ(OStream.str(), "\x90"); +} + +TEST_F(MsgPackWriter, TestWriteFixArray) { + MPWriter.WriteArraySize(4); + EXPECT_EQ(OStream.str(), "\x94"); +} + +TEST_F(MsgPackWriter, TestWriteFixArrayMax) { + // FixArray format's size is a 4 bit unsigned integer, so max is 1111 = 15 + MPWriter.WriteArraySize(15); + EXPECT_EQ(OStream.str(), "\x9f"); +} + +TEST_F(MsgPackWriter, TestWriteArray16Min) { + // See TestWriteFixArrayMax for why 16 is the min non-fix Array16 + MPWriter.WriteArraySize(16); + EXPECT_EQ(OStream.str(), std::string("\xdc\x00\x10", 3)); +} + +TEST_F(MsgPackWriter, TestWriteArray16) { + MPWriter.WriteArraySize(273); + EXPECT_EQ(OStream.str(), "\xdc\x01\x11"); +} + +TEST_F(MsgPackWriter, TestWriteArray16Max) { + MPWriter.WriteArraySize(UINT16_MAX); + EXPECT_EQ(OStream.str(), "\xdc\xff\xff"); +} + +TEST_F(MsgPackWriter, TestWriteArray32Min) { + MPWriter.WriteArraySize(static_cast(UINT16_MAX) + 1); + EXPECT_EQ(OStream.str(), std::string("\xdd\x00\x01\x00\x00", 5)); +} + +TEST_F(MsgPackWriter, TestWriteArray32) { + MPWriter.WriteArraySize(131071); + EXPECT_EQ(OStream.str(), std::string("\xdd\x00\x01\xff\xff", 5)); +} + +TEST_F(MsgPackWriter, TestWriteArray32Max) { + MPWriter.WriteArraySize(UINT32_MAX); + EXPECT_EQ(OStream.str(), "\xdd\xff\xff\xff\xff"); +} + +TEST_F(MsgPackWriter, TestWriteFixMapMin) { + MPWriter.WriteMapSize(0); + EXPECT_EQ(OStream.str(), "\x80"); +} + +TEST_F(MsgPackWriter, TestWriteFixMap) { + MPWriter.WriteMapSize(4); + EXPECT_EQ(OStream.str(), "\x84"); +} + +TEST_F(MsgPackWriter, TestWriteFixMapMax) { + // FixMap format's size is a 4 bit unsigned integer, so max is 1111 = 15 + MPWriter.WriteMapSize(15); + EXPECT_EQ(OStream.str(), "\x8f"); +} + +TEST_F(MsgPackWriter, TestWriteMap16Min) { + // See TestWriteFixMapMax for why 16 is the min non-fix Map16 + MPWriter.WriteMapSize(16); + EXPECT_EQ(OStream.str(), std::string("\xde\x00\x10", 3)); +} + +TEST_F(MsgPackWriter, TestWriteMap16) { + MPWriter.WriteMapSize(273); + EXPECT_EQ(OStream.str(), "\xde\x01\x11"); +} + +TEST_F(MsgPackWriter, TestWriteMap16Max) { + MPWriter.WriteMapSize(UINT16_MAX); + EXPECT_EQ(OStream.str(), "\xde\xff\xff"); +} + +TEST_F(MsgPackWriter, TestWriteMap32Min) { + MPWriter.WriteMapSize(static_cast(UINT16_MAX) + 1); + EXPECT_EQ(OStream.str(), std::string("\xdf\x00\x01\x00\x00", 5)); +} + +TEST_F(MsgPackWriter, TestWriteMap32) { + MPWriter.WriteMapSize(131071); + EXPECT_EQ(OStream.str(), std::string("\xdf\x00\x01\xff\xff", 5)); +} + +TEST_F(MsgPackWriter, TestWriteMap32Max) { + MPWriter.WriteMapSize(UINT32_MAX); + EXPECT_EQ(OStream.str(), std::string("\xdf\xff\xff\xff\xff", 5)); +} + +// FixExt formats are only available for these specific lengths: 1, 2, 4, 8, 16 + +TEST_F(MsgPackWriter, TestWriteFixExt1) { + std::string s; + s.assign(1, 'a'); + MemoryBufferRef Ref = MemoryBufferRef(s, ""); + MPWriter.WriteExt(0x01, Ref); + s.insert(0, "\xd4\x01"); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteFixExt2) { + std::string s; + s.assign(2, 'a'); + MemoryBufferRef Ref = MemoryBufferRef(s, ""); + MPWriter.WriteExt(0x01, Ref); + s.insert(0, "\xd5\x01"); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteFixExt4) { + std::string s; + s.assign(4, 'a'); + MemoryBufferRef Ref = MemoryBufferRef(s, ""); + MPWriter.WriteExt(0x01, Ref); + s.insert(0, "\xd6\x01"); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteFixExt8) { + std::string s; + s.assign(8, 'a'); + MemoryBufferRef Ref = MemoryBufferRef(s, ""); + MPWriter.WriteExt(0x01, Ref); + s.insert(0, "\xd7\x01"); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteFixExt16) { + std::string s; + s.assign(16, 'a'); + MemoryBufferRef Ref = MemoryBufferRef(s, ""); + MPWriter.WriteExt(0x01, Ref); + s.insert(0, "\xd8\x01"); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteExt8Min) { + std::string s; + MemoryBufferRef Ref = MemoryBufferRef(s, ""); + MPWriter.WriteExt(0x01, Ref); + s.insert(0, std::string("\xc7\x00\x01", 3)); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteExt8) { + std::string s; + s.assign(0x2a, 'a'); + MemoryBufferRef Ref = MemoryBufferRef(s, ""); + MPWriter.WriteExt(0x01, Ref); + s.insert(0, "\xc7\x2a\x01"); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteExt8Max) { + std::string s; + s.assign(UINT8_MAX, 'a'); + MemoryBufferRef Ref = MemoryBufferRef(s, ""); + MPWriter.WriteExt(0x01, Ref); + s.insert(0, "\xc7\xff\x01"); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteExt16Min) { + std::string s; + s.assign(static_cast(UINT8_MAX) + 1, 'a'); + MemoryBufferRef Ref = MemoryBufferRef(s, ""); + MPWriter.WriteExt(0x02, Ref); + s.insert(0, std::string("\xc8\x01\x00\x02", 4)); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteExt16) { + std::string s; + s.assign(273, 'a'); + MemoryBufferRef Ref = MemoryBufferRef(s, ""); + MPWriter.WriteExt(0x01, Ref); + s.insert(0, "\xc8\x01\x11\x01"); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteExt16Max) { + std::string s; + s.assign(UINT16_MAX, 'a'); + MemoryBufferRef Ref = MemoryBufferRef(s, ""); + MPWriter.WriteExt(0x01, Ref); + s.insert(0, "\xc8\xff\xff\x01"); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteExt32Min) { + std::string s; + s.assign(static_cast(UINT16_MAX) + 1, 'a'); + MemoryBufferRef Ref = MemoryBufferRef(s, ""); + MPWriter.WriteExt(0x02, Ref); + s.insert(0, std::string("\xc9\x00\x01\x00\x00\x02", 6)); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteCompatibleNoStr8) { + Writer CompatWriter(OStream, true); + std::string s; + + s.insert(0, 32, 'a'); + CompatWriter.Write(s); + s.insert(0, std::string("\xda\x00\x20", 3)); + EXPECT_EQ(OStream.str(), s); +} + +TEST_F(MsgPackWriter, TestWriteCompatibleNoBin) { + Writer CompatWriter(OStream, true); + std::string s; + +#ifdef GTEST_HAS_DEATH_TEST +#ifndef NDEBUG + MemoryBufferRef Ref; + Ref = MemoryBufferRef(s, ""); + EXPECT_DEATH(CompatWriter.Write(Ref), "compatible mode"); +#endif +#endif +}