summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/crtp_tag.h71
-rw-r--r--include/endian_str.h132
-rw-r--r--include/io/izlibstream.h120
-rw-r--r--include/io/ozlibstream.h131
-rw-r--r--include/io/stream_reader.h213
-rw-r--r--include/io/stream_writer.h195
-rw-r--r--include/io/zlib_streambuf.h63
-rw-r--r--include/make_unique.h14
-rw-r--r--include/nbt_visitor.h94
-rw-r--r--include/primitive_detail.h48
-rw-r--r--include/tag.h274
-rw-r--r--include/tag_array.h467
-rw-r--r--include/tag_compound.h257
-rw-r--r--include/tag_list.h416
-rw-r--r--include/tag_primitive.h170
-rw-r--r--include/tag_string.h101
-rw-r--r--include/tagfwd.h32
-rw-r--r--include/text/json_formatter.h31
-rw-r--r--include/value.h408
-rw-r--r--include/value_initializer.h72
-rw-r--r--src/endian_str.cpp518
-rw-r--r--src/io/izlibstream.cpp123
-rw-r--r--src/io/ozlibstream.cpp155
-rw-r--r--src/io/stream_reader.cpp155
-rw-r--r--src/io/stream_writer.cpp53
-rw-r--r--src/tag.cpp300
-rw-r--r--src/tag_compound.cpp138
-rw-r--r--src/tag_list.cpp278
-rw-r--r--src/tag_string.cpp29
-rw-r--r--src/text/json_formatter.cpp426
-rw-r--r--src/value.cpp576
-rw-r--r--src/value_initializer.cpp29
-rw-r--r--test/endian_str_test.h278
-rw-r--r--test/format_test.cpp119
-rw-r--r--test/nbttest.h1002
-rw-r--r--test/read_test.h448
-rw-r--r--test/test_value.h44
-rw-r--r--test/write_test.h451
-rw-r--r--test/zlibstream_test.h453
39 files changed, 4695 insertions, 4189 deletions
diff --git a/include/crtp_tag.h b/include/crtp_tag.h
index 28a59f1530..91c629732d 100644
--- a/include/crtp_tag.h
+++ b/include/crtp_tag.h
@@ -33,38 +33,63 @@
namespace nbt
{
-namespace detail
-{
+ namespace detail
+ {
- template<class Sub>
- class crtp_tag : public tag
- {
- public:
- //Pure virtual destructor to make the class abstract
- virtual ~crtp_tag() noexcept = 0;
+ template <class Sub> class crtp_tag : public tag
+ {
+ public:
+ // Pure virtual destructor to make the class abstract
+ virtual ~crtp_tag() noexcept = 0;
- tag_type get_type() const noexcept override final { return Sub::type; };
+ tag_type get_type() const noexcept override final
+ {
+ return Sub::type;
+ };
- std::unique_ptr<tag> clone() const& override final { return make_unique<Sub>(sub_this()); }
- std::unique_ptr<tag> move_clone() && override final { return make_unique<Sub>(std::move(sub_this())); }
+ std::unique_ptr<tag> clone() const& override final
+ {
+ return make_unique<Sub>(sub_this());
+ }
+ std::unique_ptr<tag> move_clone() && override final
+ {
+ return make_unique<Sub>(std::move(sub_this()));
+ }
- tag& assign(tag&& rhs) override final { return sub_this() = dynamic_cast<Sub&&>(rhs); }
+ tag& assign(tag&& rhs) override final
+ {
+ return sub_this() = dynamic_cast<Sub&&>(rhs);
+ }
- void accept(nbt_visitor& visitor) override final { visitor.visit(sub_this()); }
- void accept(const_nbt_visitor& visitor) const override final { visitor.visit(sub_this()); }
+ void accept(nbt_visitor& visitor) override final
+ {
+ visitor.visit(sub_this());
+ }
+ void accept(const_nbt_visitor& visitor) const override final
+ {
+ visitor.visit(sub_this());
+ }
- private:
- bool equals(const tag& rhs) const override final { return sub_this() == static_cast<const Sub&>(rhs); }
+ private:
+ bool equals(const tag& rhs) const override final
+ {
+ return sub_this() == static_cast<const Sub&>(rhs);
+ }
- Sub& sub_this() { return static_cast<Sub&>(*this); }
- const Sub& sub_this() const { return static_cast<const Sub&>(*this); }
- };
+ Sub& sub_this()
+ {
+ return static_cast<Sub&>(*this);
+ }
+ const Sub& sub_this() const
+ {
+ return static_cast<const Sub&>(*this);
+ }
+ };
- template<class Sub>
- crtp_tag<Sub>::~crtp_tag() noexcept {}
+ template <class Sub> crtp_tag<Sub>::~crtp_tag() noexcept {}
-}
+ } // namespace detail
-}
+} // namespace nbt
#endif // CRTP_TAG_H_INCLUDED
diff --git a/include/endian_str.h b/include/endian_str.h
index 4a3500342b..a21a87f94b 100644
--- a/include/endian_str.h
+++ b/include/endian_str.h
@@ -37,82 +37,78 @@
namespace endian
{
-enum endian { little, big };
+ enum endian { little, big };
-///Reads number from stream in specified endian
-template<class T>
-void read(std::istream& is, T& x, endian e);
+ /// Reads number from stream in specified endian
+ template <class T> void read(std::istream& is, T& x, endian e);
-///Reads number from stream in little endian
-NBT_EXPORT void read_little(std::istream& is, uint8_t& x);
-NBT_EXPORT void read_little(std::istream& is, uint16_t& x);
-NBT_EXPORT void read_little(std::istream& is, uint32_t& x);
-NBT_EXPORT void read_little(std::istream& is, uint64_t& x);
-NBT_EXPORT void read_little(std::istream& is, int8_t& x);
-NBT_EXPORT void read_little(std::istream& is, int16_t& x);
-NBT_EXPORT void read_little(std::istream& is, int32_t& x);
-NBT_EXPORT void read_little(std::istream& is, int64_t& x);
-NBT_EXPORT void read_little(std::istream& is, float& x);
-NBT_EXPORT void read_little(std::istream& is, double& x);
+ /// Reads number from stream in little endian
+ NBT_EXPORT void read_little(std::istream& is, uint8_t& x);
+ NBT_EXPORT void read_little(std::istream& is, uint16_t& x);
+ NBT_EXPORT void read_little(std::istream& is, uint32_t& x);
+ NBT_EXPORT void read_little(std::istream& is, uint64_t& x);
+ NBT_EXPORT void read_little(std::istream& is, int8_t& x);
+ NBT_EXPORT void read_little(std::istream& is, int16_t& x);
+ NBT_EXPORT void read_little(std::istream& is, int32_t& x);
+ NBT_EXPORT void read_little(std::istream& is, int64_t& x);
+ NBT_EXPORT void read_little(std::istream& is, float& x);
+ NBT_EXPORT void read_little(std::istream& is, double& x);
-///Reads number from stream in big endian
-NBT_EXPORT void read_big(std::istream& is, uint8_t& x);
-NBT_EXPORT void read_big(std::istream& is, uint16_t& x);
-NBT_EXPORT void read_big(std::istream& is, uint32_t& x);
-NBT_EXPORT void read_big(std::istream& is, uint64_t& x);
-NBT_EXPORT void read_big(std::istream& is, int8_t& x);
-NBT_EXPORT void read_big(std::istream& is, int16_t& x);
-NBT_EXPORT void read_big(std::istream& is, int32_t& x);
-NBT_EXPORT void read_big(std::istream& is, int64_t& x);
-NBT_EXPORT void read_big(std::istream& is, float& x);
-NBT_EXPORT void read_big(std::istream& is, double& x);
+ /// Reads number from stream in big endian
+ NBT_EXPORT void read_big(std::istream& is, uint8_t& x);
+ NBT_EXPORT void read_big(std::istream& is, uint16_t& x);
+ NBT_EXPORT void read_big(std::istream& is, uint32_t& x);
+ NBT_EXPORT void read_big(std::istream& is, uint64_t& x);
+ NBT_EXPORT void read_big(std::istream& is, int8_t& x);
+ NBT_EXPORT void read_big(std::istream& is, int16_t& x);
+ NBT_EXPORT void read_big(std::istream& is, int32_t& x);
+ NBT_EXPORT void read_big(std::istream& is, int64_t& x);
+ NBT_EXPORT void read_big(std::istream& is, float& x);
+ NBT_EXPORT void read_big(std::istream& is, double& x);
-///Writes number to stream in specified endian
-template<class T>
-void write(std::ostream& os, T x, endian e);
+ /// Writes number to stream in specified endian
+ template <class T> void write(std::ostream& os, T x, endian e);
-///Writes number to stream in little endian
-NBT_EXPORT void write_little(std::ostream& os, uint8_t x);
-NBT_EXPORT void write_little(std::ostream& os, uint16_t x);
-NBT_EXPORT void write_little(std::ostream& os, uint32_t x);
-NBT_EXPORT void write_little(std::ostream& os, uint64_t x);
-NBT_EXPORT void write_little(std::ostream& os, int8_t x);
-NBT_EXPORT void write_little(std::ostream& os, int16_t x);
-NBT_EXPORT void write_little(std::ostream& os, int32_t x);
-NBT_EXPORT void write_little(std::ostream& os, int64_t x);
-NBT_EXPORT void write_little(std::ostream& os, float x);
-NBT_EXPORT void write_little(std::ostream& os, double x);
+ /// Writes number to stream in little endian
+ NBT_EXPORT void write_little(std::ostream& os, uint8_t x);
+ NBT_EXPORT void write_little(std::ostream& os, uint16_t x);
+ NBT_EXPORT void write_little(std::ostream& os, uint32_t x);
+ NBT_EXPORT void write_little(std::ostream& os, uint64_t x);
+ NBT_EXPORT void write_little(std::ostream& os, int8_t x);
+ NBT_EXPORT void write_little(std::ostream& os, int16_t x);
+ NBT_EXPORT void write_little(std::ostream& os, int32_t x);
+ NBT_EXPORT void write_little(std::ostream& os, int64_t x);
+ NBT_EXPORT void write_little(std::ostream& os, float x);
+ NBT_EXPORT void write_little(std::ostream& os, double x);
-///Writes number to stream in big endian
-NBT_EXPORT void write_big(std::ostream& os, uint8_t x);
-NBT_EXPORT void write_big(std::ostream& os, uint16_t x);
-NBT_EXPORT void write_big(std::ostream& os, uint32_t x);
-NBT_EXPORT void write_big(std::ostream& os, uint64_t x);
-NBT_EXPORT void write_big(std::ostream& os, int8_t x);
-NBT_EXPORT void write_big(std::ostream& os, int16_t x);
-NBT_EXPORT void write_big(std::ostream& os, int32_t x);
-NBT_EXPORT void write_big(std::ostream& os, int64_t x);
-NBT_EXPORT void write_big(std::ostream& os, float x);
-NBT_EXPORT void write_big(std::ostream& os, double x);
+ /// Writes number to stream in big endian
+ NBT_EXPORT void write_big(std::ostream& os, uint8_t x);
+ NBT_EXPORT void write_big(std::ostream& os, uint16_t x);
+ NBT_EXPORT void write_big(std::ostream& os, uint32_t x);
+ NBT_EXPORT void write_big(std::ostream& os, uint64_t x);
+ NBT_EXPORT void write_big(std::ostream& os, int8_t x);
+ NBT_EXPORT void write_big(std::ostream& os, int16_t x);
+ NBT_EXPORT void write_big(std::ostream& os, int32_t x);
+ NBT_EXPORT void write_big(std::ostream& os, int64_t x);
+ NBT_EXPORT void write_big(std::ostream& os, float x);
+ NBT_EXPORT void write_big(std::ostream& os, double x);
-template<class T>
-void read(std::istream& is, T& x, endian e)
-{
- if(e == little)
- read_little(is, x);
- else
- read_big(is, x);
-}
+ template <class T> void read(std::istream& is, T& x, endian e)
+ {
+ if (e == little)
+ read_little(is, x);
+ else
+ read_big(is, x);
+ }
-template<class T>
-void write(std::ostream& os, T x, endian e)
-{
- if(e == little)
- write_little(os, x);
- else
- write_big(os, x);
-}
+ template <class T> void write(std::ostream& os, T x, endian e)
+ {
+ if (e == little)
+ write_little(os, x);
+ else
+ write_big(os, x);
+ }
-}
+} // namespace endian
#endif // ENDIAN_STR_H_INCLUDED
diff --git a/include/io/izlibstream.h b/include/io/izlibstream.h
index f7d180f4e8..c2a8e59b04 100644
--- a/include/io/izlibstream.h
+++ b/include/io/izlibstream.h
@@ -33,67 +33,75 @@
namespace zlib
{
-/**
- * @brief Stream buffer used by zlib::izlibstream
- * @sa izlibstream
- */
-class NBT_EXPORT inflate_streambuf : public zlib_streambuf
-{
-public:
- /**
- * @param input the istream to wrap
- * @param bufsize the size of the internal buffers
- * @param window_bits the base two logarithm of the maximum window size that
- * zlib will use.
- * This parameter also determines which type of input to expect.
- * The default argument will autodetect between zlib and gzip data.
- * Refer to the zlib documentation of inflateInit2 for more details.
- *
- * @throw zlib_error if zlib encounters a problem during initialization
- */
- explicit inflate_streambuf(std::istream& input, size_t bufsize = 32768, int window_bits = 32 + 15);
- ~inflate_streambuf() noexcept;
+ /**
+ * @brief Stream buffer used by zlib::izlibstream
+ * @sa izlibstream
+ */
+ class NBT_EXPORT inflate_streambuf : public zlib_streambuf
+ {
+ public:
+ /**
+ * @param input the istream to wrap
+ * @param bufsize the size of the internal buffers
+ * @param window_bits the base two logarithm of the maximum window size
+ * that zlib will use. This parameter also determines which type of
+ * input to expect. The default argument will autodetect between zlib
+ * and gzip data. Refer to the zlib documentation of inflateInit2 for
+ * more details.
+ *
+ * @throw zlib_error if zlib encounters a problem during initialization
+ */
+ explicit inflate_streambuf(std::istream& input, size_t bufsize = 32768,
+ int window_bits = 32 + 15);
+ ~inflate_streambuf() noexcept;
- ///@return the wrapped istream
- std::istream& get_istr() const { return is; }
+ ///@return the wrapped istream
+ std::istream& get_istr() const
+ {
+ return is;
+ }
-private:
- std::istream& is;
- bool stream_end;
+ private:
+ std::istream& is;
+ bool stream_end;
- int_type underflow() override;
-};
+ int_type underflow() override;
+ };
-/**
- * @brief An istream adapter that decompresses data using zlib
- *
- * This istream wraps another istream. The izlibstream will read compressed
- * data from the wrapped istream and inflate (decompress) it with zlib.
- *
- * @note If you want to read more data from the wrapped istream after the end
- * of the compressed data, then it must allow seeking. It is unavoidable for
- * the izlibstream to consume more data after the compressed data.
- * It will automatically attempt to seek the wrapped istream back to the point
- * after the end of the compressed data.
- * @sa inflate_streambuf
- */
-class NBT_EXPORT izlibstream : public std::istream
-{
-public:
- /**
- * @param input the istream to wrap
- * @param bufsize the size of the internal buffers
- */
- explicit izlibstream(std::istream& input, size_t bufsize = 32768):
- std::istream(&buf), buf(input, bufsize)
- {}
- ///@return the wrapped istream
- std::istream& get_istr() const { return buf.get_istr(); }
+ /**
+ * @brief An istream adapter that decompresses data using zlib
+ *
+ * This istream wraps another istream. The izlibstream will read compressed
+ * data from the wrapped istream and inflate (decompress) it with zlib.
+ *
+ * @note If you want to read more data from the wrapped istream after the
+ * end of the compressed data, then it must allow seeking. It is unavoidable
+ * for the izlibstream to consume more data after the compressed data. It
+ * will automatically attempt to seek the wrapped istream back to the point
+ * after the end of the compressed data.
+ * @sa inflate_streambuf
+ */
+ class NBT_EXPORT izlibstream : public std::istream
+ {
+ public:
+ /**
+ * @param input the istream to wrap
+ * @param bufsize the size of the internal buffers
+ */
+ explicit izlibstream(std::istream& input, size_t bufsize = 32768)
+ : std::istream(&buf), buf(input, bufsize)
+ {
+ }
+ ///@return the wrapped istream
+ std::istream& get_istr() const
+ {
+ return buf.get_istr();
+ }
-private:
- inflate_streambuf buf;
-};
+ private:
+ inflate_streambuf buf;
+ };
-}
+} // namespace zlib
#endif // IZLIBSTREAM_H_INCLUDED
diff --git a/include/io/ozlibstream.h b/include/io/ozlibstream.h
index 5ac3dba97f..f04f33deef 100644
--- a/include/io/ozlibstream.h
+++ b/include/io/ozlibstream.h
@@ -33,69 +33,88 @@
namespace zlib
{
-/**
- * @brief Stream buffer used by zlib::ozlibstream
- * @sa ozlibstream
- */
-class NBT_EXPORT deflate_streambuf : public zlib_streambuf
-{
-public:
- /**
- * @param output the ostream to wrap
- * @param bufsize the size of the internal buffers
- * @param level the compression level, ranges from 0 to 9, or -1 for default
- *
- * Refer to the zlib documentation of deflateInit2 for details about the arguments.
- *
- * @throw zlib_error if zlib encounters a problem during initialization
- */
- explicit deflate_streambuf(std::ostream& output, size_t bufsize = 32768, int level = Z_DEFAULT_COMPRESSION, int window_bits = 15, int mem_level = 8, int strategy = Z_DEFAULT_STRATEGY);
- ~deflate_streambuf() noexcept;
+ /**
+ * @brief Stream buffer used by zlib::ozlibstream
+ * @sa ozlibstream
+ */
+ class NBT_EXPORT deflate_streambuf : public zlib_streambuf
+ {
+ public:
+ /**
+ * @param output the ostream to wrap
+ * @param bufsize the size of the internal buffers
+ * @param level the compression level, ranges from 0 to 9, or -1 for
+ * default
+ *
+ * Refer to the zlib documentation of deflateInit2 for details about the
+ * arguments.
+ *
+ * @throw zlib_error if zlib encounters a problem during initialization
+ */
+ explicit deflate_streambuf(std::ostream& output, size_t bufsize = 32768,
+ int level = Z_DEFAULT_COMPRESSION,
+ int window_bits = 15, int mem_level = 8,
+ int strategy = Z_DEFAULT_STRATEGY);
+ ~deflate_streambuf() noexcept;
- ///@return the wrapped ostream
- std::ostream& get_ostr() const { return os; }
+ ///@return the wrapped ostream
+ std::ostream& get_ostr() const
+ {
+ return os;
+ }
- ///Finishes compression and writes all pending data to the output
- void close();
-private:
- std::ostream& os;
+ /// Finishes compression and writes all pending data to the output
+ void close();
- void deflate_chunk(int flush = Z_NO_FLUSH);
+ private:
+ std::ostream& os;
- int_type overflow(int_type ch) override;
- int sync() override;
-};
+ void deflate_chunk(int flush = Z_NO_FLUSH);
-/**
- * @brief An ostream adapter that compresses data using zlib
- *
- * This ostream wraps another ostream. Data written to an ozlibstream will be
- * deflated (compressed) with zlib and written to the wrapped ostream.
- *
- * @sa deflate_streambuf
- */
-class NBT_EXPORT ozlibstream : public std::ostream
-{
-public:
- /**
- * @param output the ostream to wrap
- * @param level the compression level, ranges from 0 to 9, or -1 for default
- * @param gzip if true, the output will be in gzip format rather than zlib
- * @param bufsize the size of the internal buffers
- */
- explicit ozlibstream(std::ostream& output, int level = Z_DEFAULT_COMPRESSION, bool gzip = false, size_t bufsize = 32768):
- std::ostream(&buf), buf(output, bufsize, level, 15 + (gzip ? 16 : 0))
- {}
+ int_type overflow(int_type ch) override;
+ int sync() override;
+ };
+
+ /**
+ * @brief An ostream adapter that compresses data using zlib
+ *
+ * This ostream wraps another ostream. Data written to an ozlibstream will
+ * be deflated (compressed) with zlib and written to the wrapped ostream.
+ *
+ * @sa deflate_streambuf
+ */
+ class NBT_EXPORT ozlibstream : public std::ostream
+ {
+ public:
+ /**
+ * @param output the ostream to wrap
+ * @param level the compression level, ranges from 0 to 9, or -1 for
+ * default
+ * @param gzip if true, the output will be in gzip format rather than
+ * zlib
+ * @param bufsize the size of the internal buffers
+ */
+ explicit ozlibstream(std::ostream& output,
+ int level = Z_DEFAULT_COMPRESSION,
+ bool gzip = false, size_t bufsize = 32768)
+ : std::ostream(&buf),
+ buf(output, bufsize, level, 15 + (gzip ? 16 : 0))
+ {
+ }
+
+ ///@return the wrapped ostream
+ std::ostream& get_ostr() const
+ {
+ return buf.get_ostr();
+ }
- ///@return the wrapped ostream
- std::ostream& get_ostr() const { return buf.get_ostr(); }
+ /// Finishes compression and writes all pending data to the output
+ void close();
- ///Finishes compression and writes all pending data to the output
- void close();
-private:
- deflate_streambuf buf;
-};
+ private:
+ deflate_streambuf buf;
+ };
-}
+} // namespace zlib
#endif // OZLIBSTREAM_H_INCLUDED
diff --git a/include/io/stream_reader.h b/include/io/stream_reader.h
index 821a9ff8d3..05f8d99c92 100644
--- a/include/io/stream_reader.h
+++ b/include/io/stream_reader.h
@@ -36,108 +36,115 @@
namespace nbt
{
-namespace io
-{
-
-///Exception that gets thrown when reading is not successful
-class NBT_EXPORT input_error : public std::runtime_error
-{
- using std::runtime_error::runtime_error;
-};
-
-/**
- * @brief Reads a named tag from the stream, making sure that it is a compound
- * @param is the stream to read from
- * @param e the byte order of the source data. The Java edition
- * of Minecraft uses Big Endian, the Pocket edition uses Little Endian
- * @throw input_error on failure, or if the tag in the stream is not a compound
- */
-NBT_EXPORT std::pair<std::string, std::unique_ptr<tag_compound>> read_compound(std::istream& is, endian::endian e = endian::big);
-
-/**
- * @brief Reads a named tag from the stream
- * @param is the stream to read from
- * @param e the byte order of the source data. The Java edition
- * of Minecraft uses Big Endian, the Pocket edition uses Little Endian
- * @throw input_error on failure
- */
-NBT_EXPORT std::pair<std::string, std::unique_ptr<tag>> read_tag(std::istream& is, endian::endian e = endian::big);
-
-/**
- * @brief Helper class for reading NBT tags from input streams
- *
- * Can be reused to read multiple tags
- */
-class NBT_EXPORT stream_reader
-{
-public:
- /**
- * @param is the stream to read from
- * @param e the byte order of the source data. The Java edition
- * of Minecraft uses Big Endian, the Pocket edition uses Little Endian
- */
- explicit stream_reader(std::istream& is, endian::endian e = endian::big) noexcept;
-
- ///Returns the stream
- std::istream& get_istr() const;
- ///Returns the byte order
- endian::endian get_endian() const;
-
- /**
- * @brief Reads a named tag from the stream, making sure that it is a compound
- * @throw input_error on failure, or if the tag in the stream is not a compound
- */
- std::pair<std::string, std::unique_ptr<tag_compound>> read_compound();
-
- /**
- * @brief Reads a named tag from the stream
- * @throw input_error on failure
- */
- std::pair<std::string, std::unique_ptr<tag>> read_tag();
-
- /**
- * @brief Reads a tag of the given type without name from the stream
- * @throw input_error on failure
- */
- std::unique_ptr<tag> read_payload(tag_type type);
-
- /**
- * @brief Reads a tag type from the stream
- * @param allow_end whether to consider tag_type::End valid
- * @throw input_error on failure
- */
- tag_type read_type(bool allow_end = false);
-
- /**
- * @brief Reads a binary number from the stream
- *
- * On failure, will set the failbit on the stream.
- */
- template<class T>
- void read_num(T& x);
-
- /**
- * @brief Reads an NBT string from the stream
- *
- * An NBT string consists of two bytes indicating the length, followed by
- * the characters encoded in modified UTF-8.
- * @throw input_error on failure
- */
- std::string read_string();
-
-private:
- std::istream& is;
- int depth = 0;
- const endian::endian endian;
-};
-
-template<class T>
-void stream_reader::read_num(T& x)
-{
- endian::read(is, x, endian);
-}
-
-}
-}
+ namespace io
+ {
+
+ /// Exception that gets thrown when reading is not successful
+ class NBT_EXPORT input_error : public std::runtime_error
+ {
+ using std::runtime_error::runtime_error;
+ };
+
+ /**
+ * @brief Reads a named tag from the stream, making sure that it is a
+ * compound
+ * @param is the stream to read from
+ * @param e the byte order of the source data. The Java edition
+ * of Minecraft uses Big Endian, the Pocket edition uses Little Endian
+ * @throw input_error on failure, or if the tag in the stream is not a
+ * compound
+ */
+ NBT_EXPORT std::pair<std::string, std::unique_ptr<tag_compound>>
+ read_compound(std::istream& is, endian::endian e = endian::big);
+
+ /**
+ * @brief Reads a named tag from the stream
+ * @param is the stream to read from
+ * @param e the byte order of the source data. The Java edition
+ * of Minecraft uses Big Endian, the Pocket edition uses Little Endian
+ * @throw input_error on failure
+ */
+ NBT_EXPORT std::pair<std::string, std::unique_ptr<tag>>
+ read_tag(std::istream& is, endian::endian e = endian::big);
+
+ /**
+ * @brief Helper class for reading NBT tags from input streams
+ *
+ * Can be reused to read multiple tags
+ */
+ class NBT_EXPORT stream_reader
+ {
+ public:
+ /**
+ * @param is the stream to read from
+ * @param e the byte order of the source data. The Java edition
+ * of Minecraft uses Big Endian, the Pocket edition uses Little
+ * Endian
+ */
+ explicit stream_reader(std::istream& is,
+ endian::endian e = endian::big) noexcept;
+
+ /// Returns the stream
+ std::istream& get_istr() const;
+ /// Returns the byte order
+ endian::endian get_endian() const;
+
+ /**
+ * @brief Reads a named tag from the stream, making sure that it is
+ * a compound
+ * @throw input_error on failure, or if the tag in the stream is not
+ * a compound
+ */
+ std::pair<std::string, std::unique_ptr<tag_compound>>
+ read_compound();
+
+ /**
+ * @brief Reads a named tag from the stream
+ * @throw input_error on failure
+ */
+ std::pair<std::string, std::unique_ptr<tag>> read_tag();
+
+ /**
+ * @brief Reads a tag of the given type without name from the stream
+ * @throw input_error on failure
+ */
+ std::unique_ptr<tag> read_payload(tag_type type);
+
+ /**
+ * @brief Reads a tag type from the stream
+ * @param allow_end whether to consider tag_type::End valid
+ * @throw input_error on failure
+ */
+ tag_type read_type(bool allow_end = false);
+
+ /**
+ * @brief Reads a binary number from the stream
+ *
+ * On failure, will set the failbit on the stream.
+ */
+ template <class T> void read_num(T& x);
+
+ /**
+ * @brief Reads an NBT string from the stream
+ *
+ * An NBT string consists of two bytes indicating the length,
+ * followed by the characters encoded in modified UTF-8.
+ * @throw input_error on failure
+ */
+ std::string read_string();
+
+ private:
+ std::istream& is;
+ int depth = 0;
+ const endian::endian endian;
+ };
+
+ template <class T> void stream_reader::read_num(T& x)
+ {
+ endian::read(is, x, endian);
+ }
+
+ } // namespace io
+} // namespace nbt
#endif // STREAM_READER_H_INCLUDED
diff --git a/include/io/stream_writer.h b/include/io/stream_writer.h
index 32a01c89c9..04f039948c 100644
--- a/include/io/stream_writer.h
+++ b/include/io/stream_writer.h
@@ -33,95 +33,110 @@
namespace nbt
{
-namespace io
-{
-
-/* Not sure if that is even needed
-///Exception that gets thrown when writing is not successful
-class output_error : public std::runtime_error
-{
- using std::runtime_error::runtime_error;
-};*/
-
-/**
- * @brief Writes a named tag into the stream, including the tag type
- * @param key the name of the tag
- * @param t the tag
- * @param os the stream to write to
- * @param e the byte order of the written data. The Java edition
- * of Minecraft uses Big Endian, the Pocket edition uses Little Endian
- */
-NBT_EXPORT void write_tag(const std::string& key, const tag& t, std::ostream& os, endian::endian e = endian::big);
-
-/**
- * @brief Helper class for writing NBT tags to output streams
- *
- * Can be reused to write multiple tags
- */
-class NBT_EXPORT stream_writer
-{
-public:
- ///Maximum length of an NBT string (16 bit unsigned)
- static constexpr size_t max_string_len = UINT16_MAX;
- ///Maximum length of an NBT list or array (32 bit signed)
- static constexpr uint32_t max_array_len = INT32_MAX;
-
- /**
- * @param os the stream to write to
- * @param e the byte order of the written data. The Java edition
- * of Minecraft uses Big Endian, the Pocket edition uses Little Endian
- */
- explicit stream_writer(std::ostream& os, endian::endian e = endian::big) noexcept:
- os(os), endian(e)
- {}
-
- ///Returns the stream
- std::ostream& get_ostr() const { return os; }
- ///Returns the byte order
- endian::endian get_endian() const { return endian; }
-
- /**
- * @brief Writes a named tag into the stream, including the tag type
- */
- void write_tag(const std::string& key, const tag& t);
-
- /**
- * @brief Writes the given tag's payload into the stream
- */
- void write_payload(const tag& t) { t.write_payload(*this); }
-
- /**
- * @brief Writes a tag type to the stream
- */
- void write_type(tag_type tt) { write_num(static_cast<int8_t>(tt)); }
-
- /**
- * @brief Writes a binary number to the stream
- */
- template<class T>
- void write_num(T x);
-
- /**
- * @brief Writes an NBT string to the stream
- *
- * An NBT string consists of two bytes indicating the length, followed by
- * the characters encoded in modified UTF-8.
- * @throw std::length_error if the string is too long for NBT
- */
- void write_string(const std::string& str);
-
-private:
- std::ostream& os;
- const endian::endian endian;
-};
-
-template<class T>
-void stream_writer::write_num(T x)
-{
- endian::write(os, x, endian);
-}
-
-}
-}
+ namespace io
+ {
+
+ /* Not sure if that is even needed
+ ///Exception that gets thrown when writing is not successful
+ class output_error : public std::runtime_error
+ {
+ using std::runtime_error::runtime_error;
+ };*/
+
+ /**
+ * @brief Writes a named tag into the stream, including the tag type
+ * @param key the name of the tag
+ * @param t the tag
+ * @param os the stream to write to
+ * @param e the byte order of the written data. The Java edition
+ * of Minecraft uses Big Endian, the Pocket edition uses Little Endian
+ */
+ NBT_EXPORT void write_tag(const std::string& key, const tag& t,
+ std::ostream& os,
+ endian::endian e = endian::big);
+
+ /**
+ * @brief Helper class for writing NBT tags to output streams
+ *
+ * Can be reused to write multiple tags
+ */
+ class NBT_EXPORT stream_writer
+ {
+ public:
+ /// Maximum length of an NBT string (16 bit unsigned)
+ static constexpr size_t max_string_len = UINT16_MAX;
+ /// Maximum length of an NBT list or array (32 bit signed)
+ static constexpr uint32_t max_array_len = INT32_MAX;
+
+ /**
+ * @param os the stream to write to
+ * @param e the byte order of the written data. The Java edition
+ * of Minecraft uses Big Endian, the Pocket edition uses Little
+ * Endian
+ */
+ explicit stream_writer(std::ostream& os,
+ endian::endian e = endian::big) noexcept
+ : os(os), endian(e)
+ {
+ }
+
+ /// Returns the stream
+ std::ostream& get_ostr() const
+ {
+ return os;
+ }
+ /// Returns the byte order
+ endian::endian get_endian() const
+ {
+ return endian;
+ }
+
+ /**
+ * @brief Writes a named tag into the stream, including the tag type
+ */
+ void write_tag(const std::string& key, const tag& t);
+
+ /**
+ * @brief Writes the given tag's payload into the stream
+ */
+ void write_payload(const tag& t)
+ {
+ t.write_payload(*this);
+ }
+
+ /**
+ * @brief Writes a tag type to the stream
+ */
+ void write_type(tag_type tt)
+ {
+ write_num(static_cast<int8_t>(tt));
+ }
+
+ /**
+ * @brief Writes a binary number to the stream
+ */
+ template <class T> void write_num(T x);
+
+ /**
+ * @brief Writes an NBT string to the stream
+ *
+ * An NBT string consists of two bytes indicating the length,
+ * followed by the characters encoded in modified UTF-8.
+ * @throw std::length_error if the string is too long for NBT
+ */
+ void write_string(const std::string& str);
+
+ private:
+ std::ostream& os;
+ const endian::endian endian;
+ };
+
+ template <class T> void stream_writer::write_num(T x)
+ {
+ endian::write(os, x, endian);
+ }
+
+ } // namespace io
+} // namespace nbt
#endif // STREAM_WRITER_H_INCLUDED
diff --git a/include/io/zlib_streambuf.h b/include/io/zlib_streambuf.h
index e8eea1822f..91a660b9f2 100644
--- a/include/io/zlib_streambuf.h
+++ b/include/io/zlib_streambuf.h
@@ -16,37 +16,36 @@
namespace zlib
{
-///Exception thrown in case zlib encounters a problem
-class NBT_EXPORT zlib_error : public std::runtime_error
-{
-public:
- const int errcode;
-
- zlib_error(const char* msg, int errcode):
- std::runtime_error(msg
- ? std::string(zError(errcode)) + ": " + msg
- : zError(errcode)),
- errcode(errcode)
- {}
-};
-
-///Base class for deflate_streambuf and inflate_streambuf
-class zlib_streambuf : public std::streambuf
-{
-protected:
- std::vector<char> in;
- std::vector<char> out;
- z_stream zstr;
-
- explicit zlib_streambuf(size_t bufsize):
- in(bufsize), out(bufsize)
- {
- zstr.zalloc = Z_NULL;
- zstr.zfree = Z_NULL;
- zstr.opaque = Z_NULL;
- }
-};
-
-}
+ /// Exception thrown in case zlib encounters a problem
+ class NBT_EXPORT zlib_error : public std::runtime_error
+ {
+ public:
+ const int errcode;
+
+ zlib_error(const char* msg, int errcode)
+ : std::runtime_error(msg ? std::string(zError(errcode)) + ": " + msg
+ : zError(errcode)),
+ errcode(errcode)
+ {
+ }
+ };
+
+ /// Base class for deflate_streambuf and inflate_streambuf
+ class zlib_streambuf : public std::streambuf
+ {
+ protected:
+ std::vector<char> in;
+ std::vector<char> out;
+ z_stream zstr;
+
+ explicit zlib_streambuf(size_t bufsize) : in(bufsize), out(bufsize)
+ {
+ zstr.zalloc = Z_NULL;
+ zstr.zfree = Z_NULL;
+ zstr.opaque = Z_NULL;
+ }
+ };
+
+} // namespace zlib
#endif // ZLIB_STREAMBUF_H_INCLUDED
diff --git a/include/make_unique.h b/include/make_unique.h
index a475a4a734..513cbcf69f 100644
--- a/include/make_unique.h
+++ b/include/make_unique.h
@@ -31,13 +31,13 @@
namespace nbt
{
-///Creates a new object of type T and returns a std::unique_ptr to it
-template<class T, class... Args>
-std::unique_ptr<T> make_unique(Args&&... args)
-{
- return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-}
+ /// Creates a new object of type T and returns a std::unique_ptr to it
+ template <class T, class... Args>
+ std::unique_ptr<T> make_unique(Args&&... args)
+ {
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+ }
-}
+} // namespace nbt
#endif // MAKE_UNIQUE_H_INCLUDED
diff --git a/include/nbt_visitor.h b/include/nbt_visitor.h
index 0b37161a5f..e244162f83 100644
--- a/include/nbt_visitor.h
+++ b/include/nbt_visitor.h
@@ -31,58 +31,58 @@
namespace nbt
{
-/**
- * @brief Base class for visitors of tags
- *
- * Implementing the Visitor pattern
- */
-class nbt_visitor
-{
-public:
- virtual ~nbt_visitor() noexcept = 0; //Abstract class
+ /**
+ * @brief Base class for visitors of tags
+ *
+ * Implementing the Visitor pattern
+ */
+ class nbt_visitor
+ {
+ public:
+ virtual ~nbt_visitor() noexcept = 0; // Abstract class
- virtual void visit(tag_byte&) {}
- virtual void visit(tag_short&) {}
- virtual void visit(tag_int&) {}
- virtual void visit(tag_long&) {}
- virtual void visit(tag_float&) {}
- virtual void visit(tag_double&) {}
- virtual void visit(tag_byte_array&) {}
- virtual void visit(tag_string&) {}
- virtual void visit(tag_list&) {}
- virtual void visit(tag_compound&) {}
- virtual void visit(tag_int_array&) {}
- virtual void visit(tag_long_array&) {}
-};
+ virtual void visit(tag_byte&) {}
+ virtual void visit(tag_short&) {}
+ virtual void visit(tag_int&) {}
+ virtual void visit(tag_long&) {}
+ virtual void visit(tag_float&) {}
+ virtual void visit(tag_double&) {}
+ virtual void visit(tag_byte_array&) {}
+ virtual void visit(tag_string&) {}
+ virtual void visit(tag_list&) {}
+ virtual void visit(tag_compound&) {}
+ virtual void visit(tag_int_array&) {}
+ virtual void visit(tag_long_array&) {}
+ };
-/**
- * @brief Base class for visitors of constant tags
- *
- * Implementing the Visitor pattern
- */
-class const_nbt_visitor
-{
-public:
- virtual ~const_nbt_visitor() noexcept = 0; //Abstract class
+ /**
+ * @brief Base class for visitors of constant tags
+ *
+ * Implementing the Visitor pattern
+ */
+ class const_nbt_visitor
+ {
+ public:
+ virtual ~const_nbt_visitor() noexcept = 0; // Abstract class
- virtual void visit(const tag_byte&) {}
- virtual void visit(const tag_short&) {}
- virtual void visit(const tag_int&) {}
- virtual void visit(const tag_long&) {}
- virtual void visit(const tag_float&) {}
- virtual void visit(const tag_double&) {}
- virtual void visit(const tag_byte_array&) {}
- virtual void visit(const tag_string&) {}
- virtual void visit(const tag_list&) {}
- virtual void visit(const tag_compound&) {}
- virtual void visit(const tag_int_array&) {}
- virtual void visit(const tag_long_array&) {}
-};
+ virtual void visit(const tag_byte&) {}
+ virtual void visit(const tag_short&) {}
+ virtual void visit(const tag_int&) {}
+ virtual void visit(const tag_long&) {}
+ virtual void visit(const tag_float&) {}
+ virtual void visit(const tag_double&) {}
+ virtual void visit(const tag_byte_array&) {}
+ virtual void visit(const tag_string&) {}
+ virtual void visit(const tag_list&) {}
+ virtual void visit(const tag_compound&) {}
+ virtual void visit(const tag_int_array&) {}
+ virtual void visit(const tag_long_array&) {}
+ };
-inline nbt_visitor::~nbt_visitor() noexcept {}
+ inline nbt_visitor::~nbt_visitor() noexcept {}
-inline const_nbt_visitor::~const_nbt_visitor() noexcept {}
+ inline const_nbt_visitor::~const_nbt_visitor() noexcept {}
-}
+} // namespace nbt
#endif // NBT_VISITOR_H_INCLUDED
diff --git a/include/primitive_detail.h b/include/primitive_detail.h
index de070d3320..c55cd18398 100644
--- a/include/primitive_detail.h
+++ b/include/primitive_detail.h
@@ -32,21 +32,43 @@
namespace nbt
{
-namespace detail
-{
- ///Meta-struct that holds the tag_type value for a specific primitive type
- template<class T> struct get_primitive_type
- { static_assert(sizeof(T) != sizeof(T), "Invalid type paramter for tag_primitive, can only use types that NBT uses"); };
+ namespace detail
+ {
+ /// Meta-struct that holds the tag_type value for a specific primitive
+ /// type
+ template <class T> struct get_primitive_type {
+ static_assert(sizeof(T) != sizeof(T),
+ "Invalid type paramter for tag_primitive, can only "
+ "use types that NBT uses");
+ };
- template<> struct get_primitive_type<int8_t> : public std::integral_constant<tag_type, tag_type::Byte> {};
- template<> struct get_primitive_type<int16_t> : public std::integral_constant<tag_type, tag_type::Short> {};
- template<> struct get_primitive_type<int32_t> : public std::integral_constant<tag_type, tag_type::Int> {};
- template<> struct get_primitive_type<int64_t> : public std::integral_constant<tag_type, tag_type::Long> {};
- template<> struct get_primitive_type<float> : public std::integral_constant<tag_type, tag_type::Float> {};
- template<> struct get_primitive_type<double> : public std::integral_constant<tag_type, tag_type::Double> {};
-}
+ template <>
+ struct get_primitive_type<int8_t>
+ : public std::integral_constant<tag_type, tag_type::Byte> {
+ };
+ template <>
+ struct get_primitive_type<int16_t>
+ : public std::integral_constant<tag_type, tag_type::Short> {
+ };
+ template <>
+ struct get_primitive_type<int32_t>
+ : public std::integral_constant<tag_type, tag_type::Int> {
+ };
+ template <>
+ struct get_primitive_type<int64_t>
+ : public std::integral_constant<tag_type, tag_type::Long> {
+ };
+ template <>
+ struct get_primitive_type<float>
+ : public std::integral_constant<tag_type, tag_type::Float> {
+ };
+ template <>
+ struct get_primitive_type<double>
+ : public std::integral_constant<tag_type, tag_type::Double> {
+ };
+ } // namespace detail
-}
+} // namespace nbt
///@endcond
#endif // PRIMITIVE_DETAIL_H_INCLUDED
diff --git a/include/tag.h b/include/tag.h
index ac3b49fa3a..92432cf55a 100644
--- a/include/tag.h
+++ b/include/tag.h
@@ -34,142 +34,142 @@
namespace nbt
{
-///Tag type values used in the binary format
-enum class tag_type : int8_t
-{
- End = 0,
- Byte = 1,
- Short = 2,
- Int = 3,
- Long = 4,
- Float = 5,
- Double = 6,
- Byte_Array = 7,
- String = 8,
- List = 9,
- Compound = 10,
- Int_Array = 11,
- Long_Array = 12,
- Null = -1 ///< Used to denote empty @ref value s
-};
-
-/**
- * @brief Returns whether the given number falls within the range of valid tag types
- * @param allow_end whether to consider tag_type::End (0) valid
- */
-NBT_EXPORT bool is_valid_type(int type, bool allow_end = false);
-
-//Forward declarations
-class nbt_visitor;
-class const_nbt_visitor;
-namespace io
-{
- class stream_reader;
- class stream_writer;
-}
-
-///Base class for all NBT tag classes
-class NBT_EXPORT tag
-{
-public:
- //Virtual destructor
- virtual ~tag() noexcept {}
-
- ///Returns the type of the tag
- virtual tag_type get_type() const noexcept = 0;
-
- //Polymorphic clone methods
- virtual std::unique_ptr<tag> clone() const& = 0;
- virtual std::unique_ptr<tag> move_clone() && = 0;
- std::unique_ptr<tag> clone() &&;
-
- /**
- * @brief Returns a reference to the tag as an instance of T
- * @throw std::bad_cast if the tag is not of type T
- */
- template<class T>
- T& as();
- template<class T>
- const T& as() const;
-
- /**
- * @brief Move-assigns the given tag if the class is the same
- * @throw std::bad_cast if @c rhs is not the same type as @c *this
- */
- virtual tag& assign(tag&& rhs) = 0;
-
- /**
- * @brief Calls the appropriate overload of @c visit() on the visitor with
- * @c *this as argument
- *
- * Implementing the Visitor pattern
- */
- virtual void accept(nbt_visitor& visitor) = 0;
- virtual void accept(const_nbt_visitor& visitor) const = 0;
-
- /**
- * @brief Reads the tag's payload from the stream
- * @throw io::stream_reader::input_error on failure
- */
- virtual void read_payload(io::stream_reader& reader) = 0;
-
- /**
- * @brief Writes the tag's payload into the stream
- */
- virtual void write_payload(io::stream_writer& writer) const = 0;
-
- /**
- * @brief Default-constructs a new tag of the given type
- * @throw std::invalid_argument if the type is not valid (e.g. End or Null)
- */
- static std::unique_ptr<tag> create(tag_type type);
- /**
- * @brief Constructs a numeric tag of the given type and value
- * @throw std::invalid_argument if the type is not numeric
- */
- static std::unique_ptr<tag> create(tag_type type, int8_t val);
- static std::unique_ptr<tag> create(tag_type type, int16_t val);
- static std::unique_ptr<tag> create(tag_type type, int32_t val);
- static std::unique_ptr<tag> create(tag_type type, int64_t val);
- static std::unique_ptr<tag> create(tag_type type, float val);
- static std::unique_ptr<tag> create(tag_type type, double val);
-
- friend NBT_EXPORT bool operator==(const tag& lhs, const tag& rhs);
- friend NBT_EXPORT bool operator!=(const tag& lhs, const tag& rhs);
-
-private:
- /**
- * @brief Checks for equality to a tag of the same type
- * @param rhs an instance of the same class as @c *this
- */
- virtual bool equals(const tag& rhs) const = 0;
-};
-
-///Output operator for tag types
-NBT_EXPORT std::ostream& operator<<(std::ostream& os, tag_type tt);
-
-/**
- * @brief Output operator for tags
- *
- * Uses @ref text::json_formatter
- * @relates tag
- */
-NBT_EXPORT std::ostream& operator<<(std::ostream& os, const tag& t);
-
-template<class T>
-T& tag::as()
-{
- static_assert(std::is_base_of<tag, T>::value, "T must be a subclass of tag");
- return dynamic_cast<T&>(*this);
-}
-
-template<class T>
-const T& tag::as() const
-{
- static_assert(std::is_base_of<tag, T>::value, "T must be a subclass of tag");
- return dynamic_cast<const T&>(*this);
-}
-
-}
+ /// Tag type values used in the binary format
+ enum class tag_type : int8_t {
+ End = 0,
+ Byte = 1,
+ Short = 2,
+ Int = 3,
+ Long = 4,
+ Float = 5,
+ Double = 6,
+ Byte_Array = 7,
+ String = 8,
+ List = 9,
+ Compound = 10,
+ Int_Array = 11,
+ Long_Array = 12,
+ Null = -1 ///< Used to denote empty @ref value s
+ };
+
+ /**
+ * @brief Returns whether the given number falls within the range of valid
+ * tag types
+ * @param allow_end whether to consider tag_type::End (0) valid
+ */
+ NBT_EXPORT bool is_valid_type(int type, bool allow_end = false);
+
+ // Forward declarations
+ class nbt_visitor;
+ class const_nbt_visitor;
+ namespace io
+ {
+ class stream_reader;
+ class stream_writer;
+ } // namespace io
+
+ /// Base class for all NBT tag classes
+ class NBT_EXPORT tag
+ {
+ public:
+ // Virtual destructor
+ virtual ~tag() noexcept {}
+
+ /// Returns the type of the tag
+ virtual tag_type get_type() const noexcept = 0;
+
+ // Polymorphic clone methods
+ virtual std::unique_ptr<tag> clone() const& = 0;
+ virtual std::unique_ptr<tag> move_clone() && = 0;
+ std::unique_ptr<tag> clone() &&;
+
+ /**
+ * @brief Returns a reference to the tag as an instance of T
+ * @throw std::bad_cast if the tag is not of type T
+ */
+ template <class T> T& as();
+ template <class T> const T& as() const;
+
+ /**
+ * @brief Move-assigns the given tag if the class is the same
+ * @throw std::bad_cast if @c rhs is not the same type as @c *this
+ */
+ virtual tag& assign(tag&& rhs) = 0;
+
+ /**
+ * @brief Calls the appropriate overload of @c visit() on the visitor
+ * with
+ * @c *this as argument
+ *
+ * Implementing the Visitor pattern
+ */
+ virtual void accept(nbt_visitor& visitor) = 0;
+ virtual void accept(const_nbt_visitor& visitor) const = 0;
+
+ /**
+ * @brief Reads the tag's payload from the stream
+ * @throw io::stream_reader::input_error on failure
+ */
+ virtual void read_payload(io::stream_reader& reader) = 0;
+
+ /**
+ * @brief Writes the tag's payload into the stream
+ */
+ virtual void write_payload(io::stream_writer& writer) const = 0;
+
+ /**
+ * @brief Default-constructs a new tag of the given type
+ * @throw std::invalid_argument if the type is not valid (e.g. End or
+ * Null)
+ */
+ static std::unique_ptr<tag> create(tag_type type);
+ /**
+ * @brief Constructs a numeric tag of the given type and value
+ * @throw std::invalid_argument if the type is not numeric
+ */
+ static std::unique_ptr<tag> create(tag_type type, int8_t val);
+ static std::unique_ptr<tag> create(tag_type type, int16_t val);
+ static std::unique_ptr<tag> create(tag_type type, int32_t val);
+ static std::unique_ptr<tag> create(tag_type type, int64_t val);
+ static std::unique_ptr<tag> create(tag_type type, float val);
+ static std::unique_ptr<tag> create(tag_type type, double val);
+
+ friend NBT_EXPORT bool operator==(const tag& lhs, const tag& rhs);
+ friend NBT_EXPORT bool operator!=(const tag& lhs, const tag& rhs);
+
+ private:
+ /**
+ * @brief Checks for equality to a tag of the same type
+ * @param rhs an instance of the same class as @c *this
+ */
+ virtual bool equals(const tag& rhs) const = 0;
+ };
+
+ /// Output operator for tag types
+ NBT_EXPORT std::ostream& operator<<(std::ostream& os, tag_type tt);
+
+ /**
+ * @brief Output operator for tags
+ *
+ * Uses @ref text::json_formatter
+ * @relates tag
+ */
+ NBT_EXPORT std::ostream& operator<<(std::ostream& os, const tag& t);
+
+ template <class T> T& tag::as()
+ {
+ static_assert(std::is_base_of<tag, T>::value,
+ "T must be a subclass of tag");
+ return dynamic_cast<T&>(*this);
+ }
+
+ template <class T> const T& tag::as() const
+ {
+ static_assert(std::is_base_of<tag, T>::value,
+ "T must be a subclass of tag");
+ return dynamic_cast<const T&>(*this);
+ }
+
+} // namespace nbt
#endif // TAG_H_INCLUDED
diff --git a/include/tag_array.h b/include/tag_array.h
index de1dd0953a..290680573f 100644
--- a/include/tag_array.h
+++ b/include/tag_array.h
@@ -36,206 +36,271 @@
namespace nbt
{
-///@cond
-namespace detail
-{
- ///Meta-struct that holds the tag_type value for a specific array type
- template<class T> struct get_array_type
- { static_assert(sizeof(T) != sizeof(T), "Invalid type paramter for tag_array, can only use byte or int"); };
-
- template<> struct get_array_type<int8_t> : public std::integral_constant<tag_type, tag_type::Byte_Array> {};
- template<> struct get_array_type<int32_t> : public std::integral_constant<tag_type, tag_type::Int_Array> {};
- template<> struct get_array_type<int64_t> : public std::integral_constant<tag_type, tag_type::Long_Array> {};
-}
-///@cond
-
-/**
- * @brief Tag that contains an array of byte or int values
- *
- * Common class for tag_byte_array, tag_int_array and tag_long_array.
- */
-template<class T>
-class tag_array final : public detail::crtp_tag<tag_array<T>>
-{
-public:
- //Iterator types
- typedef typename std::vector<T>::iterator iterator;
- typedef typename std::vector<T>::const_iterator const_iterator;
-
- ///The type of the contained values
- typedef T value_type;
-
- ///The type of the tag
- static constexpr tag_type type = detail::get_array_type<T>::value;
-
- ///Constructs an empty array
- tag_array() {}
-
- ///Constructs an array with the given values
- tag_array(std::initializer_list<T> init): data(init) {}
- tag_array(std::vector<T>&& vec) noexcept: data(std::move(vec)) {}
-
- ///Returns a reference to the vector that contains the values
- std::vector<T>& get() { return data; }
- const std::vector<T>& get() const { return data; }
-
- /**
- * @brief Accesses a value by index with bounds checking
- * @throw std::out_of_range if the index is out of range
- */
- T& at(size_t i) { return data.at(i); }
- T at(size_t i) const { return data.at(i); }
-
- /**
- * @brief Accesses a value by index
- *
- * No bounds checking is performed.
- */
- T& operator[](size_t i) { return data[i]; }
- T operator[](size_t i) const { return data[i]; }
-
- ///Appends a value at the end of the array
- void push_back(T val) { data.push_back(val); }
-
- ///Removes the last element from the array
- void pop_back() { data.pop_back(); }
-
- ///Returns the number of values in the array
- size_t size() const { return data.size(); }
-
- ///Erases all values from the array.
- void clear() { data.clear(); }
-
- //Iterators
- iterator begin() { return data.begin(); }
- iterator end() { return data.end(); }
- const_iterator begin() const { return data.begin(); }
- const_iterator end() const { return data.end(); }
- const_iterator cbegin() const { return data.cbegin(); }
- const_iterator cend() const { return data.cend(); }
-
- void read_payload(io::stream_reader& reader) override;
- /**
- * @inheritdoc
- * @throw std::length_error if the array is too large for NBT
- */
- void write_payload(io::stream_writer& writer) const override;
-
-private:
- std::vector<T> data;
-};
-
-template<class T> bool operator==(const tag_array<T>& lhs, const tag_array<T>& rhs)
-{ return lhs.get() == rhs.get(); }
-template<class T> bool operator!=(const tag_array<T>& lhs, const tag_array<T>& rhs)
-{ return !(lhs == rhs); }
-
-//Slightly different between byte_array and int_array
-//Reading
-template<>
-inline void tag_array<int8_t>::read_payload(io::stream_reader& reader)
-{
- int32_t length;
- reader.read_num(length);
- if(length < 0)
- reader.get_istr().setstate(std::ios::failbit);
- if(!reader.get_istr())
- throw io::input_error("Error reading length of tag_byte_array");
-
- data.resize(length);
- reader.get_istr().read(reinterpret_cast<char*>(data.data()), length);
- if(!reader.get_istr())
- throw io::input_error("Error reading contents of tag_byte_array");
-}
-
-template<typename T>
-inline void tag_array<T>::read_payload(io::stream_reader& reader)
-{
- int32_t length;
- reader.read_num(length);
- if(length < 0)
- reader.get_istr().setstate(std::ios::failbit);
- if(!reader.get_istr())
- throw io::input_error("Error reading length of generic array tag");
-
- data.clear();
- data.reserve(length);
- for(T i = 0; i < length; ++i)
- {
- T val;
- reader.read_num(val);
- data.push_back(val);
- }
- if(!reader.get_istr())
- throw io::input_error("Error reading contents of generic array tag");
-}
-
-template<>
-inline void tag_array<int64_t>::read_payload(io::stream_reader& reader)
-{
- int32_t length;
- reader.read_num(length);
- if(length < 0)
- reader.get_istr().setstate(std::ios::failbit);
- if(!reader.get_istr())
- throw io::input_error("Error reading length of tag_long_array");
-
- data.clear();
- data.reserve(length);
- for(int32_t i = 0; i < length; ++i)
- {
- int64_t val;
- reader.read_num(val);
- data.push_back(val);
- }
- if(!reader.get_istr())
- throw io::input_error("Error reading contents of tag_long_array");
-}
-
-//Writing
-template<>
-inline void tag_array<int8_t>::write_payload(io::stream_writer& writer) const
-{
- if(size() > io::stream_writer::max_array_len)
- {
- writer.get_ostr().setstate(std::ios::failbit);
- throw std::length_error("Byte array is too large for NBT");
- }
- writer.write_num(static_cast<int32_t>(size()));
- writer.get_ostr().write(reinterpret_cast<const char*>(data.data()), data.size());
-}
-
-template<typename T>
-inline void tag_array<T>::write_payload(io::stream_writer& writer) const
-{
- if(size() > io::stream_writer::max_array_len)
- {
- writer.get_ostr().setstate(std::ios::failbit);
- throw std::length_error("Generic array is too large for NBT");
- }
- writer.write_num(static_cast<int32_t>(size()));
- for(T i: data)
- writer.write_num(i);
-}
-
-template<>
-inline void tag_array<int64_t>::write_payload(io::stream_writer& writer) const
-{
- if(size() > io::stream_writer::max_array_len)
- {
- writer.get_ostr().setstate(std::ios::failbit);
- throw std::length_error("Long array is too large for NBT");
- }
- writer.write_num(static_cast<int32_t>(size()));
- for(int64_t i: data)
- writer.write_num(i);
-}
-
-//Typedefs that should be used instead of the template tag_array.
-typedef tag_array<int8_t> tag_byte_array;
-typedef tag_array<int32_t> tag_int_array;
-typedef tag_array<int64_t> tag_long_array;
-
-}
+ ///@cond
+ namespace detail
+ {
+ /// Meta-struct that holds the tag_type value for a specific array type
+ template <class T> struct get_array_type {
+ static_assert(sizeof(T) != sizeof(T),
+ "Invalid type paramter for tag_array, can only use "
+ "byte or int");
+ };
+
+ template <>
+ struct get_array_type<int8_t>
+ : public std::integral_constant<tag_type, tag_type::Byte_Array> {
+ };
+ template <>
+ struct get_array_type<int32_t>
+ : public std::integral_constant<tag_type, tag_type::Int_Array> {
+ };
+ template <>
+ struct get_array_type<int64_t>
+ : public std::integral_constant<tag_type, tag_type::Long_Array> {
+ };
+ } // namespace detail
+ ///@cond
+
+ /**
+ * @brief Tag that contains an array of byte or int values
+ *
+ * Common class for tag_byte_array, tag_int_array and tag_long_array.
+ */
+ template <class T>
+ class tag_array final : public detail::crtp_tag<tag_array<T>>
+ {
+ public:
+ // Iterator types
+ typedef typename std::vector<T>::iterator iterator;
+ typedef typename std::vector<T>::const_iterator const_iterator;
+
+ /// The type of the contained values
+ typedef T value_type;
+
+ /// The type of the tag
+ static constexpr tag_type type = detail::get_array_type<T>::value;
+
+ /// Constructs an empty array
+ tag_array() {}
+
+ /// Constructs an array with the given values
+ tag_array(std::initializer_list<T> init) : data(init) {}
+ tag_array(std::vector<T>&& vec) noexcept : data(std::move(vec)) {}
+
+ /// Returns a reference to the vector that contains the values
+ std::vector<T>& get()
+ {
+ return data;
+ }
+ const std::vector<T>& get() const
+ {
+ return data;
+ }
+
+ /**
+ * @brief Accesses a value by index with bounds checking
+ * @throw std::out_of_range if the index is out of range
+ */
+ T& at(size_t i)
+ {
+ return data.at(i);
+ }
+ T at(size_t i) const
+ {
+ return data.at(i);
+ }
+
+ /**
+ * @brief Accesses a value by index
+ *
+ * No bounds checking is performed.
+ */
+ T& operator[](size_t i)
+ {
+ return data[i];
+ }
+ T operator[](size_t i) const
+ {
+ return data[i];
+ }
+
+ /// Appends a value at the end of the array
+ void push_back(T val)
+ {
+ data.push_back(val);
+ }
+
+ /// Removes the last element from the array
+ void pop_back()
+ {
+ data.pop_back();
+ }
+
+ /// Returns the number of values in the array
+ size_t size() const
+ {
+ return data.size();
+ }
+
+ /// Erases all values from the array.
+ void clear()
+ {
+ data.clear();
+ }
+
+ // Iterators
+ iterator begin()
+ {
+ return data.begin();
+ }
+ iterator end()
+ {
+ return data.end();
+ }
+ const_iterator begin() const
+ {
+ return data.begin();
+ }
+ const_iterator end() const
+ {
+ return data.end();
+ }
+ const_iterator cbegin() const
+ {
+ return data.cbegin();
+ }
+ const_iterator cend() const
+ {
+ return data.cend();
+ }
+
+ void read_payload(io::stream_reader& reader) override;
+ /**
+ * @inheritdoc
+ * @throw std::length_error if the array is too large for NBT
+ */
+ void write_payload(io::stream_writer& writer) const override;
+
+ private:
+ std::vector<T> data;
+ };
+
+ template <class T>
+ bool operator==(const tag_array<T>& lhs, const tag_array<T>& rhs)
+ {
+ return lhs.get() == rhs.get();
+ }
+ template <class T>
+ bool operator!=(const tag_array<T>& lhs, const tag_array<T>& rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+ // Slightly different between byte_array and int_array
+ // Reading
+ template <>
+ inline void tag_array<int8_t>::read_payload(io::stream_reader& reader)
+ {
+ int32_t length;
+ reader.read_num(length);
+ if (length < 0)
+ reader.get_istr().setstate(std::ios::failbit);
+ if (!reader.get_istr())
+ throw io::input_error("Error reading length of tag_byte_array");
+
+ data.resize(length);
+ reader.get_istr().read(reinterpret_cast<char*>(data.data()), length);
+ if (!reader.get_istr())
+ throw io::input_error("Error reading contents of tag_byte_array");
+ }
+
+ template <typename T>
+ inline void tag_array<T>::read_payload(io::stream_reader& reader)
+ {
+ int32_t length;
+ reader.read_num(length);
+ if (length < 0)
+ reader.get_istr().setstate(std::ios::failbit);
+ if (!reader.get_istr())
+ throw io::input_error("Error reading length of generic array tag");
+
+ data.clear();
+ data.reserve(length);
+ for (T i = 0; i < length; ++i) {
+ T val;
+ reader.read_num(val);
+ data.push_back(val);
+ }
+ if (!reader.get_istr())
+ throw io::input_error(
+ "Error reading contents of generic array tag");
+ }
+
+ template <>
+ inline void tag_array<int64_t>::read_payload(io::stream_reader& reader)
+ {
+ int32_t length;
+ reader.read_num(length);
+ if (length < 0)
+ reader.get_istr().setstate(std::ios::failbit);
+ if (!reader.get_istr())
+ throw io::input_error("Error reading length of tag_long_array");
+
+ data.clear();
+ data.reserve(length);
+ for (int32_t i = 0; i < length; ++i) {
+ int64_t val;
+ reader.read_num(val);
+ data.push_back(val);
+ }
+ if (!reader.get_istr())
+ throw io::input_error("Error reading contents of tag_long_array");
+ }
+
+ // Writing
+ template <>
+ inline void
+ tag_array<int8_t>::write_payload(io::stream_writer& writer) const
+ {
+ if (size() > io::stream_writer::max_array_len) {
+ writer.get_ostr().setstate(std::ios::failbit);
+ throw std::length_error("Byte array is too large for NBT");
+ }
+ writer.write_num(static_cast<int32_t>(size()));
+ writer.get_ostr().write(reinterpret_cast<const char*>(data.data()),
+ data.size());
+ }
+
+ template <typename T>
+ inline void tag_array<T>::write_payload(io::stream_writer& writer) const
+ {
+ if (size() > io::stream_writer::max_array_len) {
+ writer.get_ostr().setstate(std::ios::failbit);
+ throw std::length_error("Generic array is too large for NBT");
+ }
+ writer.write_num(static_cast<int32_t>(size()));
+ for (T i : data)
+ writer.write_num(i);
+ }
+
+ template <>
+ inline void
+ tag_array<int64_t>::write_payload(io::stream_writer& writer) const
+ {
+ if (size() > io::stream_writer::max_array_len) {
+ writer.get_ostr().setstate(std::ios::failbit);
+ throw std::length_error("Long array is too large for NBT");
+ }
+ writer.write_num(static_cast<int32_t>(size()));
+ for (int64_t i : data)
+ writer.write_num(i);
+ }
+
+ // Typedefs that should be used instead of the template tag_array.
+ typedef tag_array<int8_t> tag_byte_array;
+ typedef tag_array<int32_t> tag_int_array;
+ typedef tag_array<int64_t> tag_long_array;
+
+} // namespace nbt
#endif // TAG_ARRAY_H_INCLUDED
diff --git a/include/tag_compound.h b/include/tag_compound.h
index 60d9d83927..3f342c6d2b 100644
--- a/include/tag_compound.h
+++ b/include/tag_compound.h
@@ -34,115 +34,152 @@
namespace nbt
{
-///Tag that contains multiple unordered named tags of arbitrary types
-class NBT_EXPORT tag_compound final : public detail::crtp_tag<tag_compound>
-{
- typedef std::map<std::string, value> map_t_;
-
-public:
- //Iterator types
- typedef map_t_::iterator iterator;
- typedef map_t_::const_iterator const_iterator;
-
- ///The type of the tag
- static constexpr tag_type type = tag_type::Compound;
-
- ///Constructs an empty compound
- tag_compound() {}
-
- ///Constructs a compound with the given key-value pairs
- tag_compound(std::initializer_list<std::pair<std::string, value_initializer>> init);
-
- /**
- * @brief Accesses a tag by key with bounds checking
- *
- * Returns a value to the tag with the specified key, or throws an
- * exception if it does not exist.
- * @throw std::out_of_range if given key does not exist
- */
- value& at(const std::string& key);
- const value& at(const std::string& key) const;
-
- /**
- * @brief Accesses a tag by key
- *
- * Returns a value to the tag with the specified key. If it does not exist,
- * creates a new uninitialized entry under the key.
- */
- value& operator[](const std::string& key) { return tags[key]; }
-
- /**
- * @brief Inserts or assigns a tag
- *
- * If the given key already exists, assigns the tag to it.
- * Otherwise, it is inserted under the given key.
- * @return a pair of the iterator to the value and a bool indicating
- * whether the key did not exist
- */
- std::pair<iterator, bool> put(const std::string& key, value_initializer&& val);
-
- /**
- * @brief Inserts a tag if the key does not exist
- * @return a pair of the iterator to the value with the key and a bool
- * indicating whether the value was actually inserted
- */
- std::pair<iterator, bool> insert(const std::string& key, value_initializer&& val);
-
- /**
- * @brief Constructs and assigns or inserts a tag into the compound
- *
- * Constructs a new tag of type @c T with the given args and inserts
- * or assigns it to the given key.
- * @note Unlike std::map::emplace, this will overwrite existing values
- * @return a pair of the iterator to the value and a bool indicating
- * whether the key did not exist
- */
- template<class T, class... Args>
- std::pair<iterator, bool> emplace(const std::string& key, Args&&... args);
-
- /**
- * @brief Erases a tag from the compound
- * @return true if a tag was erased
- */
- bool erase(const std::string& key);
-
- ///Returns true if the given key exists in the compound
- bool has_key(const std::string& key) const;
- ///Returns true if the given key exists and the tag has the given type
- bool has_key(const std::string& key, tag_type type) const;
-
- ///Returns the number of tags in the compound
- size_t size() const { return tags.size(); }
-
- ///Erases all tags from the compound
- void clear() { tags.clear(); }
-
- //Iterators
- iterator begin() { return tags.begin(); }
- iterator end() { return tags.end(); }
- const_iterator begin() const { return tags.begin(); }
- const_iterator end() const { return tags.end(); }
- const_iterator cbegin() const { return tags.cbegin(); }
- const_iterator cend() const { return tags.cend(); }
-
- void read_payload(io::stream_reader& reader) override;
- void write_payload(io::stream_writer& writer) const override;
-
- friend bool operator==(const tag_compound& lhs, const tag_compound& rhs)
- { return lhs.tags == rhs.tags; }
- friend bool operator!=(const tag_compound& lhs, const tag_compound& rhs)
- { return !(lhs == rhs); }
-
-private:
- map_t_ tags;
-};
-
-template<class T, class... Args>
-std::pair<tag_compound::iterator, bool> tag_compound::emplace(const std::string& key, Args&&... args)
-{
- return put(key, value(make_unique<T>(std::forward<Args>(args)...)));
-}
-
-}
+ /// Tag that contains multiple unordered named tags of arbitrary types
+ class NBT_EXPORT tag_compound final : public detail::crtp_tag<tag_compound>
+ {
+ typedef std::map<std::string, value> map_t_;
+
+ public:
+ // Iterator types
+ typedef map_t_::iterator iterator;
+ typedef map_t_::const_iterator const_iterator;
+
+ /// The type of the tag
+ static constexpr tag_type type = tag_type::Compound;
+
+ /// Constructs an empty compound
+ tag_compound() {}
+
+ /// Constructs a compound with the given key-value pairs
+ tag_compound(
+ std::initializer_list<std::pair<std::string, value_initializer>>
+ init);
+
+ /**
+ * @brief Accesses a tag by key with bounds checking
+ *
+ * Returns a value to the tag with the specified key, or throws an
+ * exception if it does not exist.
+ * @throw std::out_of_range if given key does not exist
+ */
+ value& at(const std::string& key);
+ const value& at(const std::string& key) const;
+
+ /**
+ * @brief Accesses a tag by key
+ *
+ * Returns a value to the tag with the specified key. If it does not
+ * exist, creates a new uninitialized entry under the key.
+ */
+ value& operator[](const std::string& key)
+ {
+ return tags[key];
+ }
+
+ /**
+ * @brief Inserts or assigns a tag
+ *
+ * If the given key already exists, assigns the tag to it.
+ * Otherwise, it is inserted under the given key.
+ * @return a pair of the iterator to the value and a bool indicating
+ * whether the key did not exist
+ */
+ std::pair<iterator, bool> put(const std::string& key,
+ value_initializer&& val);
+
+ /**
+ * @brief Inserts a tag if the key does not exist
+ * @return a pair of the iterator to the value with the key and a bool
+ * indicating whether the value was actually inserted
+ */
+ std::pair<iterator, bool> insert(const std::string& key,
+ value_initializer&& val);
+
+ /**
+ * @brief Constructs and assigns or inserts a tag into the compound
+ *
+ * Constructs a new tag of type @c T with the given args and inserts
+ * or assigns it to the given key.
+ * @note Unlike std::map::emplace, this will overwrite existing values
+ * @return a pair of the iterator to the value and a bool indicating
+ * whether the key did not exist
+ */
+ template <class T, class... Args>
+ std::pair<iterator, bool> emplace(const std::string& key,
+ Args&&... args);
+
+ /**
+ * @brief Erases a tag from the compound
+ * @return true if a tag was erased
+ */
+ bool erase(const std::string& key);
+
+ /// Returns true if the given key exists in the compound
+ bool has_key(const std::string& key) const;
+ /// Returns true if the given key exists and the tag has the given type
+ bool has_key(const std::string& key, tag_type type) const;
+
+ /// Returns the number of tags in the compound
+ size_t size() const
+ {
+ return tags.size();
+ }
+
+ /// Erases all tags from the compound
+ void clear()
+ {
+ tags.clear();
+ }
+
+ // Iterators
+ iterator begin()
+ {
+ return tags.begin();
+ }
+ iterator end()
+ {
+ return tags.end();
+ }
+ const_iterator begin() const
+ {
+ return tags.begin();
+ }
+ const_iterator end() const
+ {
+ return tags.end();
+ }
+ const_iterator cbegin() const
+ {
+ return tags.cbegin();
+ }
+ const_iterator cend() const
+ {
+ return tags.cend();
+ }
+
+ void read_payload(io::stream_reader& reader) override;
+ void write_payload(io::stream_writer& writer) const override;
+
+ friend bool operator==(const tag_compound& lhs, const tag_compound& rhs)
+ {
+ return lhs.tags == rhs.tags;
+ }
+ friend bool operator!=(const tag_compound& lhs, const tag_compound& rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+ private:
+ map_t_ tags;
+ };
+
+ template <class T, class... Args>
+ std::pair<tag_compound::iterator, bool>
+ tag_compound::emplace(const std::string& key, Args&&... args)
+ {
+ return put(key, value(make_unique<T>(std::forward<Args>(args)...)));
+ }
+
+} // namespace nbt
#endif // TAG_COMPOUND_H_INCLUDED
diff --git a/include/tag_list.h b/include/tag_list.h
index 556678a59d..eaa41bd65d 100644
--- a/include/tag_list.h
+++ b/include/tag_list.h
@@ -35,195 +35,235 @@
namespace nbt
{
-/**
- * @brief Tag that contains multiple unnamed tags of the same type
- *
- * All the tags contained in the list have the same type, which can be queried
- * with el_type(). The types of the values contained in the list should not
- * be changed to mismatch the element type.
- *
- * If the list is empty, the type can be undetermined, in which case el_type()
- * will return tag_type::Null. The type will then be set when the first tag
- * is added to the list.
- */
-class NBT_EXPORT tag_list final : public detail::crtp_tag<tag_list>
-{
-public:
- //Iterator types
- typedef std::vector<value>::iterator iterator;
- typedef std::vector<value>::const_iterator const_iterator;
-
- ///The type of the tag
- static constexpr tag_type type = tag_type::List;
-
- /**
- * @brief Constructs a list of type T with the given values
- *
- * Example: @code tag_list::of<tag_byte>({3, 4, 5}) @endcode
- * @param init list of values from which the elements are constructed
- */
- template<class T>
- static tag_list of(std::initializer_list<T> init);
-
- /**
- * @brief Constructs an empty list
- *
- * The content type is determined when the first tag is added.
- */
- tag_list(): tag_list(tag_type::Null) {}
-
- ///Constructs an empty list with the given content type
- explicit tag_list(tag_type content_type): el_type_(content_type) {}
-
- ///Constructs a list with the given contents
- tag_list(std::initializer_list<int8_t> init);
- tag_list(std::initializer_list<int16_t> init);
- tag_list(std::initializer_list<int32_t> init);
- tag_list(std::initializer_list<int64_t> init);
- tag_list(std::initializer_list<float> init);
- tag_list(std::initializer_list<double> init);
- tag_list(std::initializer_list<std::string> init);
- tag_list(std::initializer_list<tag_byte_array> init);
- tag_list(std::initializer_list<tag_list> init);
- tag_list(std::initializer_list<tag_compound> init);
- tag_list(std::initializer_list<tag_int_array> init);
- tag_list(std::initializer_list<tag_long_array> init);
-
- /**
- * @brief Constructs a list with the given contents
- * @throw std::invalid_argument if the tags are not all of the same type
- */
- tag_list(std::initializer_list<value> init);
-
- /**
- * @brief Accesses a tag by index with bounds checking
- *
- * Returns a value to the tag at the specified index, or throws an
- * exception if it is out of range.
- * @throw std::out_of_range if the index is out of range
- */
- value& at(size_t i);
- const value& at(size_t i) const;
-
- /**
- * @brief Accesses a tag by index
- *
- * Returns a value to the tag at the specified index. No bounds checking
- * is performed.
- */
- value& operator[](size_t i) { return tags[i]; }
- const value& operator[](size_t i) const { return tags[i]; }
-
- /**
- * @brief Assigns a value at the given index
- * @throw std::invalid_argument if the type of the value does not match the list's
- * content type
- * @throw std::out_of_range if the index is out of range
- */
- void set(size_t i, value&& val);
-
- /**
- * @brief Appends the tag to the end of the list
- * @throw std::invalid_argument if the type of the tag does not match the list's
- * content type
- */
- void push_back(value_initializer&& val);
-
- /**
- * @brief Constructs and appends a tag to the end of the list
- * @throw std::invalid_argument if the type of the tag does not match the list's
- * content type
- */
- template<class T, class... Args>
- void emplace_back(Args&&... args);
-
- ///Removes the last element of the list
- void pop_back() { tags.pop_back(); }
-
- ///Returns the content type of the list, or tag_type::Null if undetermined
- tag_type el_type() const { return el_type_; }
-
- ///Returns the number of tags in the list
- size_t size() const { return tags.size(); }
-
- ///Erases all tags from the list. Preserves the content type.
- void clear() { tags.clear(); }
-
- /**
- * @brief Erases all tags from the list and changes the content type.
- * @param type the new content type. Can be tag_type::Null to leave it undetermined.
- */
- void reset(tag_type type = tag_type::Null);
-
- //Iterators
- iterator begin() { return tags.begin(); }
- iterator end() { return tags.end(); }
- const_iterator begin() const { return tags.begin(); }
- const_iterator end() const { return tags.end(); }
- const_iterator cbegin() const { return tags.cbegin(); }
- const_iterator cend() const { return tags.cend(); }
-
- /**
- * @inheritdoc
- * In case of a list of tag_end, the content type will be undetermined.
- */
- void read_payload(io::stream_reader& reader) override;
- /**
- * @inheritdoc
- * In case of a list of undetermined content type, the written type will be tag_end.
- * @throw std::length_error if the list is too long for NBT
- */
- void write_payload(io::stream_writer& writer) const override;
-
- /**
- * @brief Equality comparison for lists
- *
- * Lists are considered equal if their content types and the contained tags
- * are equal.
- */
- friend NBT_EXPORT bool operator==(const tag_list& lhs, const tag_list& rhs);
- friend NBT_EXPORT bool operator!=(const tag_list& lhs, const tag_list& rhs);
-
-private:
- std::vector<value> tags;
- tag_type el_type_;
-
- /**
- * Internally used initialization function that initializes the list with
- * tags of type T, with the constructor arguments of each T given by il.
- * @param il list of values that are, one by one, given to a constructor of T
- */
- template<class T, class Arg>
- void init(std::initializer_list<Arg> il);
-};
-
-template<class T>
-tag_list tag_list::of(std::initializer_list<T> il)
-{
- tag_list result;
- result.init<T, T>(il);
- return result;
-}
+ /**
+ * @brief Tag that contains multiple unnamed tags of the same type
+ *
+ * All the tags contained in the list have the same type, which can be
+ * queried with el_type(). The types of the values contained in the list
+ * should not be changed to mismatch the element type.
+ *
+ * If the list is empty, the type can be undetermined, in which case
+ * el_type() will return tag_type::Null. The type will then be set when the
+ * first tag is added to the list.
+ */
+ class NBT_EXPORT tag_list final : public detail::crtp_tag<tag_list>
+ {
+ public:
+ // Iterator types
+ typedef std::vector<value>::iterator iterator;
+ typedef std::vector<value>::const_iterator const_iterator;
-template<class T, class... Args>
-void tag_list::emplace_back(Args&&... args)
-{
- if(el_type_ == tag_type::Null) //set content type if undetermined
- el_type_ = T::type;
- else if(el_type_ != T::type)
- throw std::invalid_argument("The tag type does not match the list's content type");
- tags.emplace_back(make_unique<T>(std::forward<Args>(args)...));
-}
-
-template<class T, class Arg>
-void tag_list::init(std::initializer_list<Arg> init)
-{
- el_type_ = T::type;
- tags.reserve(init.size());
- for(const Arg& arg: init)
- tags.emplace_back(nbt::make_unique<T>(arg));
-}
+ /// The type of the tag
+ static constexpr tag_type type = tag_type::List;
+
+ /**
+ * @brief Constructs a list of type T with the given values
+ *
+ * Example: @code tag_list::of<tag_byte>({3, 4, 5}) @endcode
+ * @param init list of values from which the elements are constructed
+ */
+ template <class T> static tag_list of(std::initializer_list<T> init);
+
+ /**
+ * @brief Constructs an empty list
+ *
+ * The content type is determined when the first tag is added.
+ */
+ tag_list() : tag_list(tag_type::Null) {}
+
+ /// Constructs an empty list with the given content type
+ explicit tag_list(tag_type content_type) : el_type_(content_type) {}
+
+ /// Constructs a list with the given contents
+ tag_list(std::initializer_list<int8_t> init);
+ tag_list(std::initializer_list<int16_t> init);
+ tag_list(std::initializer_list<int32_t> init);
+ tag_list(std::initializer_list<int64_t> init);
+ tag_list(std::initializer_list<float> init);
+ tag_list(std::initializer_list<double> init);
+ tag_list(std::initializer_list<std::string> init);
+ tag_list(std::initializer_list<tag_byte_array> init);
+ tag_list(std::initializer_list<tag_list> init);
+ tag_list(std::initializer_list<tag_compound> init);
+ tag_list(std::initializer_list<tag_int_array> init);
+ tag_list(std::initializer_list<tag_long_array> init);
+
+ /**
+ * @brief Constructs a list with the given contents
+ * @throw std::invalid_argument if the tags are not all of the same type
+ */
+ tag_list(std::initializer_list<value> init);
+
+ /**
+ * @brief Accesses a tag by index with bounds checking
+ *
+ * Returns a value to the tag at the specified index, or throws an
+ * exception if it is out of range.
+ * @throw std::out_of_range if the index is out of range
+ */
+ value& at(size_t i);
+ const value& at(size_t i) const;
+
+ /**
+ * @brief Accesses a tag by index
+ *
+ * Returns a value to the tag at the specified index. No bounds checking
+ * is performed.
+ */
+ value& operator[](size_t i)
+ {
+ return tags[i];
+ }
+ const value& operator[](size_t i) const
+ {
+ return tags[i];
+ }
+
+ /**
+ * @brief Assigns a value at the given index
+ * @throw std::invalid_argument if the type of the value does not match
+ * the list's content type
+ * @throw std::out_of_range if the index is out of range
+ */
+ void set(size_t i, value&& val);
+
+ /**
+ * @brief Appends the tag to the end of the list
+ * @throw std::invalid_argument if the type of the tag does not match
+ * the list's content type
+ */
+ void push_back(value_initializer&& val);
+
+ /**
+ * @brief Constructs and appends a tag to the end of the list
+ * @throw std::invalid_argument if the type of the tag does not match
+ * the list's content type
+ */
+ template <class T, class... Args> void emplace_back(Args&&... args);
+
+ /// Removes the last element of the list
+ void pop_back()
+ {
+ tags.pop_back();
+ }
+
+ /// Returns the content type of the list, or tag_type::Null if
+ /// undetermined
+ tag_type el_type() const
+ {
+ return el_type_;
+ }
+
+ /// Returns the number of tags in the list
+ size_t size() const
+ {
+ return tags.size();
+ }
+
+ /// Erases all tags from the list. Preserves the content type.
+ void clear()
+ {
+ tags.clear();
+ }
+
+ /**
+ * @brief Erases all tags from the list and changes the content type.
+ * @param type the new content type. Can be tag_type::Null to leave it
+ * undetermined.
+ */
+ void reset(tag_type type = tag_type::Null);
+
+ // Iterators
+ iterator begin()
+ {
+ return tags.begin();
+ }
+ iterator end()
+ {
+ return tags.end();
+ }
+ const_iterator begin() const
+ {
+ return tags.begin();
+ }
+ const_iterator end() const
+ {
+ return tags.end();
+ }
+ const_iterator cbegin() const
+ {
+ return tags.cbegin();
+ }
+ const_iterator cend() const
+ {
+ return tags.cend();
+ }
+
+ /**
+ * @inheritdoc
+ * In case of a list of tag_end, the content type will be undetermined.
+ */
+ void read_payload(io::stream_reader& reader) override;
+ /**
+ * @inheritdoc
+ * In case of a list of undetermined content type, the written type will
+ * be tag_end.
+ * @throw std::length_error if the list is too long for NBT
+ */
+ void write_payload(io::stream_writer& writer) const override;
+
+ /**
+ * @brief Equality comparison for lists
+ *
+ * Lists are considered equal if their content types and the contained
+ * tags are equal.
+ */
+ friend NBT_EXPORT bool operator==(const tag_list& lhs,
+ const tag_list& rhs);
+ friend NBT_EXPORT bool operator!=(const tag_list& lhs,
+ const tag_list& rhs);
+
+ private:
+ std::vector<value> tags;
+ tag_type el_type_;
+
+ /**
+ * Internally used initialization function that initializes the list
+ * with tags of type T, with the constructor arguments of each T given
+ * by il.
+ * @param il list of values that are, one by one, given to a constructor
+ * of T
+ */
+ template <class T, class Arg> void init(std::initializer_list<Arg> il);
+ };
+
+ template <class T> tag_list tag_list::of(std::initializer_list<T> il)
+ {
+ tag_list result;
+ result.init<T, T>(il);
+ return result;
+ }
+
+ template <class T, class... Args>
+ void tag_list::emplace_back(Args&&... args)
+ {
+ if (el_type_ == tag_type::Null) // set content type if undetermined
+ el_type_ = T::type;
+ else if (el_type_ != T::type)
+ throw std::invalid_argument(
+ "The tag type does not match the list's content type");
+ tags.emplace_back(make_unique<T>(std::forward<Args>(args)...));
+ }
+
+ template <class T, class Arg>
+ void tag_list::init(std::initializer_list<Arg> init)
+ {
+ el_type_ = T::type;
+ tags.reserve(init.size());
+ for (const Arg& arg : init)
+ tags.emplace_back(nbt::make_unique<T>(arg));
+ }
-}
+} // namespace nbt
#endif // TAG_LIST_H_INCLUDED
diff --git a/include/tag_primitive.h b/include/tag_primitive.h
index a2da0cfab9..e1b3fd28ea 100644
--- a/include/tag_primitive.h
+++ b/include/tag_primitive.h
@@ -36,79 +36,101 @@
namespace nbt
{
-/**
- * @brief Tag that contains an integral or floating-point value
- *
- * Common class for tag_byte, tag_short, tag_int, tag_long, tag_float and tag_double.
- */
-template<class T>
-class tag_primitive final : public detail::crtp_tag<tag_primitive<T>>
-{
-public:
- ///The type of the value
- typedef T value_type;
-
- ///The type of the tag
- static constexpr tag_type type = detail::get_primitive_type<T>::value;
-
- //Constructor
- constexpr tag_primitive(T val = 0) noexcept: value(val) {}
-
- //Getters
- operator T&() { return value; }
- constexpr operator T() const { return value; }
- constexpr T get() const { return value; }
-
- //Setters
- tag_primitive& operator=(T val) { value = val; return *this; }
- void set(T val) { value = val; }
-
- void read_payload(io::stream_reader& reader) override;
- void write_payload(io::stream_writer& writer) const override;
-
-private:
- T value;
-};
-
-template<class T> bool operator==(const tag_primitive<T>& lhs, const tag_primitive<T>& rhs)
-{ return lhs.get() == rhs.get(); }
-template<class T> bool operator!=(const tag_primitive<T>& lhs, const tag_primitive<T>& rhs)
-{ return !(lhs == rhs); }
-
-//Typedefs that should be used instead of the template tag_primitive.
-typedef tag_primitive<int8_t> tag_byte;
-typedef tag_primitive<int16_t> tag_short;
-typedef tag_primitive<int32_t> tag_int;
-typedef tag_primitive<int64_t> tag_long;
-typedef tag_primitive<float> tag_float;
-typedef tag_primitive<double> tag_double;
-
-//Explicit instantiation declarations
-extern template class NBT_EXPORT tag_primitive<int8_t>;
-extern template class NBT_EXPORT tag_primitive<int16_t>;
-extern template class NBT_EXPORT tag_primitive<int32_t>;
-extern template class NBT_EXPORT tag_primitive<int64_t>;
-extern template class NBT_EXPORT tag_primitive<float>;
-extern template class NBT_EXPORT tag_primitive<double>;
-
-template<class T>
-void tag_primitive<T>::read_payload(io::stream_reader& reader)
-{
- reader.read_num(value);
- if(!reader.get_istr())
- {
- std::ostringstream str;
- str << "Error reading tag_" << type;
- throw io::input_error(str.str());
- }
-}
-
-template<class T>
-void tag_primitive<T>::write_payload(io::stream_writer& writer) const
-{
- writer.write_num(value);
-}
-
-}
+ /**
+ * @brief Tag that contains an integral or floating-point value
+ *
+ * Common class for tag_byte, tag_short, tag_int, tag_long, tag_float and
+ * tag_double.
+ */
+ template <class T>
+ class tag_primitive final : public detail::crtp_tag<tag_primitive<T>>
+ {
+ public:
+ /// The type of the value
+ typedef T value_type;
+
+ /// The type of the tag
+ static constexpr tag_type type = detail::get_primitive_type<T>::value;
+
+ // Constructor
+ constexpr tag_primitive(T val = 0) noexcept : value(val) {}
+
+ // Getters
+ operator T&()
+ {
+ return value;
+ }
+ constexpr operator T() const
+ {
+ return value;
+ }
+ constexpr T get() const
+ {
+ return value;
+ }
+
+ // Setters
+ tag_primitive& operator=(T val)
+ {
+ value = val;
+ return *this;
+ }
+ void set(T val)
+ {
+ value = val;
+ }
+
+ void read_payload(io::stream_reader& reader) override;
+ void write_payload(io::stream_writer& writer) const override;
+
+ private:
+ T value;
+ };
+
+ template <class T>
+ bool operator==(const tag_primitive<T>& lhs, const tag_primitive<T>& rhs)
+ {
+ return lhs.get() == rhs.get();
+ }
+ template <class T>
+ bool operator!=(const tag_primitive<T>& lhs, const tag_primitive<T>& rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+ // Typedefs that should be used instead of the template tag_primitive.
+ typedef tag_primitive<int8_t> tag_byte;
+ typedef tag_primitive<int16_t> tag_short;
+ typedef tag_primitive<int32_t> tag_int;
+ typedef tag_primitive<int64_t> tag_long;
+ typedef tag_primitive<float> tag_float;
+ typedef tag_primitive<double> tag_double;
+
+ // Explicit instantiation declarations
+ extern template class NBT_EXPORT tag_primitive<int8_t>;
+ extern template class NBT_EXPORT tag_primitive<int16_t>;
+ extern template class NBT_EXPORT tag_primitive<int32_t>;
+ extern template class NBT_EXPORT tag_primitive<int64_t>;
+ extern template class NBT_EXPORT tag_primitive<float>;
+ extern template class NBT_EXPORT tag_primitive<double>;
+
+ template <class T>
+ void tag_primitive<T>::read_payload(io::stream_reader& reader)
+ {
+ reader.read_num(value);
+ if (!reader.get_istr()) {
+ std::ostringstream str;
+ str << "Error reading tag_" << type;
+ throw io::input_error(str.str());
+ }
+ }
+
+ template <class T>
+ void tag_primitive<T>::write_payload(io::stream_writer& writer) const
+ {
+ writer.write_num(value);
+ }
+
+} // namespace nbt
#endif // TAG_PRIMITIVE_H_INCLUDED
diff --git a/include/tag_string.h b/include/tag_string.h
index 35ddd371aa..ec08e3d7d5 100644
--- a/include/tag_string.h
+++ b/include/tag_string.h
@@ -32,47 +32,78 @@
namespace nbt
{
-///Tag that contains a UTF-8 string
-class NBT_EXPORT tag_string final : public detail::crtp_tag<tag_string>
-{
-public:
- ///The type of the tag
- static constexpr tag_type type = tag_type::String;
+ /// Tag that contains a UTF-8 string
+ class NBT_EXPORT tag_string final : public detail::crtp_tag<tag_string>
+ {
+ public:
+ /// The type of the tag
+ static constexpr tag_type type = tag_type::String;
- //Constructors
- tag_string() {}
- tag_string(const std::string& str): value(str) {}
- tag_string(std::string&& str) noexcept: value(std::move(str)) {}
- tag_string(const char* str): value(str) {}
+ // Constructors
+ tag_string() {}
+ tag_string(const std::string& str) : value(str) {}
+ tag_string(std::string&& str) noexcept : value(std::move(str)) {}
+ tag_string(const char* str) : value(str) {}
- //Getters
- operator std::string&() { return value; }
- operator const std::string&() const { return value; }
- const std::string& get() const { return value; }
+ // Getters
+ operator std::string&()
+ {
+ return value;
+ }
+ operator const std::string&() const
+ {
+ return value;
+ }
+ const std::string& get() const
+ {
+ return value;
+ }
- //Setters
- tag_string& operator=(const std::string& str) { value = str; return *this; }
- tag_string& operator=(std::string&& str) { value = std::move(str); return *this; }
- tag_string& operator=(const char* str) { value = str; return *this; }
- void set(const std::string& str) { value = str; }
- void set(std::string&& str) { value = std::move(str); }
+ // Setters
+ tag_string& operator=(const std::string& str)
+ {
+ value = str;
+ return *this;
+ }
+ tag_string& operator=(std::string&& str)
+ {
+ value = std::move(str);
+ return *this;
+ }
+ tag_string& operator=(const char* str)
+ {
+ value = str;
+ return *this;
+ }
+ void set(const std::string& str)
+ {
+ value = str;
+ }
+ void set(std::string&& str)
+ {
+ value = std::move(str);
+ }
- void read_payload(io::stream_reader& reader) override;
- /**
- * @inheritdoc
- * @throw std::length_error if the string is too long for NBT
- */
- void write_payload(io::stream_writer& writer) const override;
+ void read_payload(io::stream_reader& reader) override;
+ /**
+ * @inheritdoc
+ * @throw std::length_error if the string is too long for NBT
+ */
+ void write_payload(io::stream_writer& writer) const override;
-private:
- std::string value;
-};
+ private:
+ std::string value;
+ };
-inline bool operator==(const tag_string& lhs, const tag_string& rhs)
-{ return lhs.get() == rhs.get(); }
-inline bool operator!=(const tag_string& lhs, const tag_string& rhs)
-{ return !(lhs == rhs); }
+ inline bool operator==(const tag_string& lhs, const tag_string& rhs)
+ {
+ return lhs.get() == rhs.get();
+ }
+ inline bool operator!=(const tag_string& lhs, const tag_string& rhs)
+ {
+ return !(lhs == rhs);
+ }
-}
+} // namespace nbt
#endif // TAG_STRING_H_INCLUDED
diff --git a/include/tagfwd.h b/include/tagfwd.h
index a302fe327b..7459e7ba08 100644
--- a/include/tagfwd.h
+++ b/include/tagfwd.h
@@ -33,26 +33,26 @@
namespace nbt
{
-class tag;
+ class tag;
-template<class T> class tag_primitive;
-typedef tag_primitive<int8_t> tag_byte;
-typedef tag_primitive<int16_t> tag_short;
-typedef tag_primitive<int32_t> tag_int;
-typedef tag_primitive<int64_t> tag_long;
-typedef tag_primitive<float> tag_float;
-typedef tag_primitive<double> tag_double;
+ template <class T> class tag_primitive;
+ typedef tag_primitive<int8_t> tag_byte;
+ typedef tag_primitive<int16_t> tag_short;
+ typedef tag_primitive<int32_t> tag_int;
+ typedef tag_primitive<int64_t> tag_long;
+ typedef tag_primitive<float> tag_float;
+ typedef tag_primitive<double> tag_double;
-class tag_string;
+ class tag_string;
-template<class T> class tag_array;
-typedef tag_array<int8_t> tag_byte_array;
-typedef tag_array<int32_t> tag_int_array;
-typedef tag_array<int64_t> tag_long_array;
+ template <class T> class tag_array;
+ typedef tag_array<int8_t> tag_byte_array;
+ typedef tag_array<int32_t> tag_int_array;
+ typedef tag_array<int64_t> tag_long_array;
-class tag_list;
-class tag_compound;
+ class tag_list;
+ class tag_compound;
-}
+} // namespace nbt
#endif // TAGFWD_H_INCLUDED
diff --git a/include/text/json_formatter.h b/include/text/json_formatter.h
index 2f9246939b..923970f446 100644
--- a/include/text/json_formatter.h
+++ b/include/text/json_formatter.h
@@ -32,22 +32,23 @@
namespace nbt
{
-namespace text
-{
+ namespace text
+ {
-/**
- * @brief Prints tags in a JSON-like syntax into a stream
- *
- * @todo Make it configurable and able to produce actual standard-conformant JSON
- */
-class NBT_EXPORT json_formatter
-{
-public:
- json_formatter() {}
- void print(std::ostream& os, const tag& t) const;
-};
+ /**
+ * @brief Prints tags in a JSON-like syntax into a stream
+ *
+ * @todo Make it configurable and able to produce actual
+ * standard-conformant JSON
+ */
+ class NBT_EXPORT json_formatter
+ {
+ public:
+ json_formatter() {}
+ void print(std::ostream& os, const tag& t) const;
+ };
-}
-}
+ } // namespace text
+} // namespace nbt
#endif // JSON_FORMATTER_H_INCLUDED
diff --git a/include/value.h b/include/value.h
index 36b55ffa57..08a23ab111 100644
--- a/include/value.h
+++ b/include/value.h
@@ -33,195 +33,223 @@
namespace nbt
{
-/**
- * @brief Contains an NBT value of fixed type
- *
- * This class is a convenience wrapper for @c std::unique_ptr<tag>.
- * A value can contain any kind of tag or no tag (nullptr) and provides
- * operations for handling tags of which the type is not known at compile time.
- * Assignment or the set method on a value with no tag will fill in the value.
- *
- * The rationale for the existance of this class is to provide a type-erasured
- * means of storing tags, especially when they are contained in tag_compound
- * or tag_list. The alternative would be directly using @c std::unique_ptr<tag>
- * and @c tag&, which is how it was done in libnbt++1. The main drawback is that
- * it becomes very cumbersome to deal with tags of unknown type.
- *
- * For example, in this case it would not be possible to allow a syntax like
- * <tt>compound["foo"] = 42</tt>. If the key "foo" does not exist beforehand,
- * the left hand side could not have any sensible value if it was of type
- * @c tag&.
- * Firstly, the compound tag would have to create a new tag_int there, but it
- * cannot know that the new tag is going to be assigned an integer.
- * Also, if the type was @c tag& and it allowed assignment of integers, that
- * would mean the tag base class has assignments and conversions like this.
- * Which means that all other tag classes would inherit them from the base
- * class, even though it does not make any sense to allow converting a
- * tag_compound into an integer. Attempts like this should be caught at
- * compile time.
- *
- * This is why all the syntactic sugar for tags is contained in the value class
- * while the tag class only contains common operations for all tag types.
- */
-class NBT_EXPORT value
-{
-public:
- //Constructors
- value() noexcept {}
- explicit value(std::unique_ptr<tag>&& t) noexcept: tag_(std::move(t)) {}
- explicit value(tag&& t);
-
- //Moving
- value(value&&) noexcept = default;
- value& operator=(value&&) noexcept = default;
-
- //Copying
- explicit value(const value& rhs);
- value& operator=(const value& rhs);
-
- /**
- * @brief Assigns the given value to the tag if the type matches
- * @throw std::bad_cast if the type of @c t is not the same as the type
- * of this value
- */
- value& operator=(tag&& t);
- void set(tag&& t);
-
- //Conversion to tag
- /**
- * @brief Returns the contained tag
- *
- * If the value is uninitialized, the behavior is undefined.
- */
- operator tag&() { return get(); }
- operator const tag&() const { return get(); }
- tag& get() { return *tag_; }
- const tag& get() const { return *tag_; }
-
- /**
- * @brief Returns a reference to the contained tag as an instance of T
- * @throw std::bad_cast if the tag is not of type T
- */
- template<class T>
- T& as();
- template<class T>
- const T& as() const;
-
- //Assignment of primitives and string
- /**
- * @brief Assigns the given value to the tag if the type is compatible
- * @throw std::bad_cast if the value is not convertible to the tag type
- * via a widening conversion
- */
- value& operator=(int8_t val);
- value& operator=(int16_t val);
- value& operator=(int32_t val);
- value& operator=(int64_t val);
- value& operator=(float val);
- value& operator=(double val);
-
- /**
- * @brief Assigns the given string to the tag if it is a tag_string
- * @throw std::bad_cast if the contained tag is not a tag_string
- */
- value& operator=(const std::string& str);
- value& operator=(std::string&& str);
-
- //Conversions to primitives and string
- /**
- * @brief Returns the contained value if the type is compatible
- * @throw std::bad_cast if the tag type is not convertible to the desired
- * type via a widening conversion
- */
- explicit operator int8_t() const;
- explicit operator int16_t() const;
- explicit operator int32_t() const;
- explicit operator int64_t() const;
- explicit operator float() const;
- explicit operator double() const;
-
- /**
- * @brief Returns the contained string if the type is tag_string
- *
- * If the value is uninitialized, the behavior is undefined.
- * @throw std::bad_cast if the tag type is not tag_string
- */
- explicit operator const std::string&() const;
-
- ///Returns true if the value is not uninitialized
- explicit operator bool() const { return tag_ != nullptr; }
-
- /**
- * @brief In case of a tag_compound, accesses a tag by key with bounds checking
- *
- * If the value is uninitialized, the behavior is undefined.
- * @throw std::bad_cast if the tag type is not tag_compound
- * @throw std::out_of_range if given key does not exist
- * @sa tag_compound::at
- */
- value& at(const std::string& key);
- const value& at(const std::string& key) const;
-
- /**
- * @brief In case of a tag_compound, accesses a tag by key
- *
- * If the value is uninitialized, the behavior is undefined.
- * @throw std::bad_cast if the tag type is not tag_compound
- * @sa tag_compound::operator[]
- */
- value& operator[](const std::string& key);
- value& operator[](const char* key); //need this overload because of conflict with built-in operator[]
-
- /**
- * @brief In case of a tag_list, accesses a tag by index with bounds checking
- *
- * If the value is uninitialized, the behavior is undefined.
- * @throw std::bad_cast if the tag type is not tag_list
- * @throw std::out_of_range if the index is out of range
- * @sa tag_list::at
- */
- value& at(size_t i);
- const value& at(size_t i) const;
-
- /**
- * @brief In case of a tag_list, accesses a tag by index
- *
- * No bounds checking is performed. If the value is uninitialized, the
- * behavior is undefined.
- * @throw std::bad_cast if the tag type is not tag_list
- * @sa tag_list::operator[]
- */
- value& operator[](size_t i);
- const value& operator[](size_t i) const;
-
- ///Returns a reference to the underlying std::unique_ptr<tag>
- std::unique_ptr<tag>& get_ptr() { return tag_; }
- const std::unique_ptr<tag>& get_ptr() const { return tag_; }
- ///Resets the underlying std::unique_ptr<tag> to a different value
- void set_ptr(std::unique_ptr<tag>&& t) { tag_ = std::move(t); }
-
- ///@sa tag::get_type
- tag_type get_type() const;
-
- friend NBT_EXPORT bool operator==(const value& lhs, const value& rhs);
- friend NBT_EXPORT bool operator!=(const value& lhs, const value& rhs);
-
-private:
- std::unique_ptr<tag> tag_;
-};
-
-template<class T>
-T& value::as()
-{
- return tag_->as<T>();
-}
-
-template<class T>
-const T& value::as() const
-{
- return tag_->as<T>();
-}
-
-}
+ /**
+ * @brief Contains an NBT value of fixed type
+ *
+ * This class is a convenience wrapper for @c std::unique_ptr<tag>.
+ * A value can contain any kind of tag or no tag (nullptr) and provides
+ * operations for handling tags of which the type is not known at compile
+ * time. Assignment or the set method on a value with no tag will fill in
+ * the value.
+ *
+ * The rationale for the existance of this class is to provide a
+ * type-erasured means of storing tags, especially when they are contained
+ * in tag_compound or tag_list. The alternative would be directly using @c
+ * std::unique_ptr<tag> and @c tag&, which is how it was done in libnbt++1.
+ * The main drawback is that it becomes very cumbersome to deal with tags of
+ * unknown type.
+ *
+ * For example, in this case it would not be possible to allow a syntax like
+ * <tt>compound["foo"] = 42</tt>. If the key "foo" does not exist
+ * beforehand, the left hand side could not have any sensible value if it
+ * was of type
+ * @c tag&.
+ * Firstly, the compound tag would have to create a new tag_int there, but
+ * it cannot know that the new tag is going to be assigned an integer. Also,
+ * if the type was @c tag& and it allowed assignment of integers, that would
+ * mean the tag base class has assignments and conversions like this. Which
+ * means that all other tag classes would inherit them from the base class,
+ * even though it does not make any sense to allow converting a tag_compound
+ * into an integer. Attempts like this should be caught at compile time.
+ *
+ * This is why all the syntactic sugar for tags is contained in the value
+ * class while the tag class only contains common operations for all tag
+ * types.
+ */
+ class NBT_EXPORT value
+ {
+ public:
+ // Constructors
+ value() noexcept {}
+ explicit value(std::unique_ptr<tag>&& t) noexcept : tag_(std::move(t))
+ {
+ }
+ explicit value(tag&& t);
+
+ // Moving
+ value(value&&) noexcept = default;
+ value& operator=(value&&) noexcept = default;
+
+ // Copying
+ explicit value(const value& rhs);
+ value& operator=(const value& rhs);
+
+ /**
+ * @brief Assigns the given value to the tag if the type matches
+ * @throw std::bad_cast if the type of @c t is not the same as the type
+ * of this value
+ */
+ value& operator=(tag&& t);
+ void set(tag&& t);
+
+ // Conversion to tag
+ /**
+ * @brief Returns the contained tag
+ *
+ * If the value is uninitialized, the behavior is undefined.
+ */
+ operator tag&()
+ {
+ return get();
+ }
+ operator const tag&() const
+ {
+ return get();
+ }
+ tag& get()
+ {
+ return *tag_;
+ }
+ const tag& get() const
+ {
+ return *tag_;
+ }
+
+ /**
+ * @brief Returns a reference to the contained tag as an instance of T
+ * @throw std::bad_cast if the tag is not of type T
+ */
+ template <class T> T& as();
+ template <class T> const T& as() const;
+
+ // Assignment of primitives and string
+ /**
+ * @brief Assigns the given value to the tag if the type is compatible
+ * @throw std::bad_cast if the value is not convertible to the tag type
+ * via a widening conversion
+ */
+ value& operator=(int8_t val);
+ value& operator=(int16_t val);
+ value& operator=(int32_t val);
+ value& operator=(int64_t val);
+ value& operator=(float val);
+ value& operator=(double val);
+
+ /**
+ * @brief Assigns the given string to the tag if it is a tag_string
+ * @throw std::bad_cast if the contained tag is not a tag_string
+ */
+ value& operator=(const std::string& str);
+ value& operator=(std::string&& str);
+
+ // Conversions to primitives and string
+ /**
+ * @brief Returns the contained value if the type is compatible
+ * @throw std::bad_cast if the tag type is not convertible to the
+ * desired type via a widening conversion
+ */
+ explicit operator int8_t() const;
+ explicit operator int16_t() const;
+ explicit operator int32_t() const;
+ explicit operator int64_t() const;
+ explicit operator float() const;
+ explicit operator double() const;
+
+ /**
+ * @brief Returns the contained string if the type is tag_string
+ *
+ * If the value is uninitialized, the behavior is undefined.
+ * @throw std::bad_cast if the tag type is not tag_string
+ */
+ explicit operator const std::string&() const;
+
+ /// Returns true if the value is not uninitialized
+ explicit operator bool() const
+ {
+ return tag_ != nullptr;
+ }
+
+ /**
+ * @brief In case of a tag_compound, accesses a tag by key with bounds
+ * checking
+ *
+ * If the value is uninitialized, the behavior is undefined.
+ * @throw std::bad_cast if the tag type is not tag_compound
+ * @throw std::out_of_range if given key does not exist
+ * @sa tag_compound::at
+ */
+ value& at(const std::string& key);
+ const value& at(const std::string& key) const;
+
+ /**
+ * @brief In case of a tag_compound, accesses a tag by key
+ *
+ * If the value is uninitialized, the behavior is undefined.
+ * @throw std::bad_cast if the tag type is not tag_compound
+ * @sa tag_compound::operator[]
+ */
+ value& operator[](const std::string& key);
+ value& operator[](const char* key); // need this overload because of
+ // conflict with built-in operator[]
+
+ /**
+ * @brief In case of a tag_list, accesses a tag by index with bounds
+ * checking
+ *
+ * If the value is uninitialized, the behavior is undefined.
+ * @throw std::bad_cast if the tag type is not tag_list
+ * @throw std::out_of_range if the index is out of range
+ * @sa tag_list::at
+ */
+ value& at(size_t i);
+ const value& at(size_t i) const;
+
+ /**
+ * @brief In case of a tag_list, accesses a tag by index
+ *
+ * No bounds checking is performed. If the value is uninitialized, the
+ * behavior is undefined.
+ * @throw std::bad_cast if the tag type is not tag_list
+ * @sa tag_list::operator[]
+ */
+ value& operator[](size_t i);
+ const value& operator[](size_t i) const;
+
+ /// Returns a reference to the underlying std::unique_ptr<tag>
+ std::unique_ptr<tag>& get_ptr()
+ {
+ return tag_;
+ }
+ const std::unique_ptr<tag>& get_ptr() const
+ {
+ return tag_;
+ }
+ /// Resets the underlying std::unique_ptr<tag> to a different value
+ void set_ptr(std::unique_ptr<tag>&& t)
+ {
+ tag_ = std::move(t);
+ }
+
+ ///@sa tag::get_type
+ tag_type get_type() const;
+
+ friend NBT_EXPORT bool operator==(const value& lhs, const value& rhs);
+ friend NBT_EXPORT bool operator!=(const value& lhs, const value& rhs);
+
+ private:
+ std::unique_ptr<tag> tag_;
+ };
+
+ template <class T> T& value::as()
+ {
+ return tag_->as<T>();
+ }
+
+ template <class T> const T& value::as() const
+ {
+ return tag_->as<T>();
+ }
+
+} // namespace nbt
#endif // TAG_REF_PROXY_H_INCLUDED
diff --git a/include/value_initializer.h b/include/value_initializer.h
index 7bcc424de2..ad83dc16ee 100644
--- a/include/value_initializer.h
+++ b/include/value_initializer.h
@@ -31,41 +31,45 @@
namespace nbt
{
-/**
- * @brief Helper class for implicitly constructing value objects
- *
- * This type is a subclass of @ref value. However the only difference to value
- * is that this class has additional constructors which allow implicit
- * conversion of various types to value objects. These constructors are not
- * part of the value class itself because implicit conversions like this
- * (especially from @c tag&& to @c value) can cause problems and ambiguities
- * in some cases.
- *
- * value_initializer is especially useful as function parameter type, it will
- * allow convenient conversion of various values to tags on function call.
- *
- * As value_initializer objects are in no way different than value objects,
- * they can just be converted to value after construction.
- */
-class NBT_EXPORT value_initializer : public value
-{
-public:
- value_initializer(std::unique_ptr<tag>&& t) noexcept: value(std::move(t)) {}
- value_initializer(std::nullptr_t) noexcept : value(nullptr) {}
- value_initializer(value&& val) noexcept : value(std::move(val)) {}
- value_initializer(tag&& t) : value(std::move(t)) {}
+ /**
+ * @brief Helper class for implicitly constructing value objects
+ *
+ * This type is a subclass of @ref value. However the only difference to
+ * value is that this class has additional constructors which allow implicit
+ * conversion of various types to value objects. These constructors are not
+ * part of the value class itself because implicit conversions like this
+ * (especially from @c tag&& to @c value) can cause problems and ambiguities
+ * in some cases.
+ *
+ * value_initializer is especially useful as function parameter type, it
+ * will allow convenient conversion of various values to tags on function
+ * call.
+ *
+ * As value_initializer objects are in no way different than value objects,
+ * they can just be converted to value after construction.
+ */
+ class NBT_EXPORT value_initializer : public value
+ {
+ public:
+ value_initializer(std::unique_ptr<tag>&& t) noexcept
+ : value(std::move(t))
+ {
+ }
+ value_initializer(std::nullptr_t) noexcept : value(nullptr) {}
+ value_initializer(value&& val) noexcept : value(std::move(val)) {}
+ value_initializer(tag&& t) : value(std::move(t)) {}
- value_initializer(int8_t val);
- value_initializer(int16_t val);
- value_initializer(int32_t val);
- value_initializer(int64_t val);
- value_initializer(float val);
- value_initializer(double val);
- value_initializer(const std::string& str);
- value_initializer(std::string&& str);
- value_initializer(const char* str);
-};
+ value_initializer(int8_t val);
+ value_initializer(int16_t val);
+ value_initializer(int32_t val);
+ value_initializer(int64_t val);
+ value_initializer(float val);
+ value_initializer(double val);
+ value_initializer(const std::string& str);
+ value_initializer(std::string&& str);
+ value_initializer(const char* str);
+ };
-}
+} // namespace nbt
#endif // VALUE_INITIALIZER_H_INCLUDED
diff --git a/src/endian_str.cpp b/src/endian_str.cpp
index c6de84201d..e9553cd9d3 100644
--- a/src/endian_str.cpp
+++ b/src/endian_str.cpp
@@ -33,256 +33,268 @@ static_assert(sizeof(double) == 8, "Assuming that a double is 8 byte long");
namespace endian
{
-namespace //anonymous
-{
- void pun_int_to_float(float& f, uint32_t i)
- {
- //Yes we need to do it this way to avoid undefined behavior
- memcpy(&f, &i, 4);
- }
-
- uint32_t pun_float_to_int(float f)
- {
- uint32_t ret;
- memcpy(&ret, &f, 4);
- return ret;
- }
-
- void pun_int_to_double(double& d, uint64_t i)
- {
- memcpy(&d, &i, 8);
- }
-
- uint64_t pun_double_to_int(double f)
- {
- uint64_t ret;
- memcpy(&ret, &f, 8);
- return ret;
- }
-}
-
-//------------------------------------------------------------------------------
-
-void read_little(std::istream& is, uint8_t& x)
-{
- is.get(reinterpret_cast<char&>(x));
-}
-
-void read_little(std::istream& is, uint16_t& x)
-{
- uint8_t tmp[2];
- is.read(reinterpret_cast<char*>(tmp), 2);
- x = uint16_t(tmp[0])
- | (uint16_t(tmp[1]) << 8);
-}
-
-void read_little(std::istream& is, uint32_t& x)
-{
- uint8_t tmp[4];
- is.read(reinterpret_cast<char*>(tmp), 4);
- x = uint32_t(tmp[0])
- | (uint32_t(tmp[1]) << 8)
- | (uint32_t(tmp[2]) << 16)
- | (uint32_t(tmp[3]) << 24);
-}
-
-void read_little(std::istream& is, uint64_t& x)
-{
- uint8_t tmp[8];
- is.read(reinterpret_cast<char*>(tmp), 8);
- x = uint64_t(tmp[0])
- | (uint64_t(tmp[1]) << 8)
- | (uint64_t(tmp[2]) << 16)
- | (uint64_t(tmp[3]) << 24)
- | (uint64_t(tmp[4]) << 32)
- | (uint64_t(tmp[5]) << 40)
- | (uint64_t(tmp[6]) << 48)
- | (uint64_t(tmp[7]) << 56);
-}
-
-void read_little(std::istream& is, int8_t & x) { read_little(is, reinterpret_cast<uint8_t &>(x)); }
-void read_little(std::istream& is, int16_t& x) { read_little(is, reinterpret_cast<uint16_t&>(x)); }
-void read_little(std::istream& is, int32_t& x) { read_little(is, reinterpret_cast<uint32_t&>(x)); }
-void read_little(std::istream& is, int64_t& x) { read_little(is, reinterpret_cast<uint64_t&>(x)); }
-
-void read_little(std::istream& is, float& x)
-{
- uint32_t tmp;
- read_little(is, tmp);
- pun_int_to_float(x, tmp);
-}
-
-void read_little(std::istream& is, double& x)
-{
- uint64_t tmp;
- read_little(is, tmp);
- pun_int_to_double(x, tmp);
-}
-
-//------------------------------------------------------------------------------
-
-void read_big(std::istream& is, uint8_t& x)
-{
- is.read(reinterpret_cast<char*>(&x), 1);
-}
-
-void read_big(std::istream& is, uint16_t& x)
-{
- uint8_t tmp[2];
- is.read(reinterpret_cast<char*>(tmp), 2);
- x = uint16_t(tmp[1])
- | (uint16_t(tmp[0]) << 8);
-}
-
-void read_big(std::istream& is, uint32_t& x)
-{
- uint8_t tmp[4];
- is.read(reinterpret_cast<char*>(tmp), 4);
- x = uint32_t(tmp[3])
- | (uint32_t(tmp[2]) << 8)
- | (uint32_t(tmp[1]) << 16)
- | (uint32_t(tmp[0]) << 24);
-}
-
-void read_big(std::istream& is, uint64_t& x)
-{
- uint8_t tmp[8];
- is.read(reinterpret_cast<char*>(tmp), 8);
- x = uint64_t(tmp[7])
- | (uint64_t(tmp[6]) << 8)
- | (uint64_t(tmp[5]) << 16)
- | (uint64_t(tmp[4]) << 24)
- | (uint64_t(tmp[3]) << 32)
- | (uint64_t(tmp[2]) << 40)
- | (uint64_t(tmp[1]) << 48)
- | (uint64_t(tmp[0]) << 56);
-}
-
-void read_big(std::istream& is, int8_t & x) { read_big(is, reinterpret_cast<uint8_t &>(x)); }
-void read_big(std::istream& is, int16_t& x) { read_big(is, reinterpret_cast<uint16_t&>(x)); }
-void read_big(std::istream& is, int32_t& x) { read_big(is, reinterpret_cast<uint32_t&>(x)); }
-void read_big(std::istream& is, int64_t& x) { read_big(is, reinterpret_cast<uint64_t&>(x)); }
-
-void read_big(std::istream& is, float& x)
-{
- uint32_t tmp;
- read_big(is, tmp);
- pun_int_to_float(x, tmp);
-}
-
-void read_big(std::istream& is, double& x)
-{
- uint64_t tmp;
- read_big(is, tmp);
- pun_int_to_double(x, tmp);
-}
-
-//------------------------------------------------------------------------------
-
-void write_little(std::ostream& os, uint8_t x)
-{
- os.put(x);
-}
-
-void write_little(std::ostream& os, uint16_t x)
-{
- uint8_t tmp[2] {
- uint8_t(x),
- uint8_t(x >> 8)};
- os.write(reinterpret_cast<const char*>(tmp), 2);
-}
-
-void write_little(std::ostream& os, uint32_t x)
-{
- uint8_t tmp[4] {
- uint8_t(x),
- uint8_t(x >> 8),
- uint8_t(x >> 16),
- uint8_t(x >> 24)};
- os.write(reinterpret_cast<const char*>(tmp), 4);
-}
-
-void write_little(std::ostream& os, uint64_t x)
-{
- uint8_t tmp[8] {
- uint8_t(x),
- uint8_t(x >> 8),
- uint8_t(x >> 16),
- uint8_t(x >> 24),
- uint8_t(x >> 32),
- uint8_t(x >> 40),
- uint8_t(x >> 48),
- uint8_t(x >> 56)};
- os.write(reinterpret_cast<const char*>(tmp), 8);
-}
-
-void write_little(std::ostream& os, int8_t x) { write_little(os, static_cast<uint8_t >(x)); }
-void write_little(std::ostream& os, int16_t x) { write_little(os, static_cast<uint16_t>(x)); }
-void write_little(std::ostream& os, int32_t x) { write_little(os, static_cast<uint32_t>(x)); }
-void write_little(std::ostream& os, int64_t x) { write_little(os, static_cast<uint64_t>(x)); }
-
-void write_little(std::ostream& os, float x)
-{
- write_little(os, pun_float_to_int(x));
-}
-
-void write_little(std::ostream& os, double x)
-{
- write_little(os, pun_double_to_int(x));
-}
-
-//------------------------------------------------------------------------------
-
-void write_big(std::ostream& os, uint8_t x)
-{
- os.put(x);
-}
-
-void write_big(std::ostream& os, uint16_t x)
-{
- uint8_t tmp[2] {
- uint8_t(x >> 8),
- uint8_t(x)};
- os.write(reinterpret_cast<const char*>(tmp), 2);
-}
-
-void write_big(std::ostream& os, uint32_t x)
-{
- uint8_t tmp[4] {
- uint8_t(x >> 24),
- uint8_t(x >> 16),
- uint8_t(x >> 8),
- uint8_t(x)};
- os.write(reinterpret_cast<const char*>(tmp), 4);
-}
-
-void write_big(std::ostream& os, uint64_t x)
-{
- uint8_t tmp[8] {
- uint8_t(x >> 56),
- uint8_t(x >> 48),
- uint8_t(x >> 40),
- uint8_t(x >> 32),
- uint8_t(x >> 24),
- uint8_t(x >> 16),
- uint8_t(x >> 8),
- uint8_t(x)};
- os.write(reinterpret_cast<const char*>(tmp), 8);
-}
-
-void write_big(std::ostream& os, int8_t x) { write_big(os, static_cast<uint8_t >(x)); }
-void write_big(std::ostream& os, int16_t x) { write_big(os, static_cast<uint16_t>(x)); }
-void write_big(std::ostream& os, int32_t x) { write_big(os, static_cast<uint32_t>(x)); }
-void write_big(std::ostream& os, int64_t x) { write_big(os, static_cast<uint64_t>(x)); }
-
-void write_big(std::ostream& os, float x)
-{
- write_big(os, pun_float_to_int(x));
-}
-
-void write_big(std::ostream& os, double x)
-{
- write_big(os, pun_double_to_int(x));
-}
-
-}
+ namespace // anonymous
+ {
+ void pun_int_to_float(float& f, uint32_t i)
+ {
+ // Yes we need to do it this way to avoid undefined behavior
+ memcpy(&f, &i, 4);
+ }
+
+ uint32_t pun_float_to_int(float f)
+ {
+ uint32_t ret;
+ memcpy(&ret, &f, 4);
+ return ret;
+ }
+
+ void pun_int_to_double(double& d, uint64_t i)
+ {
+ memcpy(&d, &i, 8);
+ }
+
+ uint64_t pun_double_to_int(double f)
+ {
+ uint64_t ret;
+ memcpy(&ret, &f, 8);
+ return ret;
+ }
+ } // namespace
+
+ //------------------------------------------------------------------------------
+
+ void read_little(std::istream& is, uint8_t& x)
+ {
+ is.get(reinterpret_cast<char&>(x));
+ }
+
+ void read_little(std::istream& is, uint16_t& x)
+ {
+ uint8_t tmp[2];
+ is.read(reinterpret_cast<char*>(tmp), 2);
+ x = uint16_t(tmp[0]) | (uint16_t(tmp[1]) << 8);
+ }
+
+ void read_little(std::istream& is, uint32_t& x)
+ {
+ uint8_t tmp[4];
+ is.read(reinterpret_cast<char*>(tmp), 4);
+ x = uint32_t(tmp[0]) | (uint32_t(tmp[1]) << 8) |
+ (uint32_t(tmp[2]) << 16) | (uint32_t(tmp[3]) << 24);
+ }
+
+ void read_little(std::istream& is, uint64_t& x)
+ {
+ uint8_t tmp[8];
+ is.read(reinterpret_cast<char*>(tmp), 8);
+ x = uint64_t(tmp[0]) | (uint64_t(tmp[1]) << 8) |
+ (uint64_t(tmp[2]) << 16) | (uint64_t(tmp[3]) << 24) |
+ (uint64_t(tmp[4]) << 32) | (uint64_t(tmp[5]) << 40) |
+ (uint64_t(tmp[6]) << 48) | (uint64_t(tmp[7]) << 56);
+ }
+
+ void read_little(std::istream& is, int8_t& x)
+ {
+ read_little(is, reinterpret_cast<uint8_t&>(x));
+ }
+ void read_little(std::istream& is, int16_t& x)
+ {
+ read_little(is, reinterpret_cast<uint16_t&>(x));
+ }
+ void read_little(std::istream& is, int32_t& x)
+ {
+ read_little(is, reinterpret_cast<uint32_t&>(x));
+ }
+ void read_little(std::istream& is, int64_t& x)
+ {
+ read_little(is, reinterpret_cast<uint64_t&>(x));
+ }
+
+ void read_little(std::istream& is, float& x)
+ {
+ uint32_t tmp;
+ read_little(is, tmp);
+ pun_int_to_float(x, tmp);
+ }
+
+ void read_little(std::istream& is, double& x)
+ {
+ uint64_t tmp;
+ read_little(is, tmp);
+ pun_int_to_double(x, tmp);
+ }
+
+ //------------------------------------------------------------------------------
+
+ void read_big(std::istream& is, uint8_t& x)
+ {
+ is.read(reinterpret_cast<char*>(&x), 1);
+ }
+
+ void read_big(std::istream& is, uint16_t& x)
+ {
+ uint8_t tmp[2];
+ is.read(reinterpret_cast<char*>(tmp), 2);
+ x = uint16_t(tmp[1]) | (uint16_t(tmp[0]) << 8);
+ }
+
+ void read_big(std::istream& is, uint32_t& x)
+ {
+ uint8_t tmp[4];
+ is.read(reinterpret_cast<char*>(tmp), 4);
+ x = uint32_t(tmp[3]) | (uint32_t(tmp[2]) << 8) |
+ (uint32_t(tmp[1]) << 16) | (uint32_t(tmp[0]) << 24);
+ }
+
+ void read_big(std::istream& is, uint64_t& x)
+ {
+ uint8_t tmp[8];
+ is.read(reinterpret_cast<char*>(tmp), 8);
+ x = uint64_t(tmp[7]) | (uint64_t(tmp[6]) << 8) |
+ (uint64_t(tmp[5]) << 16) | (uint64_t(tmp[4]) << 24) |
+ (uint64_t(tmp[3]) << 32) | (uint64_t(tmp[2]) << 40) |
+ (uint64_t(tmp[1]) << 48) | (uint64_t(tmp[0]) << 56);
+ }
+
+ void read_big(std::istream& is, int8_t& x)
+ {
+ read_big(is, reinterpret_cast<uint8_t&>(x));
+ }
+ void read_big(std::istream& is, int16_t& x)
+ {
+ read_big(is, reinterpret_cast<uint16_t&>(x));
+ }
+ void read_big(std::istream& is, int32_t& x)
+ {
+ read_big(is, reinterpret_cast<uint32_t&>(x));
+ }
+ void read_big(std::istream& is, int64_t& x)
+ {
+ read_big(is, reinterpret_cast<uint64_t&>(x));
+ }
+
+ void read_big(std::istream& is, float& x)
+ {
+ uint32_t tmp;
+ read_big(is, tmp);
+ pun_int_to_float(x, tmp);
+ }
+
+ void read_big(std::istream& is, double& x)
+ {
+ uint64_t tmp;
+ read_big(is, tmp);
+ pun_int_to_double(x, tmp);
+ }
+
+ //------------------------------------------------------------------------------
+
+ void write_little(std::ostream& os, uint8_t x)
+ {
+ os.put(x);
+ }
+
+ void write_little(std::ostream& os, uint16_t x)
+ {
+ uint8_t tmp[2]{uint8_t(x), uint8_t(x >> 8)};
+ os.write(reinterpret_cast<const char*>(tmp), 2);
+ }
+
+ void write_little(std::ostream& os, uint32_t x)
+ {
+ uint8_t tmp[4]{uint8_t(x), uint8_t(x >> 8), uint8_t(x >> 16),
+ uint8_t(x >> 24)};
+ os.write(reinterpret_cast<const char*>(tmp), 4);
+ }
+
+ void write_little(std::ostream& os, uint64_t x)
+ {
+ uint8_t tmp[8]{uint8_t(x), uint8_t(x >> 8), uint8_t(x >> 16),
+ uint8_t(x >> 24), uint8_t(x >> 32), uint8_t(x >> 40),
+ uint8_t(x >> 48), uint8_t(x >> 56)};
+ os.write(reinterpret_cast<const char*>(tmp), 8);
+ }
+
+ void write_little(std::ostream& os, int8_t x)
+ {
+ write_little(os, static_cast<uint8_t>(x));
+ }
+ void write_little(std::ostream& os, int16_t x)
+ {
+ write_little(os, static_cast<uint16_t>(x));
+ }
+ void write_little(std::ostream& os, int32_t x)
+ {
+ write_little(os, static_cast<uint32_t>(x));
+ }
+ void write_little(std::ostream& os, int64_t x)
+ {
+ write_little(os, static_cast<uint64_t>(x));
+ }
+
+ void write_little(std::ostream& os, float x)
+ {
+ write_little(os, pun_float_to_int(x));
+ }
+
+ void write_little(std::ostream& os, double x)
+ {
+ write_little(os, pun_double_to_int(x));
+ }
+
+ //------------------------------------------------------------------------------
+
+ void write_big(std::ostream& os, uint8_t x)
+ {
+ os.put(x);
+ }
+
+ void write_big(std::ostream& os, uint16_t x)
+ {
+ uint8_t tmp[2]{uint8_t(x >> 8), uint8_t(x)};
+ os.write(reinterpret_cast<const char*>(tmp), 2);
+ }
+
+ void write_big(std::ostream& os, uint32_t x)
+ {
+ uint8_t tmp[4]{uint8_t(x >> 24), uint8_t(x >> 16), uint8_t(x >> 8),
+ uint8_t(x)};
+ os.write(reinterpret_cast<const char*>(tmp), 4);
+ }
+
+ void write_big(std::ostream& os, uint64_t x)
+ {
+ uint8_t tmp[8]{uint8_t(x >> 56), uint8_t(x >> 48), uint8_t(x >> 40),
+ uint8_t(x >> 32), uint8_t(x >> 24), uint8_t(x >> 16),
+ uint8_t(x >> 8), uint8_t(x)};
+ os.write(reinterpret_cast<const char*>(tmp), 8);
+ }
+
+ void write_big(std::ostream& os, int8_t x)
+ {
+ write_big(os, static_cast<uint8_t>(x));
+ }
+ void write_big(std::ostream& os, int16_t x)
+ {
+ write_big(os, static_cast<uint16_t>(x));
+ }
+ void write_big(std::ostream& os, int32_t x)
+ {
+ write_big(os, static_cast<uint32_t>(x));
+ }
+ void write_big(std::ostream& os, int64_t x)
+ {
+ write_big(os, static_cast<uint64_t>(x));
+ }
+
+ void write_big(std::ostream& os, float x)
+ {
+ write_big(os, pun_float_to_int(x));
+ }
+
+ void write_big(std::ostream& os, double x)
+ {
+ write_big(os, pun_double_to_int(x));
+ }
+
+} // namespace endian
diff --git a/src/io/izlibstream.cpp b/src/io/izlibstream.cpp
index b361971081..9d57545360 100644
--- a/src/io/izlibstream.cpp
+++ b/src/io/izlibstream.cpp
@@ -27,76 +27,75 @@
namespace zlib
{
-inflate_streambuf::inflate_streambuf(std::istream& input, size_t bufsize, int window_bits):
- zlib_streambuf(bufsize), is(input), stream_end(false)
-{
- zstr.next_in = Z_NULL;
- zstr.avail_in = 0;
- int ret = inflateInit2(&zstr, window_bits);
- if(ret != Z_OK)
- throw zlib_error(zstr.msg, ret);
+ inflate_streambuf::inflate_streambuf(std::istream& input, size_t bufsize,
+ int window_bits)
+ : zlib_streambuf(bufsize), is(input), stream_end(false)
+ {
+ zstr.next_in = Z_NULL;
+ zstr.avail_in = 0;
+ int ret = inflateInit2(&zstr, window_bits);
+ if (ret != Z_OK)
+ throw zlib_error(zstr.msg, ret);
- char* end = out.data() + out.size();
- setg(end, end, end);
-}
+ char* end = out.data() + out.size();
+ setg(end, end, end);
+ }
-inflate_streambuf::~inflate_streambuf() noexcept
-{
- inflateEnd(&zstr);
-}
+ inflate_streambuf::~inflate_streambuf() noexcept
+ {
+ inflateEnd(&zstr);
+ }
-inflate_streambuf::int_type inflate_streambuf::underflow()
-{
- if(gptr() < egptr())
- return traits_type::to_int_type(*gptr());
+ inflate_streambuf::int_type inflate_streambuf::underflow()
+ {
+ if (gptr() < egptr())
+ return traits_type::to_int_type(*gptr());
- size_t have;
- do
- {
- //Read if input buffer is empty
- if(zstr.avail_in <= 0)
- {
- is.read(in.data(), in.size());
- if(is.bad())
- throw std::ios_base::failure("Input stream is bad");
- size_t count = is.gcount();
- if(count == 0 && !stream_end)
- throw zlib_error("Unexpected end of stream", Z_DATA_ERROR);
+ size_t have;
+ do {
+ // Read if input buffer is empty
+ if (zstr.avail_in <= 0) {
+ is.read(in.data(), in.size());
+ if (is.bad())
+ throw std::ios_base::failure("Input stream is bad");
+ size_t count = is.gcount();
+ if (count == 0 && !stream_end)
+ throw zlib_error("Unexpected end of stream", Z_DATA_ERROR);
- zstr.next_in = reinterpret_cast<Bytef*>(in.data());
- zstr.avail_in = count;
- }
+ zstr.next_in = reinterpret_cast<Bytef*>(in.data());
+ zstr.avail_in = count;
+ }
- zstr.next_out = reinterpret_cast<Bytef*>(out.data());
- zstr.avail_out = out.size();
+ zstr.next_out = reinterpret_cast<Bytef*>(out.data());
+ zstr.avail_out = out.size();
- int ret = inflate(&zstr, Z_NO_FLUSH);
- have = out.size() - zstr.avail_out;
- switch(ret)
- {
- case Z_NEED_DICT:
- case Z_DATA_ERROR:
- throw zlib_error(zstr.msg, ret);
+ int ret = inflate(&zstr, Z_NO_FLUSH);
+ have = out.size() - zstr.avail_out;
+ switch (ret) {
+ case Z_NEED_DICT:
+ case Z_DATA_ERROR:
+ throw zlib_error(zstr.msg, ret);
- case Z_MEM_ERROR:
- throw std::bad_alloc();
+ case Z_MEM_ERROR:
+ throw std::bad_alloc();
- case Z_STREAM_END:
- if(!stream_end)
- {
- stream_end = true;
- //In case we consumed too much, we have to rewind the input stream
- is.clear();
- is.seekg(-static_cast<std::streamoff>(zstr.avail_in), std::ios_base::cur);
- }
- if(have == 0)
- return traits_type::eof();
- break;
- }
- } while(have == 0);
+ case Z_STREAM_END:
+ if (!stream_end) {
+ stream_end = true;
+ // In case we consumed too much, we have to rewind the
+ // input stream
+ is.clear();
+ is.seekg(-static_cast<std::streamoff>(zstr.avail_in),
+ std::ios_base::cur);
+ }
+ if (have == 0)
+ return traits_type::eof();
+ break;
+ }
+ } while (have == 0);
- setg(out.data(), out.data(), out.data() + have);
- return traits_type::to_int_type(*gptr());
-}
+ setg(out.data(), out.data(), out.data() + have);
+ return traits_type::to_int_type(*gptr());
+ }
-}
+} // namespace zlib
diff --git a/src/io/ozlibstream.cpp b/src/io/ozlibstream.cpp
index eefe38a91c..c8a3e862a1 100644
--- a/src/io/ozlibstream.cpp
+++ b/src/io/ozlibstream.cpp
@@ -27,93 +27,88 @@
namespace zlib
{
-deflate_streambuf::deflate_streambuf(std::ostream& output, size_t bufsize, int level, int window_bits, int mem_level, int strategy):
- zlib_streambuf(bufsize), os(output)
-{
- int ret = deflateInit2(&zstr, level, Z_DEFLATED, window_bits, mem_level, strategy);
- if(ret != Z_OK)
- throw zlib_error(zstr.msg, ret);
+ deflate_streambuf::deflate_streambuf(std::ostream& output, size_t bufsize,
+ int level, int window_bits,
+ int mem_level, int strategy)
+ : zlib_streambuf(bufsize), os(output)
+ {
+ int ret = deflateInit2(&zstr, level, Z_DEFLATED, window_bits, mem_level,
+ strategy);
+ if (ret != Z_OK)
+ throw zlib_error(zstr.msg, ret);
- setp(in.data(), in.data() + in.size());
-}
+ setp(in.data(), in.data() + in.size());
+ }
-deflate_streambuf::~deflate_streambuf() noexcept
-{
- try
- {
- close();
- }
- catch(...)
- {
- //ignore as we can't do anything about it
- }
- deflateEnd(&zstr);
-}
+ deflate_streambuf::~deflate_streambuf() noexcept
+ {
+ try {
+ close();
+ } catch (...) {
+ // ignore as we can't do anything about it
+ }
+ deflateEnd(&zstr);
+ }
-void deflate_streambuf::close()
-{
- deflate_chunk(Z_FINISH);
-}
+ void deflate_streambuf::close()
+ {
+ deflate_chunk(Z_FINISH);
+ }
-void deflate_streambuf::deflate_chunk(int flush)
-{
- zstr.next_in = reinterpret_cast<Bytef*>(pbase());
- zstr.avail_in = pptr() - pbase();
- do
- {
- zstr.next_out = reinterpret_cast<Bytef*>(out.data());
- zstr.avail_out = out.size();
- int ret = deflate(&zstr, flush);
- if(ret != Z_OK && ret != Z_STREAM_END)
- {
- os.setstate(std::ios_base::failbit);
- throw zlib_error(zstr.msg, ret);
- }
- int have = out.size() - zstr.avail_out;
- if(!os.write(out.data(), have))
- throw std::ios_base::failure("Could not write to the output stream");
- } while(zstr.avail_out == 0);
- setp(in.data(), in.data() + in.size());
-}
+ void deflate_streambuf::deflate_chunk(int flush)
+ {
+ zstr.next_in = reinterpret_cast<Bytef*>(pbase());
+ zstr.avail_in = pptr() - pbase();
+ do {
+ zstr.next_out = reinterpret_cast<Bytef*>(out.data());
+ zstr.avail_out = out.size();
+ int ret = deflate(&zstr, flush);
+ if (ret != Z_OK && ret != Z_STREAM_END) {
+ os.setstate(std::ios_base::failbit);
+ throw zlib_error(zstr.msg, ret);
+ }
+ int have = out.size() - zstr.avail_out;
+ if (!os.write(out.data(), have))
+ throw std::ios_base::failure(
+ "Could not write to the output stream");
+ } while (zstr.avail_out == 0);
+ setp(in.data(), in.data() + in.size());
+ }
-deflate_streambuf::int_type deflate_streambuf::overflow(int_type ch)
-{
- deflate_chunk();
- if(ch != traits_type::eof())
- {
- *pptr() = ch;
- pbump(1);
- }
- return ch;
-}
+ deflate_streambuf::int_type deflate_streambuf::overflow(int_type ch)
+ {
+ deflate_chunk();
+ if (ch != traits_type::eof()) {
+ *pptr() = ch;
+ pbump(1);
+ }
+ return ch;
+ }
-int deflate_streambuf::sync()
-{
- deflate_chunk();
- return 0;
-}
+ int deflate_streambuf::sync()
+ {
+ deflate_chunk();
+ return 0;
+ }
-void ozlibstream::close()
-{
- // Capture the exception mask so we can restore it if close() fails.
- std::ios_base::iostate old_ex = exceptions();
- try
- {
- buf.close();
- return;
- }
- catch(...)
- {
- // fall through to mark the stream as bad
- }
+ void ozlibstream::close()
+ {
+ // Capture the exception mask so we can restore it if close() fails.
+ std::ios_base::iostate old_ex = exceptions();
+ try {
+ buf.close();
+ return;
+ } catch (...) {
+ // fall through to mark the stream as bad
+ }
- // Setting the stream state while exceptions are enabled may cause
- // `setstate` to throw an `ios_base::failure` which would replace
- // the original exception. Temporarily disable exceptions on this
- // stream, set the `badbit`, then restore the exception mask.
- exceptions(std::ios_base::goodbit);
- setstate(std::ios_base::badbit);
- exceptions(old_ex);
-}
+ // Setting the stream state while exceptions are enabled may cause
+ // `setstate` to throw an `ios_base::failure` which would replace
+ // the original exception. Temporarily disable exceptions on this
+ // stream, set the `badbit`, then restore the exception mask.
+ exceptions(std::ios_base::goodbit);
+ setstate(std::ios_base::badbit);
+ exceptions(old_ex);
+ }
} // namespace zlib
diff --git a/src/io/stream_reader.cpp b/src/io/stream_reader.cpp
index 06375ca0d5..43032b1120 100644
--- a/src/io/stream_reader.cpp
+++ b/src/io/stream_reader.cpp
@@ -28,92 +28,95 @@
namespace nbt
{
-namespace io
-{
+ namespace io
+ {
-static constexpr int MAX_DEPTH = 1024;
+ static constexpr int MAX_DEPTH = 1024;
-std::pair<std::string, std::unique_ptr<tag_compound>> read_compound(std::istream& is, endian::endian e)
-{
- return stream_reader(is, e).read_compound();
-}
+ std::pair<std::string, std::unique_ptr<tag_compound>>
+ read_compound(std::istream& is, endian::endian e)
+ {
+ return stream_reader(is, e).read_compound();
+ }
-std::pair<std::string, std::unique_ptr<tag>> read_tag(std::istream& is, endian::endian e)
-{
- return stream_reader(is, e).read_tag();
-}
+ std::pair<std::string, std::unique_ptr<tag>> read_tag(std::istream& is,
+ endian::endian e)
+ {
+ return stream_reader(is, e).read_tag();
+ }
-stream_reader::stream_reader(std::istream& is, endian::endian e) noexcept:
- is(is), endian(e)
-{}
+ stream_reader::stream_reader(std::istream& is,
+ endian::endian e) noexcept
+ : is(is), endian(e)
+ {
+ }
-std::istream& stream_reader::get_istr() const
-{
- return is;
-}
+ std::istream& stream_reader::get_istr() const
+ {
+ return is;
+ }
-endian::endian stream_reader::get_endian() const
-{
- return endian;
-}
+ endian::endian stream_reader::get_endian() const
+ {
+ return endian;
+ }
-std::pair<std::string, std::unique_ptr<tag_compound>> stream_reader::read_compound()
-{
- if(read_type() != tag_type::Compound)
- {
- is.setstate(std::ios::failbit);
- throw input_error("Tag is not a compound");
- }
- std::string key = read_string();
- auto comp = make_unique<tag_compound>();
- comp->read_payload(*this);
- return {std::move(key), std::move(comp)};
-}
+ std::pair<std::string, std::unique_ptr<tag_compound>>
+ stream_reader::read_compound()
+ {
+ if (read_type() != tag_type::Compound) {
+ is.setstate(std::ios::failbit);
+ throw input_error("Tag is not a compound");
+ }
+ std::string key = read_string();
+ auto comp = make_unique<tag_compound>();
+ comp->read_payload(*this);
+ return {std::move(key), std::move(comp)};
+ }
-std::pair<std::string, std::unique_ptr<tag>> stream_reader::read_tag()
-{
- tag_type type = read_type();
- std::string key = read_string();
- std::unique_ptr<tag> t = read_payload(type);
- return {std::move(key), std::move(t)};
-}
+ std::pair<std::string, std::unique_ptr<tag>> stream_reader::read_tag()
+ {
+ tag_type type = read_type();
+ std::string key = read_string();
+ std::unique_ptr<tag> t = read_payload(type);
+ return {std::move(key), std::move(t)};
+ }
-std::unique_ptr<tag> stream_reader::read_payload(tag_type type)
-{
- if (++depth > MAX_DEPTH)
- throw input_error("Too deeply nested");
- std::unique_ptr<tag> t = tag::create(type);
- t->read_payload(*this);
- --depth;
- return t;
-}
+ std::unique_ptr<tag> stream_reader::read_payload(tag_type type)
+ {
+ if (++depth > MAX_DEPTH)
+ throw input_error("Too deeply nested");
+ std::unique_ptr<tag> t = tag::create(type);
+ t->read_payload(*this);
+ --depth;
+ return t;
+ }
-tag_type stream_reader::read_type(bool allow_end)
-{
- int type = is.get();
- if(!is)
- throw input_error("Error reading tag type");
- if(!is_valid_type(type, allow_end))
- {
- is.setstate(std::ios::failbit);
- throw input_error("Invalid tag type: " + std::to_string(type));
- }
- return static_cast<tag_type>(type);
-}
+ tag_type stream_reader::read_type(bool allow_end)
+ {
+ int type = is.get();
+ if (!is)
+ throw input_error("Error reading tag type");
+ if (!is_valid_type(type, allow_end)) {
+ is.setstate(std::ios::failbit);
+ throw input_error("Invalid tag type: " + std::to_string(type));
+ }
+ return static_cast<tag_type>(type);
+ }
-std::string stream_reader::read_string()
-{
- uint16_t len;
- read_num(len);
- if(!is)
- throw input_error("Error reading string");
+ std::string stream_reader::read_string()
+ {
+ uint16_t len;
+ read_num(len);
+ if (!is)
+ throw input_error("Error reading string");
- std::string ret(len, '\0');
- is.read(&ret[0], len); //C++11 allows us to do this
- if(!is)
- throw input_error("Error reading string");
- return ret;
-}
+ std::string ret(len, '\0');
+ is.read(&ret[0], len); // C++11 allows us to do this
+ if (!is)
+ throw input_error("Error reading string");
+ return ret;
+ }
-}
-}
+ } // namespace io
+} // namespace nbt
diff --git a/src/io/stream_writer.cpp b/src/io/stream_writer.cpp
index 0294a6d4e9..5275c5a1b4 100644
--- a/src/io/stream_writer.cpp
+++ b/src/io/stream_writer.cpp
@@ -26,33 +26,34 @@
namespace nbt
{
-namespace io
-{
+ namespace io
+ {
-void write_tag(const std::string& key, const tag& t, std::ostream& os, endian::endian e)
-{
- stream_writer(os, e).write_tag(key, t);
-}
+ void write_tag(const std::string& key, const tag& t, std::ostream& os,
+ endian::endian e)
+ {
+ stream_writer(os, e).write_tag(key, t);
+ }
-void stream_writer::write_tag(const std::string& key, const tag& t)
-{
- write_type(t.get_type());
- write_string(key);
- write_payload(t);
-}
+ void stream_writer::write_tag(const std::string& key, const tag& t)
+ {
+ write_type(t.get_type());
+ write_string(key);
+ write_payload(t);
+ }
-void stream_writer::write_string(const std::string& str)
-{
- if(str.size() > max_string_len)
- {
- os.setstate(std::ios::failbit);
- std::ostringstream sstr;
- sstr << "String is too long for NBT (" << str.size() << " > " << max_string_len << ")";
- throw std::length_error(sstr.str());
- }
- write_num(static_cast<uint16_t>(str.size()));
- os.write(str.data(), str.size());
-}
+ void stream_writer::write_string(const std::string& str)
+ {
+ if (str.size() > max_string_len) {
+ os.setstate(std::ios::failbit);
+ std::ostringstream sstr;
+ sstr << "String is too long for NBT (" << str.size() << " > "
+ << max_string_len << ")";
+ throw std::length_error(sstr.str());
+ }
+ write_num(static_cast<uint16_t>(str.size()));
+ os.write(str.data(), str.size());
+ }
-}
-}
+ } // namespace io
+} // namespace nbt
diff --git a/src/tag.cpp b/src/tag.cpp
index 0446dfd3f8..8e57548f2d 100644
--- a/src/tag.cpp
+++ b/src/tag.cpp
@@ -32,136 +32,170 @@
namespace nbt
{
-//Explicit instantiation definitions for tag_primitive
-template class tag_primitive<int8_t>;
-template class tag_primitive<int16_t>;
-template class tag_primitive<int32_t>;
-template class tag_primitive<int64_t>;
-template class tag_primitive<float>;
-template class tag_primitive<double>;
-
-static_assert(std::numeric_limits<float>::is_iec559 && std::numeric_limits<double>::is_iec559,
- "The floating point values for NBT must conform to IEC 559/IEEE 754");
-
-bool is_valid_type(int type, bool allow_end)
-{
- return (allow_end ? 0 : 1) <= type && type <= 12;
-}
-
-std::unique_ptr<tag> tag::clone() &&
-{
- return std::move(*this).move_clone();
-}
-
-namespace
-{
- template<typename T>
- std::unique_ptr<tag> create_numeric_tag(tag_type type, T val)
- {
- switch(type)
- {
- case tag_type::Byte: return make_unique<tag_byte>(static_cast<int8_t>(val));
- case tag_type::Short: return make_unique<tag_short>(static_cast<int16_t>(val));
- case tag_type::Int: return make_unique<tag_int>(static_cast<int32_t>(val));
- case tag_type::Long: return make_unique<tag_long>(static_cast<int64_t>(val));
- case tag_type::Float: return make_unique<tag_float>(static_cast<float>(val));
- case tag_type::Double: return make_unique<tag_double>(static_cast<double>(val));
- default: throw std::invalid_argument("Invalid numeric tag type");
- }
- }
-}
-
-std::unique_ptr<tag> tag::create(tag_type type)
-{
- switch(type)
- {
- case tag_type::Byte: return make_unique<tag_byte>();
- case tag_type::Short: return make_unique<tag_short>();
- case tag_type::Int: return make_unique<tag_int>();
- case tag_type::Long: return make_unique<tag_long>();
- case tag_type::Float: return make_unique<tag_float>();
- case tag_type::Double: return make_unique<tag_double>();
- case tag_type::Byte_Array: return make_unique<tag_byte_array>();
- case tag_type::String: return make_unique<tag_string>();
- case tag_type::List: return make_unique<tag_list>();
- case tag_type::Compound: return make_unique<tag_compound>();
- case tag_type::Int_Array: return make_unique<tag_int_array>();
- case tag_type::Long_Array: return make_unique<tag_long_array>();
-
- default: throw std::invalid_argument("Invalid tag type");
- }
-}
-
-std::unique_ptr<tag> tag::create(tag_type type, int8_t val)
-{
- return create_numeric_tag(type, val);
-}
-
-std::unique_ptr<tag> tag::create(tag_type type, int16_t val)
-{
- return create_numeric_tag(type, val);
-}
-
-std::unique_ptr<tag> tag::create(tag_type type, int32_t val)
-{
- return create_numeric_tag(type, val);
-}
-
-std::unique_ptr<tag> tag::create(tag_type type, int64_t val)
-{
- return create_numeric_tag(type, val);
-}
-
-std::unique_ptr<tag> tag::create(tag_type type, float val)
-{
- return create_numeric_tag(type, val);
-}
-
-std::unique_ptr<tag> tag::create(tag_type type, double val)
-{
- return create_numeric_tag(type, val);
-}
-
-bool operator==(const tag& lhs, const tag& rhs)
-{
- if(typeid(lhs) != typeid(rhs))
- return false;
- return lhs.equals(rhs);
-}
-
-bool operator!=(const tag& lhs, const tag& rhs)
-{
- return !(lhs == rhs);
-}
-
-std::ostream& operator<<(std::ostream& os, tag_type tt)
-{
- switch(tt)
- {
- case tag_type::End: return os << "end";
- case tag_type::Byte: return os << "byte";
- case tag_type::Short: return os << "short";
- case tag_type::Int: return os << "int";
- case tag_type::Long: return os << "long";
- case tag_type::Float: return os << "float";
- case tag_type::Double: return os << "double";
- case tag_type::Byte_Array: return os << "byte_array";
- case tag_type::String: return os << "string";
- case tag_type::List: return os << "list";
- case tag_type::Compound: return os << "compound";
- case tag_type::Int_Array: return os << "int_array";
- case tag_type::Long_Array: return os << "long_array";
- case tag_type::Null: return os << "null";
-
- default: return os << "invalid";
- }
-}
-
-std::ostream& operator<<(std::ostream& os, const tag& t)
-{
- static const text::json_formatter formatter;
- formatter.print(os, t);
- return os;
-}
-
-}
+ // Explicit instantiation definitions for tag_primitive
+ template class tag_primitive<int8_t>;
+ template class tag_primitive<int16_t>;
+ template class tag_primitive<int32_t>;
+ template class tag_primitive<int64_t>;
+ template class tag_primitive<float>;
+ template class tag_primitive<double>;
+
+ static_assert(
+ std::numeric_limits<float>::is_iec559 &&
+ std::numeric_limits<double>::is_iec559,
+ "The floating point values for NBT must conform to IEC 559/IEEE 754");
+
+ bool is_valid_type(int type, bool allow_end)
+ {
+ return (allow_end ? 0 : 1) <= type && type <= 12;
+ }
+
+ std::unique_ptr<tag> tag::clone() &&
+ {
+ return std::move(*this).move_clone();
+ }
+
+ namespace
+ {
+ template <typename T>
+ std::unique_ptr<tag> create_numeric_tag(tag_type type, T val)
+ {
+ switch (type) {
+ case tag_type::Byte:
+ return make_unique<tag_byte>(static_cast<int8_t>(val));
+ case tag_type::Short:
+ return make_unique<tag_short>(static_cast<int16_t>(val));
+ case tag_type::Int:
+ return make_unique<tag_int>(static_cast<int32_t>(val));
+ case tag_type::Long:
+ return make_unique<tag_long>(static_cast<int64_t>(val));
+ case tag_type::Float:
+ return make_unique<tag_float>(static_cast<float>(val));
+ case tag_type::Double:
+ return make_unique<tag_double>(static_cast<double>(val));
+ default:
+ throw std::invalid_argument("Invalid numeric tag type");
+ }
+ }
+ } // namespace
+
+ std::unique_ptr<tag> tag::create(tag_type type)
+ {
+ switch (type) {
+ case tag_type::Byte:
+ return make_unique<tag_byte>();
+ case tag_type::Short:
+ return make_unique<tag_short>();
+ case tag_type::Int:
+ return make_unique<tag_int>();
+ case tag_type::Long:
+ return make_unique<tag_long>();
+ case tag_type::Float:
+ return make_unique<tag_float>();
+ case tag_type::Double:
+ return make_unique<tag_double>();
+ case tag_type::Byte_Array:
+ return make_unique<tag_byte_array>();
+ case tag_type::String:
+ return make_unique<tag_string>();
+ case tag_type::List:
+ return make_unique<tag_list>();
+ case tag_type::Compound:
+ return make_unique<tag_compound>();
+ case tag_type::Int_Array:
+ return make_unique<tag_int_array>();
+ case tag_type::Long_Array:
+ return make_unique<tag_long_array>();
+
+ default:
+ throw std::invalid_argument("Invalid tag type");
+ }
+ }
+
+ std::unique_ptr<tag> tag::create(tag_type type, int8_t val)
+ {
+ return create_numeric_tag(type, val);
+ }
+
+ std::unique_ptr<tag> tag::create(tag_type type, int16_t val)
+ {
+ return create_numeric_tag(type, val);
+ }
+
+ std::unique_ptr<tag> tag::create(tag_type type, int32_t val)
+ {
+ return create_numeric_tag(type, val);
+ }
+
+ std::unique_ptr<tag> tag::create(tag_type type, int64_t val)
+ {
+ return create_numeric_tag(type, val);
+ }
+
+ std::unique_ptr<tag> tag::create(tag_type type, float val)
+ {
+ return create_numeric_tag(type, val);
+ }
+
+ std::unique_ptr<tag> tag::create(tag_type type, double val)
+ {
+ return create_numeric_tag(type, val);
+ }
+
+ bool operator==(const tag& lhs, const tag& rhs)
+ {
+ if (typeid(lhs) != typeid(rhs))
+ return false;
+ return lhs.equals(rhs);
+ }
+
+ bool operator!=(const tag& lhs, const tag& rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+ std::ostream& operator<<(std::ostream& os, tag_type tt)
+ {
+ switch (tt) {
+ case tag_type::End:
+ return os << "end";
+ case tag_type::Byte:
+ return os << "byte";
+ case tag_type::Short:
+ return os << "short";
+ case tag_type::Int:
+ return os << "int";
+ case tag_type::Long:
+ return os << "long";
+ case tag_type::Float:
+ return os << "float";
+ case tag_type::Double:
+ return os << "double";
+ case tag_type::Byte_Array:
+ return os << "byte_array";
+ case tag_type::String:
+ return os << "string";
+ case tag_type::List:
+ return os << "list";
+ case tag_type::Compound:
+ return os << "compound";
+ case tag_type::Int_Array:
+ return os << "int_array";
+ case tag_type::Long_Array:
+ return os << "long_array";
+ case tag_type::Null:
+ return os << "null";
+
+ default:
+ return os << "invalid";
+ }
+ }
+
+ std::ostream& operator<<(std::ostream& os, const tag& t)
+ {
+ static const text::json_formatter formatter;
+ formatter.print(os, t);
+ return os;
+ }
+
+} // namespace nbt
diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp
index c71166de95..c35ef5d53f 100644
--- a/src/tag_compound.cpp
+++ b/src/tag_compound.cpp
@@ -30,84 +30,80 @@
namespace nbt
{
-tag_compound::tag_compound(std::initializer_list<std::pair<std::string, value_initializer>> init)
-{
- for(const auto& pair: init)
- tags.emplace(std::move(pair.first), std::move(pair.second));
-}
+ tag_compound::tag_compound(
+ std::initializer_list<std::pair<std::string, value_initializer>> init)
+ {
+ for (const auto& pair : init)
+ tags.emplace(std::move(pair.first), std::move(pair.second));
+ }
-value& tag_compound::at(const std::string& key)
-{
- return tags.at(key);
-}
+ value& tag_compound::at(const std::string& key)
+ {
+ return tags.at(key);
+ }
-const value& tag_compound::at(const std::string& key) const
-{
- return tags.at(key);
-}
+ const value& tag_compound::at(const std::string& key) const
+ {
+ return tags.at(key);
+ }
-std::pair<tag_compound::iterator, bool> tag_compound::put(const std::string& key, value_initializer&& val)
-{
- auto it = tags.find(key);
- if(it != tags.end())
- {
- it->second = std::move(val);
- return {it, false};
- }
- else
- {
- return tags.emplace(key, std::move(val));
- }
-}
+ std::pair<tag_compound::iterator, bool>
+ tag_compound::put(const std::string& key, value_initializer&& val)
+ {
+ auto it = tags.find(key);
+ if (it != tags.end()) {
+ it->second = std::move(val);
+ return {it, false};
+ } else {
+ return tags.emplace(key, std::move(val));
+ }
+ }
-std::pair<tag_compound::iterator, bool> tag_compound::insert(const std::string& key, value_initializer&& val)
-{
- return tags.emplace(key, std::move(val));
-}
+ std::pair<tag_compound::iterator, bool>
+ tag_compound::insert(const std::string& key, value_initializer&& val)
+ {
+ return tags.emplace(key, std::move(val));
+ }
-bool tag_compound::erase(const std::string& key)
-{
- return tags.erase(key) != 0;
-}
+ bool tag_compound::erase(const std::string& key)
+ {
+ return tags.erase(key) != 0;
+ }
-bool tag_compound::has_key(const std::string& key) const
-{
- return tags.find(key) != tags.end();
-}
+ bool tag_compound::has_key(const std::string& key) const
+ {
+ return tags.find(key) != tags.end();
+ }
-bool tag_compound::has_key(const std::string& key, tag_type type) const
-{
- auto it = tags.find(key);
- return it != tags.end() && it->second.get_type() == type;
-}
+ bool tag_compound::has_key(const std::string& key, tag_type type) const
+ {
+ auto it = tags.find(key);
+ return it != tags.end() && it->second.get_type() == type;
+ }
-void tag_compound::read_payload(io::stream_reader& reader)
-{
- clear();
- tag_type tt;
- while((tt = reader.read_type(true)) != tag_type::End)
- {
- std::string key;
- try
- {
- key = reader.read_string();
- }
- catch(io::input_error& ex)
- {
- std::ostringstream str;
- str << "Error reading key of tag_" << tt;
- throw io::input_error(str.str());
- }
- auto tptr = reader.read_payload(tt);
- tags.emplace(std::move(key), value(std::move(tptr)));
- }
-}
+ void tag_compound::read_payload(io::stream_reader& reader)
+ {
+ clear();
+ tag_type tt;
+ while ((tt = reader.read_type(true)) != tag_type::End) {
+ std::string key;
+ try {
+ key = reader.read_string();
+ } catch (io::input_error& ex) {
+ std::ostringstream str;
+ str << "Error reading key of tag_" << tt;
+ throw io::input_error(str.str());
+ }
+ auto tptr = reader.read_payload(tt);
+ tags.emplace(std::move(key), value(std::move(tptr)));
+ }
+ }
-void tag_compound::write_payload(io::stream_writer& writer) const
-{
- for(const auto& pair: tags)
- writer.write_tag(pair.first, pair.second);
- writer.write_type(tag_type::End);
-}
+ void tag_compound::write_payload(io::stream_writer& writer) const
+ {
+ for (const auto& pair : tags)
+ writer.write_tag(pair.first, pair.second);
+ writer.write_type(tag_type::End);
+ }
-}
+} // namespace nbt
diff --git a/src/tag_list.cpp b/src/tag_list.cpp
index f72f86bb24..413af779ae 100644
--- a/src/tag_list.cpp
+++ b/src/tag_list.cpp
@@ -30,126 +30,158 @@
namespace nbt
{
-tag_list::tag_list(std::initializer_list<int8_t> il) { init<tag_byte>(il); }
-tag_list::tag_list(std::initializer_list<int16_t> il) { init<tag_short>(il); }
-tag_list::tag_list(std::initializer_list<int32_t> il) { init<tag_int>(il); }
-tag_list::tag_list(std::initializer_list<int64_t> il) { init<tag_long>(il); }
-tag_list::tag_list(std::initializer_list<float> il) { init<tag_float>(il); }
-tag_list::tag_list(std::initializer_list<double> il) { init<tag_double>(il); }
-tag_list::tag_list(std::initializer_list<std::string> il) { init<tag_string>(il); }
-tag_list::tag_list(std::initializer_list<tag_byte_array> il) { init<tag_byte_array>(il); }
-tag_list::tag_list(std::initializer_list<tag_list> il) { init<tag_list>(il); }
-tag_list::tag_list(std::initializer_list<tag_compound> il) { init<tag_compound>(il); }
-tag_list::tag_list(std::initializer_list<tag_int_array> il) { init<tag_int_array>(il); }
-tag_list::tag_list(std::initializer_list<tag_long_array> il) { init<tag_long_array>(il); }
-
-tag_list::tag_list(std::initializer_list<value> init)
-{
- if(init.size() == 0)
- el_type_ = tag_type::Null;
- else
- {
- el_type_ = init.begin()->get_type();
- for(const value& val: init)
- {
- if(!val || val.get_type() != el_type_)
- throw std::invalid_argument("The values are not all the same type");
- }
- tags.assign(init.begin(), init.end());
- }
-}
-
-value& tag_list::at(size_t i)
-{
- return tags.at(i);
-}
-
-const value& tag_list::at(size_t i) const
-{
- return tags.at(i);
-}
-
-void tag_list::set(size_t i, value&& val)
-{
- if(val.get_type() != el_type_)
- throw std::invalid_argument("The tag type does not match the list's content type");
- tags.at(i) = std::move(val);
-}
-
-void tag_list::push_back(value_initializer&& val)
-{
- if(!val) //don't allow null values
- throw std::invalid_argument("The value must not be null");
- if(el_type_ == tag_type::Null) //set content type if undetermined
- el_type_ = val.get_type();
- else if(el_type_ != val.get_type())
- throw std::invalid_argument("The tag type does not match the list's content type");
- tags.push_back(std::move(val));
-}
-
-void tag_list::reset(tag_type type)
-{
- clear();
- el_type_ = type;
-}
-
-void tag_list::read_payload(io::stream_reader& reader)
-{
- tag_type lt = reader.read_type(true);
-
- int32_t length;
- reader.read_num(length);
- if(length < 0)
- reader.get_istr().setstate(std::ios::failbit);
- if(!reader.get_istr())
- throw io::input_error("Error reading length of tag_list");
-
- if(lt != tag_type::End)
- {
- reset(lt);
- tags.reserve(length);
-
- for(int32_t i = 0; i < length; ++i)
- tags.emplace_back(reader.read_payload(lt));
- }
- else
- {
- //In case of tag_end, ignore the length and leave the type undetermined
- reset(tag_type::Null);
- }
-}
-
-void tag_list::write_payload(io::stream_writer& writer) const
-{
- if(size() > io::stream_writer::max_array_len)
- {
- writer.get_ostr().setstate(std::ios::failbit);
- throw std::length_error("List is too large for NBT");
- }
- writer.write_type(el_type_ != tag_type::Null
- ? el_type_
- : tag_type::End);
- writer.write_num(static_cast<int32_t>(size()));
- for(const auto& val: tags)
- {
- //check if the value is of the correct type
- if(val.get_type() != el_type_)
- {
- writer.get_ostr().setstate(std::ios::failbit);
- throw std::logic_error("The tags in the list do not all match the content type");
- }
- writer.write_payload(val);
- }
-}
-
-bool operator==(const tag_list& lhs, const tag_list& rhs)
-{
- return lhs.el_type_ == rhs.el_type_ && lhs.tags == rhs.tags;
-}
-
-bool operator!=(const tag_list& lhs, const tag_list& rhs)
-{
- return !(lhs == rhs);
-}
-
-}
+ tag_list::tag_list(std::initializer_list<int8_t> il)
+ {
+ init<tag_byte>(il);
+ }
+ tag_list::tag_list(std::initializer_list<int16_t> il)
+ {
+ init<tag_short>(il);
+ }
+ tag_list::tag_list(std::initializer_list<int32_t> il)
+ {
+ init<tag_int>(il);
+ }
+ tag_list::tag_list(std::initializer_list<int64_t> il)
+ {
+ init<tag_long>(il);
+ }
+ tag_list::tag_list(std::initializer_list<float> il)
+ {
+ init<tag_float>(il);
+ }
+ tag_list::tag_list(std::initializer_list<double> il)
+ {
+ init<tag_double>(il);
+ }
+ tag_list::tag_list(std::initializer_list<std::string> il)
+ {
+ init<tag_string>(il);
+ }
+ tag_list::tag_list(std::initializer_list<tag_byte_array> il)
+ {
+ init<tag_byte_array>(il);
+ }
+ tag_list::tag_list(std::initializer_list<tag_list> il)
+ {
+ init<tag_list>(il);
+ }
+ tag_list::tag_list(std::initializer_list<tag_compound> il)
+ {
+ init<tag_compound>(il);
+ }
+ tag_list::tag_list(std::initializer_list<tag_int_array> il)
+ {
+ init<tag_int_array>(il);
+ }
+ tag_list::tag_list(std::initializer_list<tag_long_array> il)
+ {
+ init<tag_long_array>(il);
+ }
+
+ tag_list::tag_list(std::initializer_list<value> init)
+ {
+ if (init.size() == 0)
+ el_type_ = tag_type::Null;
+ else {
+ el_type_ = init.begin()->get_type();
+ for (const value& val : init) {
+ if (!val || val.get_type() != el_type_)
+ throw std::invalid_argument(
+ "The values are not all the same type");
+ }
+ tags.assign(init.begin(), init.end());
+ }
+ }
+
+ value& tag_list::at(size_t i)
+ {
+ return tags.at(i);
+ }
+
+ const value& tag_list::at(size_t i) const
+ {
+ return tags.at(i);
+ }
+
+ void tag_list::set(size_t i, value&& val)
+ {
+ if (val.get_type() != el_type_)
+ throw std::invalid_argument(
+ "The tag type does not match the list's content type");
+ tags.at(i) = std::move(val);
+ }
+
+ void tag_list::push_back(value_initializer&& val)
+ {
+ if (!val) // don't allow null values
+ throw std::invalid_argument("The value must not be null");
+ if (el_type_ == tag_type::Null) // set content type if undetermined
+ el_type_ = val.get_type();
+ else if (el_type_ != val.get_type())
+ throw std::invalid_argument(
+ "The tag type does not match the list's content type");
+ tags.push_back(std::move(val));
+ }
+
+ void tag_list::reset(tag_type type)
+ {
+ clear();
+ el_type_ = type;
+ }
+
+ void tag_list::read_payload(io::stream_reader& reader)
+ {
+ tag_type lt = reader.read_type(true);
+
+ int32_t length;
+ reader.read_num(length);
+ if (length < 0)
+ reader.get_istr().setstate(std::ios::failbit);
+ if (!reader.get_istr())
+ throw io::input_error("Error reading length of tag_list");
+
+ if (lt != tag_type::End) {
+ reset(lt);
+ tags.reserve(length);
+
+ for (int32_t i = 0; i < length; ++i)
+ tags.emplace_back(reader.read_payload(lt));
+ } else {
+ // In case of tag_end, ignore the length and leave the type
+ // undetermined
+ reset(tag_type::Null);
+ }
+ }
+
+ void tag_list::write_payload(io::stream_writer& writer) const
+ {
+ if (size() > io::stream_writer::max_array_len) {
+ writer.get_ostr().setstate(std::ios::failbit);
+ throw std::length_error("List is too large for NBT");
+ }
+ writer.write_type(el_type_ != tag_type::Null ? el_type_
+ : tag_type::End);
+ writer.write_num(static_cast<int32_t>(size()));
+ for (const auto& val : tags) {
+ // check if the value is of the correct type
+ if (val.get_type() != el_type_) {
+ writer.get_ostr().setstate(std::ios::failbit);
+ throw std::logic_error(
+ "The tags in the list do not all match the content type");
+ }
+ writer.write_payload(val);
+ }
+ }
+
+ bool operator==(const tag_list& lhs, const tag_list& rhs)
+ {
+ return lhs.el_type_ == rhs.el_type_ && lhs.tags == rhs.tags;
+ }
+
+ bool operator!=(const tag_list& lhs, const tag_list& rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+} // namespace nbt
diff --git a/src/tag_string.cpp b/src/tag_string.cpp
index e630b1dfc4..481ad19dee 100644
--- a/src/tag_string.cpp
+++ b/src/tag_string.cpp
@@ -28,21 +28,18 @@
namespace nbt
{
-void tag_string::read_payload(io::stream_reader& reader)
-{
- try
- {
- value = reader.read_string();
- }
- catch(io::input_error& ex)
- {
- throw io::input_error("Error reading tag_string");
- }
-}
+ void tag_string::read_payload(io::stream_reader& reader)
+ {
+ try {
+ value = reader.read_string();
+ } catch (io::input_error& ex) {
+ throw io::input_error("Error reading tag_string");
+ }
+ }
-void tag_string::write_payload(io::stream_writer& writer) const
-{
- writer.write_string(value);
-}
+ void tag_string::write_payload(io::stream_writer& writer) const
+ {
+ writer.write_string(value);
+ }
-}
+} // namespace nbt
diff --git a/src/text/json_formatter.cpp b/src/text/json_formatter.cpp
index 88f3a7c58e..46d726a3c9 100644
--- a/src/text/json_formatter.cpp
+++ b/src/text/json_formatter.cpp
@@ -30,209 +30,227 @@
namespace nbt
{
-namespace text
-{
+ namespace text
+ {
-namespace //anonymous
-{
- ///Helper class which uses the Visitor pattern to pretty-print tags
- class json_fmt_visitor : public const_nbt_visitor
- {
- public:
- json_fmt_visitor(std::ostream& os):
- os(os)
- {}
-
- void visit(const tag_byte& b) override
- { os << static_cast<int>(b.get()) << "b"; } //We don't want to print a character
-
- void visit(const tag_short& s) override
- { os << s.get() << "s"; }
-
- void visit(const tag_int& i) override
- { os << i.get(); }
-
- void visit(const tag_long& l) override
- { os << l.get() << "l"; }
-
- void visit(const tag_float& f) override
- {
- write_float(f.get());
- os << "f";
- }
-
- void visit(const tag_double& d) override
- {
- write_float(d.get());
- os << "d";
- }
-
- void visit(const tag_byte_array& ba) override
- { os << "[" << ba.size() << " bytes]"; }
-
- void visit(const tag_string& s) override
- {
- os << '"';
- write_escaped_string(s.get());
- os << '"';
- }
-
- void visit(const tag_list& l) override
- {
- //Wrap lines for lists of lists or compounds.
- //Lists of other types can usually be on one line without problem.
- const bool break_lines = l.size() > 0 &&
- (l.el_type() == tag_type::List || l.el_type() == tag_type::Compound);
-
- os << "[";
- if(break_lines)
- {
- os << "\n";
- ++indent_lvl;
- for(unsigned int i = 0; i < l.size(); ++i)
- {
- indent();
- if(l[i])
- l[i].get().accept(*this);
- else
- write_null();
- if(i != l.size()-1)
- os << ",";
- os << "\n";
- }
- --indent_lvl;
- indent();
- }
- else
- {
- for(unsigned int i = 0; i < l.size(); ++i)
- {
- if(l[i])
- l[i].get().accept(*this);
- else
- write_null();
- if(i != l.size()-1)
- os << ", ";
- }
- }
- os << "]";
- }
-
- void visit(const tag_compound& c) override
- {
- if(c.size() == 0) //No line breaks inside empty compounds please
- {
- os << "{}";
- return;
- }
-
- os << "{\n";
- ++indent_lvl;
- unsigned int i = 0;
- for(const auto& kv: c)
- {
- indent();
- os << kv.first << ": ";
- if(kv.second)
- kv.second.get().accept(*this);
- else
- write_null();
- if(i != c.size()-1)
- os << ",";
- os << "\n";
- ++i;
- }
- --indent_lvl;
- indent();
- os << "}";
- }
-
- void visit(const tag_int_array& ia) override
- {
- os << "[";
- for(unsigned int i = 0; i < ia.size(); ++i)
- {
- os << ia[i];
- if(i != ia.size()-1)
- os << ", ";
- }
- os << "]";
- }
-
- void visit(const tag_long_array& la) override
- {
- os << "[";
- for(unsigned int i = 0; i < la.size(); ++i)
- {
- os << la[i];
- if(i != la.size()-1)
- os << ", ";
- }
- os << "]";
- }
-
- private:
- const std::string indent_str = " ";
-
- std::ostream& os;
- int indent_lvl = 0;
-
- void indent()
- {
- for(int i = 0; i < indent_lvl; ++i)
- os << indent_str;
- }
-
- template<class T>
- void write_float(T val, int precision = std::numeric_limits<T>::max_digits10)
- {
- if(std::isfinite(val))
- os << std::setprecision(precision) << val;
- else if(std::isinf(val))
- {
- if(std::signbit(val))
- os << "-";
- os << "Infinity";
- }
- else
- os << "NaN";
- }
-
- void write_null()
- {
- os << "null";
- }
-
- void write_escaped_string(const std::string& str)
- {
- for (char c : str) {
- switch (c) {
- case '"': os << "\\\""; break;
- case '\\': os << "\\\\"; break;
- case '\b': os << "\\b"; break;
- case '\f': os << "\\f"; break;
- case '\n': os << "\\n"; break;
- case '\r': os << "\\r"; break;
- case '\t': os << "\\t"; break;
- default:
- if (c < 32 || c == 127) {
- // Control characters, escape as \u00XX
- os << "\\u00" << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(c);
- } else {
- os << c;
- }
- break;
- }
- }
- }
- };
-}
-
-void json_formatter::print(std::ostream& os, const tag& t) const
-{
- json_fmt_visitor v(os);
- t.accept(v);
-}
+ namespace // anonymous
+ {
+ /// Helper class which uses the Visitor pattern to pretty-print tags
+ class json_fmt_visitor : public const_nbt_visitor
+ {
+ public:
+ json_fmt_visitor(std::ostream& os) : os(os) {}
+
+ void visit(const tag_byte& b) override
+ {
+ os << static_cast<int>(b.get()) << "b";
+ } // We don't want to print a character
+
+ void visit(const tag_short& s) override
+ {
+ os << s.get() << "s";
+ }
+
+ void visit(const tag_int& i) override
+ {
+ os << i.get();
+ }
+
+ void visit(const tag_long& l) override
+ {
+ os << l.get() << "l";
+ }
+
+ void visit(const tag_float& f) override
+ {
+ write_float(f.get());
+ os << "f";
+ }
+
+ void visit(const tag_double& d) override
+ {
+ write_float(d.get());
+ os << "d";
+ }
+
+ void visit(const tag_byte_array& ba) override
+ {
+ os << "[" << ba.size() << " bytes]";
+ }
+
+ void visit(const tag_string& s) override
+ {
+ os << '"';
+ write_escaped_string(s.get());
+ os << '"';
+ }
+
+ void visit(const tag_list& l) override
+ {
+ // Wrap lines for lists of lists or compounds.
+ // Lists of other types can usually be on one line without
+ // problem.
+ const bool break_lines =
+ l.size() > 0 && (l.el_type() == tag_type::List ||
+ l.el_type() == tag_type::Compound);
+
+ os << "[";
+ if (break_lines) {
+ os << "\n";
+ ++indent_lvl;
+ for (unsigned int i = 0; i < l.size(); ++i) {
+ indent();
+ if (l[i])
+ l[i].get().accept(*this);
+ else
+ write_null();
+ if (i != l.size() - 1)
+ os << ",";
+ os << "\n";
+ }
+ --indent_lvl;
+ indent();
+ } else {
+ for (unsigned int i = 0; i < l.size(); ++i) {
+ if (l[i])
+ l[i].get().accept(*this);
+ else
+ write_null();
+ if (i != l.size() - 1)
+ os << ", ";
+ }
+ }
+ os << "]";
+ }
+
+ void visit(const tag_compound& c) override
+ {
+ if (c.size() ==
+ 0) // No line breaks inside empty compounds please
+ {
+ os << "{}";
+ return;
+ }
+
+ os << "{\n";
+ ++indent_lvl;
+ unsigned int i = 0;
+ for (const auto& kv : c) {
+ indent();
+ os << kv.first << ": ";
+ if (kv.second)
+ kv.second.get().accept(*this);
+ else
+ write_null();
+ if (i != c.size() - 1)
+ os << ",";
+ os << "\n";
+ ++i;
+ }
+ --indent_lvl;
+ indent();
+ os << "}";
+ }
+
+ void visit(const tag_int_array& ia) override
+ {
+ os << "[";
+ for (unsigned int i = 0; i < ia.size(); ++i) {
+ os << ia[i];
+ if (i != ia.size() - 1)
+ os << ", ";
+ }
+ os << "]";
+ }
+
+ void visit(const tag_long_array& la) override
+ {
+ os << "[";
+ for (unsigned int i = 0; i < la.size(); ++i) {
+ os << la[i];
+ if (i != la.size() - 1)
+ os << ", ";
+ }
+ os << "]";
+ }
+
+ private:
+ const std::string indent_str = " ";
+
+ std::ostream& os;
+ int indent_lvl = 0;
+
+ void indent()
+ {
+ for (int i = 0; i < indent_lvl; ++i)
+ os << indent_str;
+ }
+
+ template <class T>
+ void write_float(
+ T val, int precision = std::numeric_limits<T>::max_digits10)
+ {
+ if (std::isfinite(val))
+ os << std::setprecision(precision) << val;
+ else if (std::isinf(val)) {
+ if (std::signbit(val))
+ os << "-";
+ os << "Infinity";
+ } else
+ os << "NaN";
+ }
+
+ void write_null()
+ {
+ os << "null";
+ }
+
+ void write_escaped_string(const std::string& str)
+ {
+ for (char c : str) {
+ switch (c) {
+ case '"':
+ os << "\\\"";
+ break;
+ case '\\':
+ os << "\\\\";
+ break;
+ case '\b':
+ os << "\\b";
+ break;
+ case '\f':
+ os << "\\f";
+ break;
+ case '\n':
+ os << "\\n";
+ break;
+ case '\r':
+ os << "\\r";
+ break;
+ case '\t':
+ os << "\\t";
+ break;
+ default:
+ if (c < 32 || c == 127) {
+ // Control characters, escape as \u00XX
+ os << "\\u00" << std::hex << std::setw(2)
+ << std::setfill('0')
+ << static_cast<int>(c);
+ } else {
+ os << c;
+ }
+ break;
+ }
+ }
+ }
+ };
+ } // namespace
+
+ void json_formatter::print(std::ostream& os, const tag& t) const
+ {
+ json_fmt_visitor v(os);
+ t.accept(v);
+ }
-}
-}
+ } // namespace text
+} // namespace nbt
diff --git a/src/value.cpp b/src/value.cpp
index bf0ffcad78..3a9ccb71bc 100644
--- a/src/value.cpp
+++ b/src/value.cpp
@@ -32,286 +32,296 @@
namespace nbt
{
-value::value(tag&& t):
- tag_(std::move(t).move_clone())
-{}
-
-value::value(const value& rhs):
- tag_(rhs.tag_ ? rhs.tag_->clone() : nullptr)
-{}
-
-value& value::operator=(const value& rhs)
-{
- if(this != &rhs)
- {
- tag_ = rhs.tag_ ? rhs.tag_->clone() : nullptr;
- }
- return *this;
-}
-
-value& value::operator=(tag&& t)
-{
- set(std::move(t));
- return *this;
-}
-
-void value::set(tag&& t)
-{
- if(tag_)
- tag_->assign(std::move(t));
- else
- tag_ = std::move(t).move_clone();
-}
-
-//Primitive assignment
-namespace // helper functions local to this translation unit
-{
- template<typename T>
- void assign_numeric_impl(std::unique_ptr<tag>& tag_ptr, T val,
- tag_type default_type)
- {
- using nbt::tag_type;
- if(!tag_ptr)
- {
- tag_ptr = tag::create(default_type, val);
- return;
- }
-
- // Determine the incoming tag type for T
- auto incoming_type = detail::get_primitive_type<T>::value;
-
- // If the existing tag is of a narrower type than the incoming type,
- // reject the assignment to avoid widening the stored tag type.
- auto existing_type = tag_ptr->get_type();
-
- if(static_cast<int>(existing_type) < static_cast<int>(incoming_type))
- {
- throw std::bad_cast();
- }
-
- // Existing type is same or wider: write into the existing tag (may narrow)
- switch(existing_type)
- {
- case tag_type::Byte: static_cast<tag_byte&>(*tag_ptr).set(static_cast<int8_t>(val)); break;
- case tag_type::Short: static_cast<tag_short&>(*tag_ptr).set(static_cast<int16_t>(val)); break;
- case tag_type::Int: static_cast<tag_int&>(*tag_ptr).set(static_cast<int32_t>(val)); break;
- case tag_type::Long: static_cast<tag_long&>(*tag_ptr).set(static_cast<int64_t>(val)); break;
- case tag_type::Float: static_cast<tag_float&>(*tag_ptr).set(static_cast<float>(val)); break;
- case tag_type::Double: static_cast<tag_double&>(*tag_ptr).set(static_cast<double>(val)); break;
- default: throw std::bad_cast();
- }
- }
-}
-
-value& value::operator=(int8_t val)
-{
- assign_numeric_impl(tag_, val, tag_type::Byte);
- return *this;
-}
-
-value& value::operator=(int16_t val)
-{
- assign_numeric_impl(tag_, val, tag_type::Short);
- return *this;
-}
-
-value& value::operator=(int32_t val)
-{
- assign_numeric_impl(tag_, val, tag_type::Int);
- return *this;
-}
-
-value& value::operator=(int64_t val)
-{
- assign_numeric_impl(tag_, val, tag_type::Long);
- return *this;
-}
-
-value& value::operator=(float val)
-{
- assign_numeric_impl(tag_, val, tag_type::Float);
- return *this;
-}
-
-value& value::operator=(double val)
-{
- assign_numeric_impl(tag_, val, tag_type::Double);
- return *this;
-}
-
-//Primitive conversion
-value::operator int8_t() const
-{
- switch(tag_->get_type())
- {
- case tag_type::Byte:
- return static_cast<tag_byte&>(*tag_).get();
-
- default:
- throw std::bad_cast();
- }
-}
-
-value::operator int16_t() const
-{
- switch(tag_->get_type())
- {
- case tag_type::Byte:
- return static_cast<tag_byte&>(*tag_).get();
- case tag_type::Short:
- return static_cast<tag_short&>(*tag_).get();
-
- default:
- throw std::bad_cast();
- }
-}
-
-value::operator int32_t() const
-{
- switch(tag_->get_type())
- {
- case tag_type::Byte:
- return static_cast<tag_byte&>(*tag_).get();
- case tag_type::Short:
- return static_cast<tag_short&>(*tag_).get();
- case tag_type::Int:
- return static_cast<tag_int&>(*tag_).get();
-
- default:
- throw std::bad_cast();
- }
-}
-
-value::operator int64_t() const
-{
- switch(tag_->get_type())
- {
- case tag_type::Byte:
- return static_cast<tag_byte&>(*tag_).get();
- case tag_type::Short:
- return static_cast<tag_short&>(*tag_).get();
- case tag_type::Int:
- return static_cast<tag_int&>(*tag_).get();
- case tag_type::Long:
- return static_cast<tag_long&>(*tag_).get();
-
- default:
- throw std::bad_cast();
- }
-}
-
-value::operator float() const
-{
- switch(tag_->get_type())
- {
- case tag_type::Byte:
- return static_cast<tag_byte&>(*tag_).get();
- case tag_type::Short:
- return static_cast<tag_short&>(*tag_).get();
- case tag_type::Int:
- return static_cast<tag_int&>(*tag_).get();
- case tag_type::Long:
- return static_cast<tag_long&>(*tag_).get();
- case tag_type::Float:
- return static_cast<tag_float&>(*tag_).get();
-
- default:
- throw std::bad_cast();
- }
-}
-
-value::operator double() const
-{
- switch(tag_->get_type())
- {
- case tag_type::Byte:
- return static_cast<tag_byte&>(*tag_).get();
- case tag_type::Short:
- return static_cast<tag_short&>(*tag_).get();
- case tag_type::Int:
- return static_cast<tag_int&>(*tag_).get();
- case tag_type::Long:
- return static_cast<tag_long&>(*tag_).get();
- case tag_type::Float:
- return static_cast<tag_float&>(*tag_).get();
- case tag_type::Double:
- return static_cast<tag_double&>(*tag_).get();
-
- default:
- throw std::bad_cast();
- }
-}
-
-value& value::operator=(std::string&& str)
-{
- if(!tag_)
- set(tag_string(std::move(str)));
- else
- dynamic_cast<tag_string&>(*tag_).set(std::move(str));
- return *this;
-}
-
-value::operator const std::string&() const
-{
- return dynamic_cast<tag_string&>(*tag_).get();
-}
-
-value& value::at(const std::string& key)
-{
- return dynamic_cast<tag_compound&>(*tag_).at(key);
-}
-
-const value& value::at(const std::string& key) const
-{
- return dynamic_cast<const tag_compound&>(*tag_).at(key);
-}
-
-value& value::operator[](const std::string& key)
-{
- return dynamic_cast<tag_compound&>(*tag_)[key];
-}
-
-value& value::operator[](const char* key)
-{
- return (*this)[std::string(key)];
-}
-
-value& value::at(size_t i)
-{
- return dynamic_cast<tag_list&>(*tag_).at(i);
-}
-
-const value& value::at(size_t i) const
-{
- return dynamic_cast<const tag_list&>(*tag_).at(i);
-}
-
-value& value::operator[](size_t i)
-{
- return dynamic_cast<tag_list&>(*tag_)[i];
-}
-
-const value& value::operator[](size_t i) const
-{
- return dynamic_cast<const tag_list&>(*tag_)[i];
-}
-
-tag_type value::get_type() const
-{
- return tag_ ? tag_->get_type() : tag_type::Null;
-}
-
-bool operator==(const value& lhs, const value& rhs)
-{
- if(lhs.tag_ != nullptr && rhs.tag_ != nullptr)
- return *lhs.tag_ == *rhs.tag_;
- else
- return lhs.tag_ == nullptr && rhs.tag_ == nullptr;
-}
-
-bool operator!=(const value& lhs, const value& rhs)
-{
- return !(lhs == rhs);
-}
-
-}
+ value::value(tag&& t) : tag_(std::move(t).move_clone()) {}
+
+ value::value(const value& rhs)
+ : tag_(rhs.tag_ ? rhs.tag_->clone() : nullptr)
+ {
+ }
+
+ value& value::operator=(const value& rhs)
+ {
+ if (this != &rhs) {
+ tag_ = rhs.tag_ ? rhs.tag_->clone() : nullptr;
+ }
+ return *this;
+ }
+
+ value& value::operator=(tag&& t)
+ {
+ set(std::move(t));
+ return *this;
+ }
+
+ void value::set(tag&& t)
+ {
+ if (tag_)
+ tag_->assign(std::move(t));
+ else
+ tag_ = std::move(t).move_clone();
+ }
+
+ // Primitive assignment
+ namespace // helper functions local to this translation unit
+ {
+ template <typename T>
+ void assign_numeric_impl(std::unique_ptr<tag>& tag_ptr, T val,
+ tag_type default_type)
+ {
+ using nbt::tag_type;
+ if (!tag_ptr) {
+ tag_ptr = tag::create(default_type, val);
+ return;
+ }
+
+ // Determine the incoming tag type for T
+ auto incoming_type = detail::get_primitive_type<T>::value;
+
+ // If the existing tag is of a narrower type than the incoming type,
+ // reject the assignment to avoid widening the stored tag type.
+ auto existing_type = tag_ptr->get_type();
+
+ if (static_cast<int>(existing_type) <
+ static_cast<int>(incoming_type)) {
+ throw std::bad_cast();
+ }
+
+ // Existing type is same or wider: write into the existing tag (may
+ // narrow)
+ switch (existing_type) {
+ case tag_type::Byte:
+ static_cast<tag_byte&>(*tag_ptr).set(
+ static_cast<int8_t>(val));
+ break;
+ case tag_type::Short:
+ static_cast<tag_short&>(*tag_ptr).set(
+ static_cast<int16_t>(val));
+ break;
+ case tag_type::Int:
+ static_cast<tag_int&>(*tag_ptr).set(
+ static_cast<int32_t>(val));
+ break;
+ case tag_type::Long:
+ static_cast<tag_long&>(*tag_ptr).set(
+ static_cast<int64_t>(val));
+ break;
+ case tag_type::Float:
+ static_cast<tag_float&>(*tag_ptr).set(
+ static_cast<float>(val));
+ break;
+ case tag_type::Double:
+ static_cast<tag_double&>(*tag_ptr).set(
+ static_cast<double>(val));
+ break;
+ default:
+ throw std::bad_cast();
+ }
+ }
+ } // namespace
+
+ value& value::operator=(int8_t val)
+ {
+ assign_numeric_impl(tag_, val, tag_type::Byte);
+ return *this;
+ }
+
+ value& value::operator=(int16_t val)
+ {
+ assign_numeric_impl(tag_, val, tag_type::Short);
+ return *this;
+ }
+
+ value& value::operator=(int32_t val)
+ {
+ assign_numeric_impl(tag_, val, tag_type::Int);
+ return *this;
+ }
+
+ value& value::operator=(int64_t val)
+ {
+ assign_numeric_impl(tag_, val, tag_type::Long);
+ return *this;
+ }
+
+ value& value::operator=(float val)
+ {
+ assign_numeric_impl(tag_, val, tag_type::Float);
+ return *this;
+ }
+
+ value& value::operator=(double val)
+ {
+ assign_numeric_impl(tag_, val, tag_type::Double);
+ return *this;
+ }
+
+ // Primitive conversion
+ value::operator int8_t() const
+ {
+ switch (tag_->get_type()) {
+ case tag_type::Byte:
+ return static_cast<tag_byte&>(*tag_).get();
+
+ default:
+ throw std::bad_cast();
+ }
+ }
+
+ value::operator int16_t() const
+ {
+ switch (tag_->get_type()) {
+ case tag_type::Byte:
+ return static_cast<tag_byte&>(*tag_).get();
+ case tag_type::Short:
+ return static_cast<tag_short&>(*tag_).get();
+
+ default:
+ throw std::bad_cast();
+ }
+ }
+
+ value::operator int32_t() const
+ {
+ switch (tag_->get_type()) {
+ case tag_type::Byte:
+ return static_cast<tag_byte&>(*tag_).get();
+ case tag_type::Short:
+ return static_cast<tag_short&>(*tag_).get();
+ case tag_type::Int:
+ return static_cast<tag_int&>(*tag_).get();
+
+ default:
+ throw std::bad_cast();
+ }
+ }
+
+ value::operator int64_t() const
+ {
+ switch (tag_->get_type()) {
+ case tag_type::Byte:
+ return static_cast<tag_byte&>(*tag_).get();
+ case tag_type::Short:
+ return static_cast<tag_short&>(*tag_).get();
+ case tag_type::Int:
+ return static_cast<tag_int&>(*tag_).get();
+ case tag_type::Long:
+ return static_cast<tag_long&>(*tag_).get();
+
+ default:
+ throw std::bad_cast();
+ }
+ }
+
+ value::operator float() const
+ {
+ switch (tag_->get_type()) {
+ case tag_type::Byte:
+ return static_cast<tag_byte&>(*tag_).get();
+ case tag_type::Short:
+ return static_cast<tag_short&>(*tag_).get();
+ case tag_type::Int:
+ return static_cast<tag_int&>(*tag_).get();
+ case tag_type::Long:
+ return static_cast<tag_long&>(*tag_).get();
+ case tag_type::Float:
+ return static_cast<tag_float&>(*tag_).get();
+
+ default:
+ throw std::bad_cast();
+ }
+ }
+
+ value::operator double() const
+ {
+ switch (tag_->get_type()) {
+ case tag_type::Byte:
+ return static_cast<tag_byte&>(*tag_).get();
+ case tag_type::Short:
+ return static_cast<tag_short&>(*tag_).get();
+ case tag_type::Int:
+ return static_cast<tag_int&>(*tag_).get();
+ case tag_type::Long:
+ return static_cast<tag_long&>(*tag_).get();
+ case tag_type::Float:
+ return static_cast<tag_float&>(*tag_).get();
+ case tag_type::Double:
+ return static_cast<tag_double&>(*tag_).get();
+
+ default:
+ throw std::bad_cast();
+ }
+ }
+
+ value& value::operator=(std::string&& str)
+ {
+ if (!tag_)
+ set(tag_string(std::move(str)));
+ else
+ dynamic_cast<tag_string&>(*tag_).set(std::move(str));
+ return *this;
+ }
+
+ value::operator const std::string&() const
+ {
+ return dynamic_cast<tag_string&>(*tag_).get();
+ }
+
+ value& value::at(const std::string& key)
+ {
+ return dynamic_cast<tag_compound&>(*tag_).at(key);
+ }
+
+ const value& value::at(const std::string& key) const
+ {
+ return dynamic_cast<const tag_compound&>(*tag_).at(key);
+ }
+
+ value& value::operator[](const std::string& key)
+ {
+ return dynamic_cast<tag_compound&>(*tag_)[key];
+ }
+
+ value& value::operator[](const char* key)
+ {
+ return (*this)[std::string(key)];
+ }
+
+ value& value::at(size_t i)
+ {
+ return dynamic_cast<tag_list&>(*tag_).at(i);
+ }
+
+ const value& value::at(size_t i) const
+ {
+ return dynamic_cast<const tag_list&>(*tag_).at(i);
+ }
+
+ value& value::operator[](size_t i)
+ {
+ return dynamic_cast<tag_list&>(*tag_)[i];
+ }
+
+ const value& value::operator[](size_t i) const
+ {
+ return dynamic_cast<const tag_list&>(*tag_)[i];
+ }
+
+ tag_type value::get_type() const
+ {
+ return tag_ ? tag_->get_type() : tag_type::Null;
+ }
+
+ bool operator==(const value& lhs, const value& rhs)
+ {
+ if (lhs.tag_ != nullptr && rhs.tag_ != nullptr)
+ return *lhs.tag_ == *rhs.tag_;
+ else
+ return lhs.tag_ == nullptr && rhs.tag_ == nullptr;
+ }
+
+ bool operator!=(const value& lhs, const value& rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+} // namespace nbt
diff --git a/src/value_initializer.cpp b/src/value_initializer.cpp
index b0d0d3d747..ec52f544fe 100644
--- a/src/value_initializer.cpp
+++ b/src/value_initializer.cpp
@@ -27,14 +27,23 @@
namespace nbt
{
-value_initializer::value_initializer(int8_t val) : value(tag_byte(val)) {}
-value_initializer::value_initializer(int16_t val) : value(tag_short(val)) {}
-value_initializer::value_initializer(int32_t val) : value(tag_int(val)) {}
-value_initializer::value_initializer(int64_t val) : value(tag_long(val)) {}
-value_initializer::value_initializer(float val) : value(tag_float(val)) {}
-value_initializer::value_initializer(double val) : value(tag_double(val)) {}
-value_initializer::value_initializer(const std::string& str): value(tag_string(str)) {}
-value_initializer::value_initializer(std::string&& str) : value(tag_string(std::move(str))) {}
-value_initializer::value_initializer(const char* str) : value(tag_string(str)) {}
+ value_initializer::value_initializer(int8_t val) : value(tag_byte(val)) {}
+ value_initializer::value_initializer(int16_t val) : value(tag_short(val)) {}
+ value_initializer::value_initializer(int32_t val) : value(tag_int(val)) {}
+ value_initializer::value_initializer(int64_t val) : value(tag_long(val)) {}
+ value_initializer::value_initializer(float val) : value(tag_float(val)) {}
+ value_initializer::value_initializer(double val) : value(tag_double(val)) {}
+ value_initializer::value_initializer(const std::string& str)
+ : value(tag_string(str))
+ {
+ }
+ value_initializer::value_initializer(std::string&& str)
+ : value(tag_string(std::move(str)))
+ {
+ }
+ value_initializer::value_initializer(const char* str)
+ : value(tag_string(str))
+ {
+ }
-}
+} // namespace nbt
diff --git a/test/endian_str_test.h b/test/endian_str_test.h
index 386ba60b57..d647d563eb 100644
--- a/test/endian_str_test.h
+++ b/test/endian_str_test.h
@@ -33,149 +33,137 @@ using namespace endian;
class endian_str_test : public CxxTest::TestSuite
{
-public:
- void test_uint()
- {
- std::stringstream str(std::ios::in | std::ios::out | std::ios::binary);
-
- write_little(str, uint8_t (0x01));
- write_little(str, uint16_t(0x0102));
- write (str, uint32_t(0x01020304), little);
- write_little(str, uint64_t(0x0102030405060708));
-
- write_big (str, uint8_t (0x09));
- write_big (str, uint16_t(0x090A));
- write_big (str, uint32_t(0x090A0B0C));
- write (str, uint64_t(0x090A0B0C0D0E0F10), big);
-
- std::string expected{
- 1,
- 2, 1,
- 4, 3, 2, 1,
- 8, 7, 6, 5, 4, 3, 2, 1,
-
- 9,
- 9, 10,
- 9, 10, 11, 12,
- 9, 10, 11, 12, 13, 14, 15, 16
- };
- TS_ASSERT_EQUALS(str.str(), expected);
-
- uint8_t u8;
- uint16_t u16;
- uint32_t u32;
- uint64_t u64;
-
- read_little(str, u8);
- TS_ASSERT_EQUALS(u8, 0x01);
- read_little(str, u16);
- TS_ASSERT_EQUALS(u16, 0x0102);
- read_little(str, u32);
- TS_ASSERT_EQUALS(u32, 0x01020304u);
- read(str, u64, little);
- TS_ASSERT_EQUALS(u64, 0x0102030405060708u);
-
- read_big(str, u8);
- TS_ASSERT_EQUALS(u8, 0x09);
- read_big(str, u16);
- TS_ASSERT_EQUALS(u16, 0x090A);
- read(str, u32, big);
- TS_ASSERT_EQUALS(u32, 0x090A0B0Cu);
- read_big(str, u64);
- TS_ASSERT_EQUALS(u64, 0x090A0B0C0D0E0F10u);
-
- TS_ASSERT(str); //Check if stream has failed
- }
-
- void test_sint()
- {
- std::stringstream str(std::ios::in | std::ios::out | std::ios::binary);
-
- write_little(str, int8_t (-0x01));
- write_little(str, int16_t(-0x0102));
- write_little(str, int32_t(-0x01020304));
- write (str, int64_t(-0x0102030405060708), little);
-
- write_big (str, int8_t (-0x09));
- write_big (str, int16_t(-0x090A));
- write (str, int32_t(-0x090A0B0C), big);
- write_big (str, int64_t(-0x090A0B0C0D0E0F10));
-
- std::string expected{ //meh, stupid narrowing conversions
- '\xFF',
- '\xFE', '\xFE',
- '\xFC', '\xFC', '\xFD', '\xFE',
- '\xF8', '\xF8', '\xF9', '\xFA', '\xFB', '\xFC', '\xFD', '\xFE',
-
- '\xF7',
- '\xF6', '\xF6',
- '\xF6', '\xF5', '\xF4', '\xF4',
- '\xF6', '\xF5', '\xF4', '\xF3', '\xF2', '\xF1', '\xF0', '\xF0'
- };
- TS_ASSERT_EQUALS(str.str(), expected);
-
- int8_t i8;
- int16_t i16;
- int32_t i32;
- int64_t i64;
-
- read_little(str, i8);
- TS_ASSERT_EQUALS(i8, -0x01);
- read_little(str, i16);
- TS_ASSERT_EQUALS(i16, -0x0102);
- read(str, i32, little);
- TS_ASSERT_EQUALS(i32, -0x01020304);
- read_little(str, i64);
- TS_ASSERT_EQUALS(i64, -0x0102030405060708);
-
- read_big(str, i8);
- TS_ASSERT_EQUALS(i8, -0x09);
- read_big(str, i16);
- TS_ASSERT_EQUALS(i16, -0x090A);
- read_big(str, i32);
- TS_ASSERT_EQUALS(i32, -0x090A0B0C);
- read(str, i64, big);
- TS_ASSERT_EQUALS(i64, -0x090A0B0C0D0E0F10);
-
- TS_ASSERT(str); //Check if stream has failed
- }
-
- void test_float()
- {
- std::stringstream str(std::ios::in | std::ios::out | std::ios::binary);
-
- //C99 has hexadecimal floating point literals, C++ doesn't...
- const float fconst = std::stof("-0xCDEF01p-63"); //-1.46325e-012
- const double dconst = std::stod("-0x1DEF0102030405p-375"); //-1.09484e-097
- //We will be assuming IEEE 754 here
-
- write_little(str, fconst);
- write_little(str, dconst);
- write_big (str, fconst);
- write_big (str, dconst);
-
- std::string expected{
- '\x01', '\xEF', '\xCD', '\xAB',
- '\x05', '\x04', '\x03', '\x02', '\x01', '\xEF', '\xCD', '\xAB',
-
- '\xAB', '\xCD', '\xEF', '\x01',
- '\xAB', '\xCD', '\xEF', '\x01', '\x02', '\x03', '\x04', '\x05'
- };
- TS_ASSERT_EQUALS(str.str(), expected);
-
- float f;
- double d;
-
- read_little(str, f);
- TS_ASSERT_EQUALS(f, fconst);
- read_little(str, d);
- TS_ASSERT_EQUALS(d, dconst);
-
- read_big(str, f);
- TS_ASSERT_EQUALS(f, fconst);
- read_big(str, d);
- TS_ASSERT_EQUALS(d, dconst);
-
- TS_ASSERT(str); //Check if stream has failed
- }
+ public:
+ void test_uint()
+ {
+ std::stringstream str(std::ios::in | std::ios::out | std::ios::binary);
+
+ write_little(str, uint8_t(0x01));
+ write_little(str, uint16_t(0x0102));
+ write(str, uint32_t(0x01020304), little);
+ write_little(str, uint64_t(0x0102030405060708));
+
+ write_big(str, uint8_t(0x09));
+ write_big(str, uint16_t(0x090A));
+ write_big(str, uint32_t(0x090A0B0C));
+ write(str, uint64_t(0x090A0B0C0D0E0F10), big);
+
+ std::string expected{
+ 1, 2, 1, 4, 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, 1,
+
+ 9, 9, 10, 9, 10, 11, 12, 9, 10, 11, 12, 13, 14, 15, 16};
+ TS_ASSERT_EQUALS(str.str(), expected);
+
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+
+ read_little(str, u8);
+ TS_ASSERT_EQUALS(u8, 0x01);
+ read_little(str, u16);
+ TS_ASSERT_EQUALS(u16, 0x0102);
+ read_little(str, u32);
+ TS_ASSERT_EQUALS(u32, 0x01020304u);
+ read(str, u64, little);
+ TS_ASSERT_EQUALS(u64, 0x0102030405060708u);
+
+ read_big(str, u8);
+ TS_ASSERT_EQUALS(u8, 0x09);
+ read_big(str, u16);
+ TS_ASSERT_EQUALS(u16, 0x090A);
+ read(str, u32, big);
+ TS_ASSERT_EQUALS(u32, 0x090A0B0Cu);
+ read_big(str, u64);
+ TS_ASSERT_EQUALS(u64, 0x090A0B0C0D0E0F10u);
+
+ TS_ASSERT(str); // Check if stream has failed
+ }
+
+ void test_sint()
+ {
+ std::stringstream str(std::ios::in | std::ios::out | std::ios::binary);
+
+ write_little(str, int8_t(-0x01));
+ write_little(str, int16_t(-0x0102));
+ write_little(str, int32_t(-0x01020304));
+ write(str, int64_t(-0x0102030405060708), little);
+
+ write_big(str, int8_t(-0x09));
+ write_big(str, int16_t(-0x090A));
+ write(str, int32_t(-0x090A0B0C), big);
+ write_big(str, int64_t(-0x090A0B0C0D0E0F10));
+
+ std::string expected{
+ // meh, stupid narrowing conversions
+ '\xFF', '\xFE', '\xFE', '\xFC', '\xFC', '\xFD', '\xFE', '\xF8',
+ '\xF8', '\xF9', '\xFA', '\xFB', '\xFC', '\xFD', '\xFE',
+
+ '\xF7', '\xF6', '\xF6', '\xF6', '\xF5', '\xF4', '\xF4', '\xF6',
+ '\xF5', '\xF4', '\xF3', '\xF2', '\xF1', '\xF0', '\xF0'};
+ TS_ASSERT_EQUALS(str.str(), expected);
+
+ int8_t i8;
+ int16_t i16;
+ int32_t i32;
+ int64_t i64;
+
+ read_little(str, i8);
+ TS_ASSERT_EQUALS(i8, -0x01);
+ read_little(str, i16);
+ TS_ASSERT_EQUALS(i16, -0x0102);
+ read(str, i32, little);
+ TS_ASSERT_EQUALS(i32, -0x01020304);
+ read_little(str, i64);
+ TS_ASSERT_EQUALS(i64, -0x0102030405060708);
+
+ read_big(str, i8);
+ TS_ASSERT_EQUALS(i8, -0x09);
+ read_big(str, i16);
+ TS_ASSERT_EQUALS(i16, -0x090A);
+ read_big(str, i32);
+ TS_ASSERT_EQUALS(i32, -0x090A0B0C);
+ read(str, i64, big);
+ TS_ASSERT_EQUALS(i64, -0x090A0B0C0D0E0F10);
+
+ TS_ASSERT(str); // Check if stream has failed
+ }
+
+ void test_float()
+ {
+ std::stringstream str(std::ios::in | std::ios::out | std::ios::binary);
+
+ // C99 has hexadecimal floating point literals, C++ doesn't...
+ const float fconst = std::stof("-0xCDEF01p-63"); //-1.46325e-012
+ const double dconst =
+ std::stod("-0x1DEF0102030405p-375"); //-1.09484e-097
+ // We will be assuming IEEE 754 here
+
+ write_little(str, fconst);
+ write_little(str, dconst);
+ write_big(str, fconst);
+ write_big(str, dconst);
+
+ std::string expected{'\x01', '\xEF', '\xCD', '\xAB', '\x05', '\x04',
+ '\x03', '\x02', '\x01', '\xEF', '\xCD', '\xAB',
+
+ '\xAB', '\xCD', '\xEF', '\x01', '\xAB', '\xCD',
+ '\xEF', '\x01', '\x02', '\x03', '\x04', '\x05'};
+ TS_ASSERT_EQUALS(str.str(), expected);
+
+ float f;
+ double d;
+
+ read_little(str, f);
+ TS_ASSERT_EQUALS(f, fconst);
+ read_little(str, d);
+ TS_ASSERT_EQUALS(d, dconst);
+
+ read_big(str, f);
+ TS_ASSERT_EQUALS(f, fconst);
+ read_big(str, d);
+ TS_ASSERT_EQUALS(d, dconst);
+
+ TS_ASSERT(str); // Check if stream has failed
+ }
};
diff --git a/test/format_test.cpp b/test/format_test.cpp
index ed8d2d1bfc..f7d95985c9 100644
--- a/test/format_test.cpp
+++ b/test/format_test.cpp
@@ -21,7 +21,7 @@
* You should have received a copy of the GNU Lesser General Public License
* along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
*/
-//#include "text/json_formatter.h"
+// #include "text/json_formatter.h"
#include "io/stream_reader.h"
#include "io/stream_writer.h"
#include <fstream>
@@ -33,73 +33,74 @@ using namespace nbt;
int main()
{
- // Write that into a file and read back for testing
- tag_compound comp{
- {"byte", tag_byte(-128)},
- {"short", tag_short(-32768)},
- {"int", tag_int(-2147483648)},
- {"long", tag_long(-9223372036854775808U)},
+ // Write that into a file and read back for testing
+ tag_compound comp{
+ {"byte", tag_byte(-128)},
+ {"short", tag_short(-32768)},
+ {"int", tag_int(-2147483648)},
+ {"long", tag_long(-9223372036854775808U)},
- {"float 1", 1.618034f},
- {"float 2", 6.626070e-34f},
- {"float 3", 2.273737e+29f},
- {"float 4", -std::numeric_limits<float>::infinity()},
- {"float 5", std::numeric_limits<float>::quiet_NaN()},
+ {"float 1", 1.618034f},
+ {"float 2", 6.626070e-34f},
+ {"float 3", 2.273737e+29f},
+ {"float 4", -std::numeric_limits<float>::infinity()},
+ {"float 5", std::numeric_limits<float>::quiet_NaN()},
- {"double 1", 3.141592653589793},
- {"double 2", 1.749899444387479e-193},
- {"double 3", 2.850825855152578e+175},
- {"double 4", -std::numeric_limits<double>::infinity()},
- {"double 5", std::numeric_limits<double>::quiet_NaN()},
+ {"double 1", 3.141592653589793},
+ {"double 2", 1.749899444387479e-193},
+ {"double 3", 2.850825855152578e+175},
+ {"double 4", -std::numeric_limits<double>::infinity()},
+ {"double 5", std::numeric_limits<double>::quiet_NaN()},
- {"string 1", "Hello World! \u00E4\u00F6\u00FC\u00DF"},
- {"string 2", "String with\nline breaks\tand tabs"},
+ {"string 1", "Hello World! \u00E4\u00F6\u00FC\u00DF"},
+ {"string 2", "String with\nline breaks\tand tabs"},
- {"byte array", tag_byte_array{12, 13, 14, 15, 16}},
- {"int array", tag_int_array{0x0badc0de, -0x0dedbeef, 0x1badbabe}},
- {"long array", tag_long_array{0x0badc0de0badc0de, -0x0dedbeef0dedbeef, 0x1badbabe1badbabe}},
+ {"byte array", tag_byte_array{12, 13, 14, 15, 16}},
+ {"int array", tag_int_array{0x0badc0de, -0x0dedbeef, 0x1badbabe}},
+ {"long array", tag_long_array{0x0badc0de0badc0de, -0x0dedbeef0dedbeef,
+ 0x1badbabe1badbabe}},
- {"list (empty)", tag_list::of<tag_byte_array>({})},
- {"list (float)", tag_list{2.0f, 1.0f, 0.5f, 0.25f}},
- {"list (list)", tag_list::of<tag_list>({
- {},
- {4, 5, 6},
- {tag_compound{{"egg", "ham"}}, tag_compound{{"foo", "bar"}}}
- })},
- {"list (compound)", tag_list::of<tag_compound>({
- {{"created-on", 42}, {"names", tag_list{"Compound", "tag", "#0"}}},
- {{"created-on", 45}, {"names", tag_list{"Compound", "tag", "#1"}}}
- })},
+ {"list (empty)", tag_list::of<tag_byte_array>({})},
+ {"list (float)", tag_list{2.0f, 1.0f, 0.5f, 0.25f}},
+ {"list (list)",
+ tag_list::of<tag_list>(
+ {{},
+ {4, 5, 6},
+ {tag_compound{{"egg", "ham"}}, tag_compound{{"foo", "bar"}}}})},
+ {"list (compound)",
+ tag_list::of<tag_compound>(
+ {{{"created-on", 42},
+ {"names", tag_list{"Compound", "tag", "#0"}}},
+ {{"created-on", 45},
+ {"names", tag_list{"Compound", "tag", "#1"}}}})},
- {"compound (empty)", tag_compound()},
- {"compound (nested)", tag_compound{
- {"key", "value"},
- {"key with \u00E4\u00F6\u00FC", tag_byte(-1)},
- {"key with\nnewline and\ttab", tag_compound{}}
- }},
+ {"compound (empty)", tag_compound()},
+ {"compound (nested)",
+ tag_compound{{"key", "value"},
+ {"key with \u00E4\u00F6\u00FC", tag_byte(-1)},
+ {"key with\nnewline and\ttab", tag_compound{}}}},
- {"null", nullptr}
- };
+ {"null", nullptr}};
- std::cout << "----- default operator<<:\n";
- std::cout << comp;
- std::cout << "\n-----" << std::endl;
+ std::cout << "----- default operator<<:\n";
+ std::cout << comp;
+ std::cout << "\n-----" << std::endl;
- // Write to file and read back
- {
- tag_compound file_comp = comp;
- file_comp.erase("null");
- std::ofstream out("test_output.nbt", std::ios::binary);
- nbt::io::write_tag("root", file_comp, out);
- }
+ // Write to file and read back
+ {
+ tag_compound file_comp = comp;
+ file_comp.erase("null");
+ std::ofstream out("test_output.nbt", std::ios::binary);
+ nbt::io::write_tag("root", file_comp, out);
+ }
- {
- std::ifstream in("test_output.nbt", std::ios::binary);
- auto read_pair = nbt::io::read_compound(in);
- std::cout << "----- read back from file:\n";
- std::cout << *read_pair.second;
- std::cout << "\n-----" << std::endl;
- }
+ {
+ std::ifstream in("test_output.nbt", std::ios::binary);
+ auto read_pair = nbt::io::read_compound(in);
+ std::cout << "----- read back from file:\n";
+ std::cout << *read_pair.second;
+ std::cout << "\n-----" << std::endl;
+ }
- return 0;
+ return 0;
}
diff --git a/test/nbttest.h b/test/nbttest.h
index 4d7f2cb5de..ce660e355d 100644
--- a/test/nbttest.h
+++ b/test/nbttest.h
@@ -34,471 +34,539 @@ using namespace nbt;
class nbttest : public CxxTest::TestSuite
{
-public:
- void test_tag()
- {
- TS_ASSERT(!is_valid_type(-1));
- TS_ASSERT(!is_valid_type(0));
- TS_ASSERT(is_valid_type(0, true));
- TS_ASSERT(is_valid_type(1));
- TS_ASSERT(is_valid_type(5, false));
- TS_ASSERT(is_valid_type(7, true));
- TS_ASSERT(is_valid_type(12));
- TS_ASSERT(!is_valid_type(13));
-
- //looks like TS_ASSERT_EQUALS can't handle abstract classes...
- TS_ASSERT(*tag::create(tag_type::Byte) == tag_byte());
- TS_ASSERT_THROWS(tag::create(tag_type::Null), std::invalid_argument);
- TS_ASSERT_THROWS(tag::create(tag_type::End), std::invalid_argument);
-
- tag_string tstr("foo");
- auto cl = tstr.clone();
- TS_ASSERT_EQUALS(tstr.get(), "foo");
- TS_ASSERT(tstr == *cl);
-
- cl = std::move(tstr).clone();
- TS_ASSERT(*cl == tag_string("foo"));
- TS_ASSERT(*cl != tag_string("bar"));
-
- cl = std::move(*cl).move_clone();
- TS_ASSERT(*cl == tag_string("foo"));
-
- tstr.assign(tag_string("bar"));
- TS_ASSERT_THROWS(tstr.assign(tag_int(6)), std::bad_cast);
- TS_ASSERT_EQUALS(tstr.get(), "bar");
-
- TS_ASSERT_EQUALS(&tstr.as<tag_string>(), &tstr);
- TS_ASSERT_THROWS(tstr.as<tag_byte_array>(), std::bad_cast);
- }
-
- void test_get_type()
- {
- TS_ASSERT_EQUALS(tag_byte().get_type() , tag_type::Byte);
- TS_ASSERT_EQUALS(tag_short().get_type() , tag_type::Short);
- TS_ASSERT_EQUALS(tag_int().get_type() , tag_type::Int);
- TS_ASSERT_EQUALS(tag_long().get_type() , tag_type::Long);
- TS_ASSERT_EQUALS(tag_float().get_type() , tag_type::Float);
- TS_ASSERT_EQUALS(tag_double().get_type() , tag_type::Double);
- TS_ASSERT_EQUALS(tag_byte_array().get_type(), tag_type::Byte_Array);
- TS_ASSERT_EQUALS(tag_string().get_type() , tag_type::String);
- TS_ASSERT_EQUALS(tag_list().get_type() , tag_type::List);
- TS_ASSERT_EQUALS(tag_compound().get_type() , tag_type::Compound);
- TS_ASSERT_EQUALS(tag_int_array().get_type() , tag_type::Int_Array);
- TS_ASSERT_EQUALS(tag_long_array().get_type(), tag_type::Long_Array);
- }
-
- void test_tag_primitive()
- {
- tag_int tag(6);
- TS_ASSERT_EQUALS(tag.get(), 6);
- int& ref = tag;
- ref = 12;
- TS_ASSERT(tag == 12);
- TS_ASSERT(tag != 6);
- tag.set(24);
- TS_ASSERT_EQUALS(ref, 24);
- tag = 7;
- TS_ASSERT_EQUALS(static_cast<int>(tag), 7);
-
- TS_ASSERT_EQUALS(tag, tag_int(7));
- TS_ASSERT_DIFFERS(tag_float(2.5), tag_float(-2.5));
- TS_ASSERT_DIFFERS(tag_float(2.5), tag_double(2.5));
-
- TS_ASSERT(tag_double() == 0.0);
-
- TS_ASSERT_EQUALS(tag_byte(INT8_MAX).get(), INT8_MAX);
- TS_ASSERT_EQUALS(tag_byte(INT8_MIN).get(), INT8_MIN);
- TS_ASSERT_EQUALS(tag_short(INT16_MAX).get(), INT16_MAX);
- TS_ASSERT_EQUALS(tag_short(INT16_MIN).get(), INT16_MIN);
- TS_ASSERT_EQUALS(tag_int(INT32_MAX).get(), INT32_MAX);
- TS_ASSERT_EQUALS(tag_int(INT32_MIN).get(), INT32_MIN);
- TS_ASSERT_EQUALS(tag_long(INT64_MAX).get(), INT64_MAX);
- TS_ASSERT_EQUALS(tag_long(INT64_MIN).get(), INT64_MIN);
- }
-
- void test_tag_string()
- {
- tag_string tag("foo");
- TS_ASSERT_EQUALS(tag.get(), "foo");
- std::string& ref = tag;
- ref = "bar";
- TS_ASSERT_EQUALS(tag.get(), "bar");
- TS_ASSERT_DIFFERS(tag.get(), "foo");
- tag.set("baz");
- TS_ASSERT_EQUALS(ref, "baz");
- tag = "quux";
- TS_ASSERT_EQUALS("quux", static_cast<std::string>(tag));
- std::string str("foo");
- tag = str;
- TS_ASSERT_EQUALS(tag.get(),str);
-
- TS_ASSERT_EQUALS(tag_string(str).get(), "foo");
- TS_ASSERT_EQUALS(tag_string().get(), "");
- }
-
- void test_tag_compound()
- {
- tag_compound comp{
- {"foo", int16_t(12)},
- {"bar", "baz"},
- {"baz", -2.0},
- {"list", tag_list{16, 17}}
- };
-
- //Test assignments and conversions, and exceptions on bad conversions
- TS_ASSERT_EQUALS(comp["foo"].get_type(), tag_type::Short);
- TS_ASSERT_EQUALS(static_cast<int32_t>(comp["foo"]), 12);
- TS_ASSERT_EQUALS(static_cast<int16_t>(comp.at("foo")), int16_t(12));
- TS_ASSERT(comp["foo"] == tag_short(12));
- TS_ASSERT_THROWS(static_cast<int8_t>(comp["foo"]), std::bad_cast);
- TS_ASSERT_THROWS(static_cast<std::string>(comp["foo"]), std::bad_cast);
-
- TS_ASSERT_THROWS(comp["foo"] = 32, std::bad_cast);
- comp["foo"] = int8_t(32);
- TS_ASSERT_EQUALS(static_cast<int16_t>(comp["foo"]), 32);
-
- TS_ASSERT_EQUALS(comp["bar"].get_type(), tag_type::String);
- TS_ASSERT_EQUALS(static_cast<std::string>(comp["bar"]), "baz");
- TS_ASSERT_THROWS(static_cast<int>(comp["bar"]), std::bad_cast);
-
- TS_ASSERT_THROWS(comp["bar"] = -128, std::bad_cast);
- comp["bar"] = "barbaz";
- TS_ASSERT_EQUALS(static_cast<std::string>(comp["bar"]), "barbaz");
-
- TS_ASSERT_EQUALS(comp["baz"].get_type(), tag_type::Double);
- TS_ASSERT_EQUALS(static_cast<double>(comp["baz"]), -2.0);
- TS_ASSERT_THROWS(static_cast<float>(comp["baz"]), std::bad_cast);
-
- //Test nested access
- comp["quux"] = tag_compound{{"Hello", "World"}, {"zero", 0}};
- TS_ASSERT_EQUALS(comp.at("quux").get_type(), tag_type::Compound);
- TS_ASSERT_EQUALS(static_cast<std::string>(comp["quux"].at("Hello")), "World");
- TS_ASSERT_EQUALS(static_cast<std::string>(comp["quux"]["Hello"]), "World");
- TS_ASSERT(comp["list"][1] == tag_int(17));
-
- TS_ASSERT_THROWS(comp.at("nothing"), std::out_of_range);
-
- //Test equality comparisons
- tag_compound comp2{
- {"foo", int16_t(32)},
- {"bar", "barbaz"},
- {"baz", -2.0},
- {"quux", tag_compound{{"Hello", "World"}, {"zero", 0}}},
- {"list", tag_list{16, 17}}
- };
- TS_ASSERT(comp == comp2);
- TS_ASSERT(comp != dynamic_cast<const tag_compound&>(comp2["quux"].get()));
- TS_ASSERT(comp != comp2["quux"]);
- TS_ASSERT(dynamic_cast<const tag_compound&>(comp["quux"].get()) == comp2["quux"]);
-
- //Test whether begin() through end() goes through all the keys and their
- //values. The order of iteration is irrelevant there.
- std::set<std::string> keys{"bar", "baz", "foo", "list", "quux"};
- TS_ASSERT_EQUALS(comp2.size(), keys.size());
- unsigned int i = 0;
- for(const std::pair<const std::string, value>& val: comp2)
- {
- TS_ASSERT_LESS_THAN(i, comp2.size());
- TS_ASSERT(keys.count(val.first));
- TS_ASSERT(val.second == comp2[val.first]);
- ++i;
- }
- TS_ASSERT_EQUALS(i, comp2.size());
-
- //Test erasing and has_key
- TS_ASSERT_EQUALS(comp.erase("nothing"), false);
- TS_ASSERT(comp.has_key("quux"));
- TS_ASSERT(comp.has_key("quux", tag_type::Compound));
- TS_ASSERT(!comp.has_key("quux", tag_type::List));
- TS_ASSERT(!comp.has_key("quux", tag_type::Null));
-
- TS_ASSERT_EQUALS(comp.erase("quux"), true);
- TS_ASSERT(!comp.has_key("quux"));
- TS_ASSERT(!comp.has_key("quux", tag_type::Compound));
- TS_ASSERT(!comp.has_key("quux", tag_type::Null));
-
- comp.clear();
- TS_ASSERT(comp == tag_compound{});
-
- //Test inserting values
- TS_ASSERT_EQUALS(comp.put("abc", tag_double(6.0)).second, true);
- TS_ASSERT_EQUALS(comp.put("abc", tag_long(-28)).second, false);
- TS_ASSERT_EQUALS(comp.insert("ghi", tag_string("world")).second, true);
- TS_ASSERT_EQUALS(comp.insert("abc", tag_string("hello")).second, false);
- TS_ASSERT_EQUALS(comp.emplace<tag_string>("def", "ghi").second, true);
- TS_ASSERT_EQUALS(comp.emplace<tag_byte>("def", 4).second, false);
- TS_ASSERT((comp == tag_compound{
- {"abc", tag_long(-28)},
- {"def", tag_byte(4)},
- {"ghi", tag_string("world")}
- }));
- }
-
- void test_value()
- {
- value val1;
- value val2(make_unique<tag_int>(42));
- value val3(tag_int(42));
-
- TS_ASSERT(!val1 && val2 && val3);
- TS_ASSERT(val1 == val1);
- TS_ASSERT(val1 != val2);
- TS_ASSERT(val2 == val3);
- TS_ASSERT(val3 == val3);
-
- value valstr(tag_string("foo"));
- TS_ASSERT_EQUALS(static_cast<std::string>(valstr), "foo");
- valstr = "bar";
- TS_ASSERT_THROWS(valstr = 5, std::bad_cast);
- TS_ASSERT_EQUALS(static_cast<std::string>(valstr), "bar");
- TS_ASSERT(valstr.as<tag_string>() == "bar");
- TS_ASSERT_EQUALS(&valstr.as<tag>(), &valstr.get());
- TS_ASSERT_THROWS(valstr.as<tag_float>(), std::bad_cast);
-
- val1 = int64_t(42);
- TS_ASSERT(val2 != val1);
-
- TS_ASSERT_THROWS(val2 = int64_t(12), std::bad_cast);
- TS_ASSERT_EQUALS(static_cast<int64_t>(val2), 42);
- tag_int* ptr = dynamic_cast<tag_int*>(val2.get_ptr().get());
- TS_ASSERT(*ptr == 42);
- val2 = 52;
- TS_ASSERT_EQUALS(static_cast<int32_t>(val2), 52);
- TS_ASSERT(*ptr == 52);
-
- TS_ASSERT_THROWS(val1["foo"], std::bad_cast);
- TS_ASSERT_THROWS(val1.at("foo"), std::bad_cast);
-
- val3 = 52;
- TS_ASSERT(val2 == val3);
- TS_ASSERT(val2.get_ptr() != val3.get_ptr());
-
- val3 = std::move(val2);
- TS_ASSERT(val3 == tag_int(52));
- TS_ASSERT(!val2);
-
- tag_int& tag = dynamic_cast<tag_int&>(val3.get());
- TS_ASSERT(tag == tag_int(52));
- tag = 21;
- TS_ASSERT_EQUALS(static_cast<int32_t>(val3), 21);
- val1.set_ptr(std::move(val3.get_ptr()));
- TS_ASSERT(val1.as<tag_int>() == 21);
-
- TS_ASSERT_EQUALS(val1.get_type(), tag_type::Int);
- TS_ASSERT_EQUALS(val2.get_type(), tag_type::Null);
- TS_ASSERT_EQUALS(val3.get_type(), tag_type::Null);
-
- val2 = val1;
- val1 = val3;
- TS_ASSERT(!val1 && val2 && !val3);
- TS_ASSERT(val1.get_ptr() == nullptr);
- TS_ASSERT(val2.get() == tag_int(21));
- TS_ASSERT(value(val1) == val1);
- TS_ASSERT(value(val2) == val2);
- val1 = val1;
- val2 = val2;
- TS_ASSERT(!val1);
- TS_ASSERT(val1 == value_initializer(nullptr));
- TS_ASSERT(val2 == tag_int(21));
-
- val3 = tag_short(2);
- TS_ASSERT_THROWS(val3 = tag_string("foo"), std::bad_cast);
- TS_ASSERT(val3.get() == tag_short(2));
-
- val2.set_ptr(make_unique<tag_string>("foo"));
- TS_ASSERT(val2 == tag_string("foo"));
- }
-
- void test_tag_list()
- {
- tag_list list;
- TS_ASSERT_EQUALS(list.el_type(), tag_type::Null);
- TS_ASSERT_THROWS(list.push_back(value(nullptr)), std::invalid_argument);
-
- list.emplace_back<tag_string>("foo");
- TS_ASSERT_EQUALS(list.el_type(), tag_type::String);
- list.push_back(tag_string("bar"));
- TS_ASSERT_THROWS(list.push_back(tag_int(42)), std::invalid_argument);
- TS_ASSERT_THROWS(list.emplace_back<tag_compound>(), std::invalid_argument);
-
- TS_ASSERT((list == tag_list{"foo", "bar"}));
- TS_ASSERT(list[0] == tag_string("foo"));
- TS_ASSERT_EQUALS(static_cast<std::string>(list.at(1)), "bar");
-
- TS_ASSERT_EQUALS(list.size(), 2u);
- TS_ASSERT_THROWS(list.at(2), std::out_of_range);
- TS_ASSERT_THROWS(list.at(-1), std::out_of_range);
-
- list.set(1, value(tag_string("baz")));
- TS_ASSERT_THROWS(list.set(1, value(nullptr)), std::invalid_argument);
- TS_ASSERT_THROWS(list.set(1, value(tag_int(-42))), std::invalid_argument);
- TS_ASSERT_EQUALS(static_cast<std::string>(list[1]), "baz");
-
- TS_ASSERT_EQUALS(list.size(), 2u);
- tag_string values[] = {"foo", "baz"};
- TS_ASSERT_EQUALS(list.end() - list.begin(), int(list.size()));
- TS_ASSERT(std::equal(list.begin(), list.end(), values));
-
- list.pop_back();
- TS_ASSERT(list == tag_list{"foo"});
- TS_ASSERT(list == tag_list::of<tag_string>({"foo"}));
- TS_ASSERT(tag_list::of<tag_string>({"foo"}) == tag_list{"foo"});
- TS_ASSERT((list != tag_list{2, 3, 5, 7}));
-
- list.clear();
- TS_ASSERT_EQUALS(list.size(), 0u);
- TS_ASSERT_EQUALS(list.el_type(), tag_type::String)
- TS_ASSERT_THROWS(list.push_back(tag_short(25)), std::invalid_argument);
- TS_ASSERT_THROWS(list.push_back(value(nullptr)), std::invalid_argument);
-
- list.reset();
- TS_ASSERT_EQUALS(list.el_type(), tag_type::Null);
- list.emplace_back<tag_int>(17);
- TS_ASSERT_EQUALS(list.el_type(), tag_type::Int);
-
- list.reset(tag_type::Float);
- TS_ASSERT_EQUALS(list.el_type(), tag_type::Float);
- list.emplace_back<tag_float>(17.0f);
- TS_ASSERT(list == tag_list({17.0f}));
-
- TS_ASSERT(tag_list() != tag_list(tag_type::Int));
- TS_ASSERT(tag_list() == tag_list());
- TS_ASSERT(tag_list(tag_type::Short) != tag_list(tag_type::Int));
- TS_ASSERT(tag_list(tag_type::Short) == tag_list(tag_type::Short));
-
- tag_list short_list = tag_list::of<tag_short>({25, 36});
- TS_ASSERT_EQUALS(short_list.el_type(), tag_type::Short);
- TS_ASSERT((short_list == tag_list{int16_t(25), int16_t(36)}));
- TS_ASSERT((short_list != tag_list{25, 36}));
- TS_ASSERT((short_list == tag_list{value(tag_short(25)), value(tag_short(36))}));
-
- TS_ASSERT_THROWS((tag_list{value(tag_byte(4)), value(tag_int(5))}), std::invalid_argument);
- TS_ASSERT_THROWS((tag_list{value(nullptr), value(tag_int(6))}), std::invalid_argument);
- TS_ASSERT_THROWS((tag_list{value(tag_int(7)), value(tag_int(8)), value(nullptr)}), std::invalid_argument);
- TS_ASSERT_EQUALS((tag_list(std::initializer_list<value>{})).el_type(), tag_type::Null);
- TS_ASSERT_EQUALS((tag_list{2, 3, 5, 7}).el_type(), tag_type::Int);
- }
-
- void test_tag_byte_array()
- {
- std::vector<int8_t> vec{1, 2, 127, -128};
- tag_byte_array arr{1, 2, 127, -128};
- TS_ASSERT_EQUALS(arr.size(), 4u);
- TS_ASSERT(arr.at(0) == 1 && arr[1] == 2 && arr[2] == 127 && arr.at(3) == -128);
- TS_ASSERT_THROWS(arr.at(-1), std::out_of_range);
- TS_ASSERT_THROWS(arr.at(4), std::out_of_range);
-
- TS_ASSERT(arr.get() == vec);
- TS_ASSERT(arr == tag_byte_array(std::vector<int8_t>(vec)));
-
- arr.push_back(42);
- vec.push_back(42);
-
- TS_ASSERT_EQUALS(arr.size(), 5u);
- TS_ASSERT_EQUALS(arr.end() - arr.begin(), int(arr.size()));
- TS_ASSERT(std::equal(arr.begin(), arr.end(), vec.begin()));
-
- arr.pop_back();
- arr.pop_back();
- TS_ASSERT_EQUALS(arr.size(), 3u);
- TS_ASSERT((arr == tag_byte_array{1, 2, 127}));
- TS_ASSERT((arr != tag_int_array{1, 2, 127}));
- TS_ASSERT((arr != tag_long_array{1, 2, 127}));
- TS_ASSERT((arr != tag_byte_array{1, 2, -1}));
-
- arr.clear();
- TS_ASSERT(arr == tag_byte_array());
- }
-
- void test_tag_int_array()
- {
- std::vector<int32_t> vec{100, 200, INT32_MAX, INT32_MIN};
- tag_int_array arr{100, 200, INT32_MAX, INT32_MIN};
- TS_ASSERT_EQUALS(arr.size(), 4u);
- TS_ASSERT(arr.at(0) == 100 && arr[1] == 200 && arr[2] == INT32_MAX && arr.at(3) == INT32_MIN);
- TS_ASSERT_THROWS(arr.at(-1), std::out_of_range);
- TS_ASSERT_THROWS(arr.at(4), std::out_of_range);
-
- TS_ASSERT(arr.get() == vec);
- TS_ASSERT(arr == tag_int_array(std::vector<int32_t>(vec)));
-
- arr.push_back(42);
- vec.push_back(42);
-
- TS_ASSERT_EQUALS(arr.size(), 5u);
- TS_ASSERT_EQUALS(arr.end() - arr.begin(), int(arr.size()));
- TS_ASSERT(std::equal(arr.begin(), arr.end(), vec.begin()));
-
- arr.pop_back();
- arr.pop_back();
- TS_ASSERT_EQUALS(arr.size(), 3u);
- TS_ASSERT((arr == tag_int_array{100, 200, INT32_MAX}));
- TS_ASSERT((arr != tag_int_array{100, -56, -1}));
-
- arr.clear();
- TS_ASSERT(arr == tag_int_array());
- }
-
- void test_tag_long_array()
- {
- std::vector<int64_t> vec{100, 200, INT64_MAX, INT64_MIN};
- tag_long_array arr{100, 200, INT64_MAX, INT64_MIN};
- TS_ASSERT_EQUALS(arr.size(), 4u);
- TS_ASSERT(arr.at(0) == 100 && arr[1] == 200 && arr[2] == INT64_MAX && arr.at(3) == INT64_MIN);
- TS_ASSERT_THROWS(arr.at(-1), std::out_of_range);
- TS_ASSERT_THROWS(arr.at(4), std::out_of_range);
-
- TS_ASSERT(arr.get() == vec);
- TS_ASSERT(arr == tag_long_array(std::vector<int64_t>(vec)));
-
- arr.push_back(42);
- vec.push_back(42);
-
- TS_ASSERT_EQUALS(arr.size(), 5u);
- TS_ASSERT_EQUALS(arr.end() - arr.begin(), int(arr.size()));
- TS_ASSERT(std::equal(arr.begin(), arr.end(), vec.begin()));
-
- arr.pop_back();
- arr.pop_back();
- TS_ASSERT_EQUALS(arr.size(), 3u);
- TS_ASSERT((arr == tag_long_array{100, 200, INT64_MAX}));
- TS_ASSERT((arr != tag_long_array{100, -56, -1}));
-
- arr.clear();
- TS_ASSERT(arr == tag_long_array());
- }
-
- void test_visitor()
- {
- struct : public nbt_visitor
- {
- tag* visited = nullptr;
-
- void visit(tag_byte& tag) { visited = &tag; }
- void visit(tag_short& tag) { visited = &tag; }
- void visit(tag_int& tag) { visited = &tag; }
- void visit(tag_long& tag) { visited = &tag; }
- void visit(tag_float& tag) { visited = &tag; }
- void visit(tag_double& tag) { visited = &tag; }
- void visit(tag_byte_array& tag) { visited = &tag; }
- void visit(tag_string& tag) { visited = &tag; }
- void visit(tag_list& tag) { visited = &tag; }
- void visit(tag_compound& tag) { visited = &tag; }
- void visit(tag_int_array& tag) { visited = &tag; }
- void visit(tag_long_array& tag) { visited = &tag; }
- } v;
-
- tag_byte b; b.accept(v); TS_ASSERT_EQUALS(v.visited, &b);
- tag_short s; s.accept(v); TS_ASSERT_EQUALS(v.visited, &s);
- tag_int i; i.accept(v); TS_ASSERT_EQUALS(v.visited, &i);
- tag_long l; l.accept(v); TS_ASSERT_EQUALS(v.visited, &l);
- tag_float f; f.accept(v); TS_ASSERT_EQUALS(v.visited, &f);
- tag_double d; d.accept(v); TS_ASSERT_EQUALS(v.visited, &d);
- tag_byte_array ba; ba.accept(v); TS_ASSERT_EQUALS(v.visited, &ba);
- tag_string st; st.accept(v); TS_ASSERT_EQUALS(v.visited, &st);
- tag_list ls; ls.accept(v); TS_ASSERT_EQUALS(v.visited, &ls);
- tag_compound c; c.accept(v); TS_ASSERT_EQUALS(v.visited, &c);
- tag_int_array ia; ia.accept(v); TS_ASSERT_EQUALS(v.visited, &ia);
- tag_long_array la; la.accept(v); TS_ASSERT_EQUALS(v.visited, &la);
- }
+ public:
+ void test_tag()
+ {
+ TS_ASSERT(!is_valid_type(-1));
+ TS_ASSERT(!is_valid_type(0));
+ TS_ASSERT(is_valid_type(0, true));
+ TS_ASSERT(is_valid_type(1));
+ TS_ASSERT(is_valid_type(5, false));
+ TS_ASSERT(is_valid_type(7, true));
+ TS_ASSERT(is_valid_type(12));
+ TS_ASSERT(!is_valid_type(13));
+
+ // looks like TS_ASSERT_EQUALS can't handle abstract classes...
+ TS_ASSERT(*tag::create(tag_type::Byte) == tag_byte());
+ TS_ASSERT_THROWS(tag::create(tag_type::Null), std::invalid_argument);
+ TS_ASSERT_THROWS(tag::create(tag_type::End), std::invalid_argument);
+
+ tag_string tstr("foo");
+ auto cl = tstr.clone();
+ TS_ASSERT_EQUALS(tstr.get(), "foo");
+ TS_ASSERT(tstr == *cl);
+
+ cl = std::move(tstr).clone();
+ TS_ASSERT(*cl == tag_string("foo"));
+ TS_ASSERT(*cl != tag_string("bar"));
+
+ cl = std::move(*cl).move_clone();
+ TS_ASSERT(*cl == tag_string("foo"));
+
+ tstr.assign(tag_string("bar"));
+ TS_ASSERT_THROWS(tstr.assign(tag_int(6)), std::bad_cast);
+ TS_ASSERT_EQUALS(tstr.get(), "bar");
+
+ TS_ASSERT_EQUALS(&tstr.as<tag_string>(), &tstr);
+ TS_ASSERT_THROWS(tstr.as<tag_byte_array>(), std::bad_cast);
+ }
+
+ void test_get_type()
+ {
+ TS_ASSERT_EQUALS(tag_byte().get_type(), tag_type::Byte);
+ TS_ASSERT_EQUALS(tag_short().get_type(), tag_type::Short);
+ TS_ASSERT_EQUALS(tag_int().get_type(), tag_type::Int);
+ TS_ASSERT_EQUALS(tag_long().get_type(), tag_type::Long);
+ TS_ASSERT_EQUALS(tag_float().get_type(), tag_type::Float);
+ TS_ASSERT_EQUALS(tag_double().get_type(), tag_type::Double);
+ TS_ASSERT_EQUALS(tag_byte_array().get_type(), tag_type::Byte_Array);
+ TS_ASSERT_EQUALS(tag_string().get_type(), tag_type::String);
+ TS_ASSERT_EQUALS(tag_list().get_type(), tag_type::List);
+ TS_ASSERT_EQUALS(tag_compound().get_type(), tag_type::Compound);
+ TS_ASSERT_EQUALS(tag_int_array().get_type(), tag_type::Int_Array);
+ TS_ASSERT_EQUALS(tag_long_array().get_type(), tag_type::Long_Array);
+ }
+
+ void test_tag_primitive()
+ {
+ tag_int tag(6);
+ TS_ASSERT_EQUALS(tag.get(), 6);
+ int& ref = tag;
+ ref = 12;
+ TS_ASSERT(tag == 12);
+ TS_ASSERT(tag != 6);
+ tag.set(24);
+ TS_ASSERT_EQUALS(ref, 24);
+ tag = 7;
+ TS_ASSERT_EQUALS(static_cast<int>(tag), 7);
+
+ TS_ASSERT_EQUALS(tag, tag_int(7));
+ TS_ASSERT_DIFFERS(tag_float(2.5), tag_float(-2.5));
+ TS_ASSERT_DIFFERS(tag_float(2.5), tag_double(2.5));
+
+ TS_ASSERT(tag_double() == 0.0);
+
+ TS_ASSERT_EQUALS(tag_byte(INT8_MAX).get(), INT8_MAX);
+ TS_ASSERT_EQUALS(tag_byte(INT8_MIN).get(), INT8_MIN);
+ TS_ASSERT_EQUALS(tag_short(INT16_MAX).get(), INT16_MAX);
+ TS_ASSERT_EQUALS(tag_short(INT16_MIN).get(), INT16_MIN);
+ TS_ASSERT_EQUALS(tag_int(INT32_MAX).get(), INT32_MAX);
+ TS_ASSERT_EQUALS(tag_int(INT32_MIN).get(), INT32_MIN);
+ TS_ASSERT_EQUALS(tag_long(INT64_MAX).get(), INT64_MAX);
+ TS_ASSERT_EQUALS(tag_long(INT64_MIN).get(), INT64_MIN);
+ }
+
+ void test_tag_string()
+ {
+ tag_string tag("foo");
+ TS_ASSERT_EQUALS(tag.get(), "foo");
+ std::string& ref = tag;
+ ref = "bar";
+ TS_ASSERT_EQUALS(tag.get(), "bar");
+ TS_ASSERT_DIFFERS(tag.get(), "foo");
+ tag.set("baz");
+ TS_ASSERT_EQUALS(ref, "baz");
+ tag = "quux";
+ TS_ASSERT_EQUALS("quux", static_cast<std::string>(tag));
+ std::string str("foo");
+ tag = str;
+ TS_ASSERT_EQUALS(tag.get(), str);
+
+ TS_ASSERT_EQUALS(tag_string(str).get(), "foo");
+ TS_ASSERT_EQUALS(tag_string().get(), "");
+ }
+
+ void test_tag_compound()
+ {
+ tag_compound comp{{"foo", int16_t(12)},
+ {"bar", "baz"},
+ {"baz", -2.0},
+ {"list", tag_list{16, 17}}};
+
+ // Test assignments and conversions, and exceptions on bad conversions
+ TS_ASSERT_EQUALS(comp["foo"].get_type(), tag_type::Short);
+ TS_ASSERT_EQUALS(static_cast<int32_t>(comp["foo"]), 12);
+ TS_ASSERT_EQUALS(static_cast<int16_t>(comp.at("foo")), int16_t(12));
+ TS_ASSERT(comp["foo"] == tag_short(12));
+ TS_ASSERT_THROWS(static_cast<int8_t>(comp["foo"]), std::bad_cast);
+ TS_ASSERT_THROWS(static_cast<std::string>(comp["foo"]), std::bad_cast);
+
+ TS_ASSERT_THROWS(comp["foo"] = 32, std::bad_cast);
+ comp["foo"] = int8_t(32);
+ TS_ASSERT_EQUALS(static_cast<int16_t>(comp["foo"]), 32);
+
+ TS_ASSERT_EQUALS(comp["bar"].get_type(), tag_type::String);
+ TS_ASSERT_EQUALS(static_cast<std::string>(comp["bar"]), "baz");
+ TS_ASSERT_THROWS(static_cast<int>(comp["bar"]), std::bad_cast);
+
+ TS_ASSERT_THROWS(comp["bar"] = -128, std::bad_cast);
+ comp["bar"] = "barbaz";
+ TS_ASSERT_EQUALS(static_cast<std::string>(comp["bar"]), "barbaz");
+
+ TS_ASSERT_EQUALS(comp["baz"].get_type(), tag_type::Double);
+ TS_ASSERT_EQUALS(static_cast<double>(comp["baz"]), -2.0);
+ TS_ASSERT_THROWS(static_cast<float>(comp["baz"]), std::bad_cast);
+
+ // Test nested access
+ comp["quux"] = tag_compound{{"Hello", "World"}, {"zero", 0}};
+ TS_ASSERT_EQUALS(comp.at("quux").get_type(), tag_type::Compound);
+ TS_ASSERT_EQUALS(static_cast<std::string>(comp["quux"].at("Hello")),
+ "World");
+ TS_ASSERT_EQUALS(static_cast<std::string>(comp["quux"]["Hello"]),
+ "World");
+ TS_ASSERT(comp["list"][1] == tag_int(17));
+
+ TS_ASSERT_THROWS(comp.at("nothing"), std::out_of_range);
+
+ // Test equality comparisons
+ tag_compound comp2{
+ {"foo", int16_t(32)},
+ {"bar", "barbaz"},
+ {"baz", -2.0},
+ {"quux", tag_compound{{"Hello", "World"}, {"zero", 0}}},
+ {"list", tag_list{16, 17}}};
+ TS_ASSERT(comp == comp2);
+ TS_ASSERT(comp !=
+ dynamic_cast<const tag_compound&>(comp2["quux"].get()));
+ TS_ASSERT(comp != comp2["quux"]);
+ TS_ASSERT(dynamic_cast<const tag_compound&>(comp["quux"].get()) ==
+ comp2["quux"]);
+
+ // Test whether begin() through end() goes through all the keys and
+ // their values. The order of iteration is irrelevant there.
+ std::set<std::string> keys{"bar", "baz", "foo", "list", "quux"};
+ TS_ASSERT_EQUALS(comp2.size(), keys.size());
+ unsigned int i = 0;
+ for (const std::pair<const std::string, value>& val : comp2) {
+ TS_ASSERT_LESS_THAN(i, comp2.size());
+ TS_ASSERT(keys.count(val.first));
+ TS_ASSERT(val.second == comp2[val.first]);
+ ++i;
+ }
+ TS_ASSERT_EQUALS(i, comp2.size());
+
+ // Test erasing and has_key
+ TS_ASSERT_EQUALS(comp.erase("nothing"), false);
+ TS_ASSERT(comp.has_key("quux"));
+ TS_ASSERT(comp.has_key("quux", tag_type::Compound));
+ TS_ASSERT(!comp.has_key("quux", tag_type::List));
+ TS_ASSERT(!comp.has_key("quux", tag_type::Null));
+
+ TS_ASSERT_EQUALS(comp.erase("quux"), true);
+ TS_ASSERT(!comp.has_key("quux"));
+ TS_ASSERT(!comp.has_key("quux", tag_type::Compound));
+ TS_ASSERT(!comp.has_key("quux", tag_type::Null));
+
+ comp.clear();
+ TS_ASSERT(comp == tag_compound{});
+
+ // Test inserting values
+ TS_ASSERT_EQUALS(comp.put("abc", tag_double(6.0)).second, true);
+ TS_ASSERT_EQUALS(comp.put("abc", tag_long(-28)).second, false);
+ TS_ASSERT_EQUALS(comp.insert("ghi", tag_string("world")).second, true);
+ TS_ASSERT_EQUALS(comp.insert("abc", tag_string("hello")).second, false);
+ TS_ASSERT_EQUALS(comp.emplace<tag_string>("def", "ghi").second, true);
+ TS_ASSERT_EQUALS(comp.emplace<tag_byte>("def", 4).second, false);
+ TS_ASSERT((comp == tag_compound{{"abc", tag_long(-28)},
+ {"def", tag_byte(4)},
+ {"ghi", tag_string("world")}}));
+ }
+
+ void test_value()
+ {
+ value val1;
+ value val2(make_unique<tag_int>(42));
+ value val3(tag_int(42));
+
+ TS_ASSERT(!val1 && val2 && val3);
+ TS_ASSERT(val1 == val1);
+ TS_ASSERT(val1 != val2);
+ TS_ASSERT(val2 == val3);
+ TS_ASSERT(val3 == val3);
+
+ value valstr(tag_string("foo"));
+ TS_ASSERT_EQUALS(static_cast<std::string>(valstr), "foo");
+ valstr = "bar";
+ TS_ASSERT_THROWS(valstr = 5, std::bad_cast);
+ TS_ASSERT_EQUALS(static_cast<std::string>(valstr), "bar");
+ TS_ASSERT(valstr.as<tag_string>() == "bar");
+ TS_ASSERT_EQUALS(&valstr.as<tag>(), &valstr.get());
+ TS_ASSERT_THROWS(valstr.as<tag_float>(), std::bad_cast);
+
+ val1 = int64_t(42);
+ TS_ASSERT(val2 != val1);
+
+ TS_ASSERT_THROWS(val2 = int64_t(12), std::bad_cast);
+ TS_ASSERT_EQUALS(static_cast<int64_t>(val2), 42);
+ tag_int* ptr = dynamic_cast<tag_int*>(val2.get_ptr().get());
+ TS_ASSERT(*ptr == 42);
+ val2 = 52;
+ TS_ASSERT_EQUALS(static_cast<int32_t>(val2), 52);
+ TS_ASSERT(*ptr == 52);
+
+ TS_ASSERT_THROWS(val1["foo"], std::bad_cast);
+ TS_ASSERT_THROWS(val1.at("foo"), std::bad_cast);
+
+ val3 = 52;
+ TS_ASSERT(val2 == val3);
+ TS_ASSERT(val2.get_ptr() != val3.get_ptr());
+
+ val3 = std::move(val2);
+ TS_ASSERT(val3 == tag_int(52));
+ TS_ASSERT(!val2);
+
+ tag_int& tag = dynamic_cast<tag_int&>(val3.get());
+ TS_ASSERT(tag == tag_int(52));
+ tag = 21;
+ TS_ASSERT_EQUALS(static_cast<int32_t>(val3), 21);
+ val1.set_ptr(std::move(val3.get_ptr()));
+ TS_ASSERT(val1.as<tag_int>() == 21);
+
+ TS_ASSERT_EQUALS(val1.get_type(), tag_type::Int);
+ TS_ASSERT_EQUALS(val2.get_type(), tag_type::Null);
+ TS_ASSERT_EQUALS(val3.get_type(), tag_type::Null);
+
+ val2 = val1;
+ val1 = val3;
+ TS_ASSERT(!val1 && val2 && !val3);
+ TS_ASSERT(val1.get_ptr() == nullptr);
+ TS_ASSERT(val2.get() == tag_int(21));
+ TS_ASSERT(value(val1) == val1);
+ TS_ASSERT(value(val2) == val2);
+ val1 = val1;
+ val2 = val2;
+ TS_ASSERT(!val1);
+ TS_ASSERT(val1 == value_initializer(nullptr));
+ TS_ASSERT(val2 == tag_int(21));
+
+ val3 = tag_short(2);
+ TS_ASSERT_THROWS(val3 = tag_string("foo"), std::bad_cast);
+ TS_ASSERT(val3.get() == tag_short(2));
+
+ val2.set_ptr(make_unique<tag_string>("foo"));
+ TS_ASSERT(val2 == tag_string("foo"));
+ }
+
+ void test_tag_list()
+ {
+ tag_list list;
+ TS_ASSERT_EQUALS(list.el_type(), tag_type::Null);
+ TS_ASSERT_THROWS(list.push_back(value(nullptr)), std::invalid_argument);
+
+ list.emplace_back<tag_string>("foo");
+ TS_ASSERT_EQUALS(list.el_type(), tag_type::String);
+ list.push_back(tag_string("bar"));
+ TS_ASSERT_THROWS(list.push_back(tag_int(42)), std::invalid_argument);
+ TS_ASSERT_THROWS(list.emplace_back<tag_compound>(),
+ std::invalid_argument);
+
+ TS_ASSERT((list == tag_list{"foo", "bar"}));
+ TS_ASSERT(list[0] == tag_string("foo"));
+ TS_ASSERT_EQUALS(static_cast<std::string>(list.at(1)), "bar");
+
+ TS_ASSERT_EQUALS(list.size(), 2u);
+ TS_ASSERT_THROWS(list.at(2), std::out_of_range);
+ TS_ASSERT_THROWS(list.at(-1), std::out_of_range);
+
+ list.set(1, value(tag_string("baz")));
+ TS_ASSERT_THROWS(list.set(1, value(nullptr)), std::invalid_argument);
+ TS_ASSERT_THROWS(list.set(1, value(tag_int(-42))),
+ std::invalid_argument);
+ TS_ASSERT_EQUALS(static_cast<std::string>(list[1]), "baz");
+
+ TS_ASSERT_EQUALS(list.size(), 2u);
+ tag_string values[] = {"foo", "baz"};
+ TS_ASSERT_EQUALS(list.end() - list.begin(), int(list.size()));
+ TS_ASSERT(std::equal(list.begin(), list.end(), values));
+
+ list.pop_back();
+ TS_ASSERT(list == tag_list{"foo"});
+ TS_ASSERT(list == tag_list::of<tag_string>({"foo"}));
+ TS_ASSERT(tag_list::of<tag_string>({"foo"}) == tag_list{"foo"});
+ TS_ASSERT((list != tag_list{2, 3, 5, 7}));
+
+ list.clear();
+ TS_ASSERT_EQUALS(list.size(), 0u);
+ TS_ASSERT_EQUALS(list.el_type(), tag_type::String)
+ TS_ASSERT_THROWS(list.push_back(tag_short(25)), std::invalid_argument);
+ TS_ASSERT_THROWS(list.push_back(value(nullptr)), std::invalid_argument);
+
+ list.reset();
+ TS_ASSERT_EQUALS(list.el_type(), tag_type::Null);
+ list.emplace_back<tag_int>(17);
+ TS_ASSERT_EQUALS(list.el_type(), tag_type::Int);
+
+ list.reset(tag_type::Float);
+ TS_ASSERT_EQUALS(list.el_type(), tag_type::Float);
+ list.emplace_back<tag_float>(17.0f);
+ TS_ASSERT(list == tag_list({17.0f}));
+
+ TS_ASSERT(tag_list() != tag_list(tag_type::Int));
+ TS_ASSERT(tag_list() == tag_list());
+ TS_ASSERT(tag_list(tag_type::Short) != tag_list(tag_type::Int));
+ TS_ASSERT(tag_list(tag_type::Short) == tag_list(tag_type::Short));
+
+ tag_list short_list = tag_list::of<tag_short>({25, 36});
+ TS_ASSERT_EQUALS(short_list.el_type(), tag_type::Short);
+ TS_ASSERT((short_list == tag_list{int16_t(25), int16_t(36)}));
+ TS_ASSERT((short_list != tag_list{25, 36}));
+ TS_ASSERT((short_list ==
+ tag_list{value(tag_short(25)), value(tag_short(36))}));
+
+ TS_ASSERT_THROWS((tag_list{value(tag_byte(4)), value(tag_int(5))}),
+ std::invalid_argument);
+ TS_ASSERT_THROWS((tag_list{value(nullptr), value(tag_int(6))}),
+ std::invalid_argument);
+ TS_ASSERT_THROWS(
+ (tag_list{value(tag_int(7)), value(tag_int(8)), value(nullptr)}),
+ std::invalid_argument);
+ TS_ASSERT_EQUALS((tag_list(std::initializer_list<value>{})).el_type(),
+ tag_type::Null);
+ TS_ASSERT_EQUALS((tag_list{2, 3, 5, 7}).el_type(), tag_type::Int);
+ }
+
+ void test_tag_byte_array()
+ {
+ std::vector<int8_t> vec{1, 2, 127, -128};
+ tag_byte_array arr{1, 2, 127, -128};
+ TS_ASSERT_EQUALS(arr.size(), 4u);
+ TS_ASSERT(arr.at(0) == 1 && arr[1] == 2 && arr[2] == 127 &&
+ arr.at(3) == -128);
+ TS_ASSERT_THROWS(arr.at(-1), std::out_of_range);
+ TS_ASSERT_THROWS(arr.at(4), std::out_of_range);
+
+ TS_ASSERT(arr.get() == vec);
+ TS_ASSERT(arr == tag_byte_array(std::vector<int8_t>(vec)));
+
+ arr.push_back(42);
+ vec.push_back(42);
+
+ TS_ASSERT_EQUALS(arr.size(), 5u);
+ TS_ASSERT_EQUALS(arr.end() - arr.begin(), int(arr.size()));
+ TS_ASSERT(std::equal(arr.begin(), arr.end(), vec.begin()));
+
+ arr.pop_back();
+ arr.pop_back();
+ TS_ASSERT_EQUALS(arr.size(), 3u);
+ TS_ASSERT((arr == tag_byte_array{1, 2, 127}));
+ TS_ASSERT((arr != tag_int_array{1, 2, 127}));
+ TS_ASSERT((arr != tag_long_array{1, 2, 127}));
+ TS_ASSERT((arr != tag_byte_array{1, 2, -1}));
+
+ arr.clear();
+ TS_ASSERT(arr == tag_byte_array());
+ }
+
+ void test_tag_int_array()
+ {
+ std::vector<int32_t> vec{100, 200, INT32_MAX, INT32_MIN};
+ tag_int_array arr{100, 200, INT32_MAX, INT32_MIN};
+ TS_ASSERT_EQUALS(arr.size(), 4u);
+ TS_ASSERT(arr.at(0) == 100 && arr[1] == 200 && arr[2] == INT32_MAX &&
+ arr.at(3) == INT32_MIN);
+ TS_ASSERT_THROWS(arr.at(-1), std::out_of_range);
+ TS_ASSERT_THROWS(arr.at(4), std::out_of_range);
+
+ TS_ASSERT(arr.get() == vec);
+ TS_ASSERT(arr == tag_int_array(std::vector<int32_t>(vec)));
+
+ arr.push_back(42);
+ vec.push_back(42);
+
+ TS_ASSERT_EQUALS(arr.size(), 5u);
+ TS_ASSERT_EQUALS(arr.end() - arr.begin(), int(arr.size()));
+ TS_ASSERT(std::equal(arr.begin(), arr.end(), vec.begin()));
+
+ arr.pop_back();
+ arr.pop_back();
+ TS_ASSERT_EQUALS(arr.size(), 3u);
+ TS_ASSERT((arr == tag_int_array{100, 200, INT32_MAX}));
+ TS_ASSERT((arr != tag_int_array{100, -56, -1}));
+
+ arr.clear();
+ TS_ASSERT(arr == tag_int_array());
+ }
+
+ void test_tag_long_array()
+ {
+ std::vector<int64_t> vec{100, 200, INT64_MAX, INT64_MIN};
+ tag_long_array arr{100, 200, INT64_MAX, INT64_MIN};
+ TS_ASSERT_EQUALS(arr.size(), 4u);
+ TS_ASSERT(arr.at(0) == 100 && arr[1] == 200 && arr[2] == INT64_MAX &&
+ arr.at(3) == INT64_MIN);
+ TS_ASSERT_THROWS(arr.at(-1), std::out_of_range);
+ TS_ASSERT_THROWS(arr.at(4), std::out_of_range);
+
+ TS_ASSERT(arr.get() == vec);
+ TS_ASSERT(arr == tag_long_array(std::vector<int64_t>(vec)));
+
+ arr.push_back(42);
+ vec.push_back(42);
+
+ TS_ASSERT_EQUALS(arr.size(), 5u);
+ TS_ASSERT_EQUALS(arr.end() - arr.begin(), int(arr.size()));
+ TS_ASSERT(std::equal(arr.begin(), arr.end(), vec.begin()));
+
+ arr.pop_back();
+ arr.pop_back();
+ TS_ASSERT_EQUALS(arr.size(), 3u);
+ TS_ASSERT((arr == tag_long_array{100, 200, INT64_MAX}));
+ TS_ASSERT((arr != tag_long_array{100, -56, -1}));
+
+ arr.clear();
+ TS_ASSERT(arr == tag_long_array());
+ }
+
+ void test_visitor()
+ {
+ struct : public nbt_visitor {
+ tag* visited = nullptr;
+
+ void visit(tag_byte& tag)
+ {
+ visited = &tag;
+ }
+ void visit(tag_short& tag)
+ {
+ visited = &tag;
+ }
+ void visit(tag_int& tag)
+ {
+ visited = &tag;
+ }
+ void visit(tag_long& tag)
+ {
+ visited = &tag;
+ }
+ void visit(tag_float& tag)
+ {
+ visited = &tag;
+ }
+ void visit(tag_double& tag)
+ {
+ visited = &tag;
+ }
+ void visit(tag_byte_array& tag)
+ {
+ visited = &tag;
+ }
+ void visit(tag_string& tag)
+ {
+ visited = &tag;
+ }
+ void visit(tag_list& tag)
+ {
+ visited = &tag;
+ }
+ void visit(tag_compound& tag)
+ {
+ visited = &tag;
+ }
+ void visit(tag_int_array& tag)
+ {
+ visited = &tag;
+ }
+ void visit(tag_long_array& tag)
+ {
+ visited = &tag;
+ }
+ } v;
+
+ tag_byte b;
+ b.accept(v);
+ TS_ASSERT_EQUALS(v.visited, &b);
+ tag_short s;
+ s.accept(v);
+ TS_ASSERT_EQUALS(v.visited, &s);
+ tag_int i;
+ i.accept(v);
+ TS_ASSERT_EQUALS(v.visited, &i);
+ tag_long l;
+ l.accept(v);
+ TS_ASSERT_EQUALS(v.visited, &l);
+ tag_float f;
+ f.accept(v);
+ TS_ASSERT_EQUALS(v.visited, &f);
+ tag_double d;
+ d.accept(v);
+ TS_ASSERT_EQUALS(v.visited, &d);
+ tag_byte_array ba;
+ ba.accept(v);
+ TS_ASSERT_EQUALS(v.visited, &ba);
+ tag_string st;
+ st.accept(v);
+ TS_ASSERT_EQUALS(v.visited, &st);
+ tag_list ls;
+ ls.accept(v);
+ TS_ASSERT_EQUALS(v.visited, &ls);
+ tag_compound c;
+ c.accept(v);
+ TS_ASSERT_EQUALS(v.visited, &c);
+ tag_int_array ia;
+ ia.accept(v);
+ TS_ASSERT_EQUALS(v.visited, &ia);
+ tag_long_array la;
+ la.accept(v);
+ TS_ASSERT_EQUALS(v.visited, &la);
+ }
};
diff --git a/test/read_test.h b/test/read_test.h
index d8750f5cb6..a4e74ac248 100644
--- a/test/read_test.h
+++ b/test/read_test.h
@@ -39,218 +39,242 @@ using namespace nbt;
class read_test : public CxxTest::TestSuite
{
-public:
- void test_stream_reader_big()
- {
- std::string input{
- 1, //tag_type::Byte
- 0, //tag_type::End
- 11, //tag_type::Int_Array
-
- 0x0a, 0x0b, 0x0c, 0x0d, //0x0a0b0c0d in Big Endian
-
- 0x00, 0x06, //String length in Big Endian
- 'f', 'o', 'o', 'b', 'a', 'r',
-
- 0 //tag_type::End (invalid with allow_end = false)
- };
- std::istringstream is(input);
- nbt::io::stream_reader reader(is);
-
- TS_ASSERT_EQUALS(&reader.get_istr(), &is);
- TS_ASSERT_EQUALS(reader.get_endian(), endian::big);
-
- TS_ASSERT_EQUALS(reader.read_type(), tag_type::Byte);
- TS_ASSERT_EQUALS(reader.read_type(true), tag_type::End);
- TS_ASSERT_EQUALS(reader.read_type(false), tag_type::Int_Array);
-
- int32_t i;
- reader.read_num(i);
- TS_ASSERT_EQUALS(i, 0x0a0b0c0d);
-
- TS_ASSERT_EQUALS(reader.read_string(), "foobar");
-
- TS_ASSERT_THROWS(reader.read_type(false), io::input_error);
- TS_ASSERT(!is);
- is.clear();
-
- //Test for invalid tag type 13
- is.str("\x0d");
- TS_ASSERT_THROWS(reader.read_type(), io::input_error);
- TS_ASSERT(!is);
- is.clear();
-
- //Test for unexpcted EOF on numbers (input too short for int32_t)
- is.str("\x03\x04");
- reader.read_num(i);
- TS_ASSERT(!is);
- }
-
- void test_stream_reader_little()
- {
- std::string input{
- 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, //0x0d0c0b0a09080706 in Little Endian
-
- 0x06, 0x00, //String length in Little Endian
- 'f', 'o', 'o', 'b', 'a', 'r',
-
- 0x10, 0x00, //String length (intentionally too large)
- 'a', 'b', 'c', 'd' //unexpected EOF
- };
- std::istringstream is(input);
- nbt::io::stream_reader reader(is, endian::little);
-
- TS_ASSERT_EQUALS(reader.get_endian(), endian::little);
-
- int64_t i;
- reader.read_num(i);
- TS_ASSERT_EQUALS(i, 0x0d0c0b0a09080706);
-
- TS_ASSERT_EQUALS(reader.read_string(), "foobar");
-
- TS_ASSERT_THROWS(reader.read_string(), io::input_error);
- TS_ASSERT(!is);
- }
-
- //Tests if comp equals an extended variant of Notch's bigtest NBT
- void verify_bigtest_structure(const tag_compound& comp)
- {
- TS_ASSERT_EQUALS(comp.size(), 13u);
-
- TS_ASSERT(comp.at("byteTest") == tag_byte(127));
- TS_ASSERT(comp.at("shortTest") == tag_short(32767));
- TS_ASSERT(comp.at("intTest") == tag_int(2147483647));
- TS_ASSERT(comp.at("longTest") == tag_long(9223372036854775807));
- TS_ASSERT(comp.at("floatTest") == tag_float(std::stof("0xff1832p-25"))); //0.4982315
- TS_ASSERT(comp.at("doubleTest") == tag_double(std::stod("0x1f8f6bbbff6a5ep-54"))); //0.493128713218231
-
- //From bigtest.nbt: "the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...)"
- tag_byte_array byteArrayTest;
- for(int n = 0; n < 1000; ++n)
- byteArrayTest.push_back((n*n*255 + n*7) % 100);
- TS_ASSERT(comp.at("byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...))") == byteArrayTest);
-
- TS_ASSERT(comp.at("stringTest") == tag_string("HELLO WORLD THIS IS A TEST STRING \u00C5\u00C4\u00D6!"));
-
- TS_ASSERT(comp.at("listTest (compound)") == tag_list::of<tag_compound>({
- {{"created-on", tag_long(1264099775885)}, {"name", "Compound tag #0"}},
- {{"created-on", tag_long(1264099775885)}, {"name", "Compound tag #1"}}
- }));
- TS_ASSERT(comp.at("listTest (long)") == tag_list::of<tag_long>({11, 12, 13, 14, 15}));
- TS_ASSERT(comp.at("listTest (end)") == tag_list());
-
- TS_ASSERT((comp.at("nested compound test") == tag_compound{
- {"egg", tag_compound{{"value", 0.5f}, {"name", "Eggbert"}}},
- {"ham", tag_compound{{"value", 0.75f}, {"name", "Hampus"}}}
- }));
-
- TS_ASSERT(comp.at("intArrayTest") == tag_int_array(
- {0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f}));
- }
-
- void test_read_bigtest()
- {
- //Uses an extended variant of Notch's original bigtest file
- std::string input(__binary_bigtest_uncompr_start, __binary_bigtest_uncompr_end);
- std::istringstream file(input, std::ios::binary);
-
- auto pair = nbt::io::read_compound(file);
- TS_ASSERT_EQUALS(pair.first, "Level");
- verify_bigtest_structure(*pair.second);
- }
-
- void test_read_littletest()
- {
- //Same as bigtest, but little endian
- std::string input(__binary_littletest_uncompr_start, __binary_littletest_uncompr_end);
- std::istringstream file(input, std::ios::binary);
-
- auto pair = nbt::io::read_compound(file, endian::little);
- TS_ASSERT_EQUALS(pair.first, "Level");
- TS_ASSERT_EQUALS(pair.second->get_type(), tag_type::Compound);
- verify_bigtest_structure(*pair.second);
- }
-
- void test_read_eof1()
- {
- std::string input(__binary_errortest_eof1_start, __binary_errortest_eof1_end);
- std::istringstream file(input, std::ios::binary);
- nbt::io::stream_reader reader(file);
-
- //EOF within a tag_double payload
- TS_ASSERT(file);
- TS_ASSERT_THROWS(reader.read_tag(), io::input_error);
- TS_ASSERT(!file);
- }
-
- void test_read_eof2()
- {
- std::string input(__binary_errortest_eof2_start, __binary_errortest_eof2_end);
- std::istringstream file(input, std::ios::binary);
- nbt::io::stream_reader reader(file);
-
- //EOF within a key in a compound
- TS_ASSERT(file);
- TS_ASSERT_THROWS(reader.read_tag(), io::input_error);
- TS_ASSERT(!file);
- }
-
- void test_read_errortest_noend()
- {
- std::string input(__binary_errortest_noend_start, __binary_errortest_noend_end);
- std::istringstream file(input, std::ios::binary);
- nbt::io::stream_reader reader(file);
-
- //Missing tag_end
- TS_ASSERT(file);
- TS_ASSERT_THROWS(reader.read_tag(), io::input_error);
- TS_ASSERT(!file);
- }
-
- void test_read_errortest_neg_length()
- {
- std::string input(__binary_errortest_neg_length_start, __binary_errortest_neg_length_end);
- std::istringstream file(input, std::ios::binary);
- nbt::io::stream_reader reader(file);
-
- //Negative list length
- TS_ASSERT(file);
- TS_ASSERT_THROWS(reader.read_tag(), io::input_error);
- TS_ASSERT(!file);
- }
-
- void test_read_misc()
- {
- std::string input(__binary_toplevel_string_start, __binary_toplevel_string_end);
- std::istringstream file(input, std::ios::binary);
- nbt::io::stream_reader reader(file);
-
- //Toplevel tag other than compound
- TS_ASSERT(file);
- TS_ASSERT_THROWS(reader.read_compound(), io::input_error);
- TS_ASSERT(!file);
-
- //Rewind and try again with read_tag
- file.clear();
- TS_ASSERT(file.seekg(0));
- auto pair = reader.read_tag();
- TS_ASSERT_EQUALS(pair.first, "Test (toplevel tag_string)");
- TS_ASSERT(*pair.second == tag_string(
- "Even though unprovided for by NBT, the library should also handle "
- "the case where the file consists of something else than tag_compound"));
- }
- void test_read_gzip()
- {
+ public:
+ void test_stream_reader_big()
+ {
+ std::string input{
+ 1, // tag_type::Byte
+ 0, // tag_type::End
+ 11, // tag_type::Int_Array
+
+ 0x0a, 0x0b, 0x0c, 0x0d, // 0x0a0b0c0d in Big Endian
+
+ 0x00, 0x06, // String length in Big Endian
+ 'f', 'o', 'o', 'b', 'a', 'r',
+
+ 0 // tag_type::End (invalid with allow_end = false)
+ };
+ std::istringstream is(input);
+ nbt::io::stream_reader reader(is);
+
+ TS_ASSERT_EQUALS(&reader.get_istr(), &is);
+ TS_ASSERT_EQUALS(reader.get_endian(), endian::big);
+
+ TS_ASSERT_EQUALS(reader.read_type(), tag_type::Byte);
+ TS_ASSERT_EQUALS(reader.read_type(true), tag_type::End);
+ TS_ASSERT_EQUALS(reader.read_type(false), tag_type::Int_Array);
+
+ int32_t i;
+ reader.read_num(i);
+ TS_ASSERT_EQUALS(i, 0x0a0b0c0d);
+
+ TS_ASSERT_EQUALS(reader.read_string(), "foobar");
+
+ TS_ASSERT_THROWS(reader.read_type(false), io::input_error);
+ TS_ASSERT(!is);
+ is.clear();
+
+ // Test for invalid tag type 13
+ is.str("\x0d");
+ TS_ASSERT_THROWS(reader.read_type(), io::input_error);
+ TS_ASSERT(!is);
+ is.clear();
+
+ // Test for unexpcted EOF on numbers (input too short for int32_t)
+ is.str("\x03\x04");
+ reader.read_num(i);
+ TS_ASSERT(!is);
+ }
+
+ void test_stream_reader_little()
+ {
+ std::string input{
+ 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, // 0x0d0c0b0a09080706 in Little Endian
+
+ 0x06, 0x00, // String length in Little Endian
+ 'f', 'o', 'o', 'b', 'a', 'r',
+
+ 0x10, 0x00, // String length (intentionally too large)
+ 'a', 'b', 'c', 'd' // unexpected EOF
+ };
+ std::istringstream is(input);
+ nbt::io::stream_reader reader(is, endian::little);
+
+ TS_ASSERT_EQUALS(reader.get_endian(), endian::little);
+
+ int64_t i;
+ reader.read_num(i);
+ TS_ASSERT_EQUALS(i, 0x0d0c0b0a09080706);
+
+ TS_ASSERT_EQUALS(reader.read_string(), "foobar");
+
+ TS_ASSERT_THROWS(reader.read_string(), io::input_error);
+ TS_ASSERT(!is);
+ }
+
+ // Tests if comp equals an extended variant of Notch's bigtest NBT
+ void verify_bigtest_structure(const tag_compound& comp)
+ {
+ TS_ASSERT_EQUALS(comp.size(), 13u);
+
+ TS_ASSERT(comp.at("byteTest") == tag_byte(127));
+ TS_ASSERT(comp.at("shortTest") == tag_short(32767));
+ TS_ASSERT(comp.at("intTest") == tag_int(2147483647));
+ TS_ASSERT(comp.at("longTest") == tag_long(9223372036854775807));
+ TS_ASSERT(comp.at("floatTest") ==
+ tag_float(std::stof("0xff1832p-25"))); // 0.4982315
+ TS_ASSERT(
+ comp.at("doubleTest") ==
+ tag_double(std::stod("0x1f8f6bbbff6a5ep-54"))); // 0.493128713218231
+
+ // From bigtest.nbt: "the first 1000 values of (n*n*255+n*7)%100,
+ // starting with n=0 (0, 62, 34, 16, 8, ...)"
+ tag_byte_array byteArrayTest;
+ for (int n = 0; n < 1000; ++n)
+ byteArrayTest.push_back((n * n * 255 + n * 7) % 100);
+ TS_ASSERT(
+ comp.at(
+ "byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, "
+ "starting with n=0 (0, 62, 34, 16, 8, ...))") == byteArrayTest);
+
+ TS_ASSERT(comp.at("stringTest") ==
+ tag_string(
+ "HELLO WORLD THIS IS A TEST STRING \u00C5\u00C4\u00D6!"));
+
+ TS_ASSERT(comp.at("listTest (compound)") ==
+ tag_list::of<tag_compound>(
+ {{{"created-on", tag_long(1264099775885)},
+ {"name", "Compound tag #0"}},
+ {{"created-on", tag_long(1264099775885)},
+ {"name", "Compound tag #1"}}}));
+ TS_ASSERT(comp.at("listTest (long)") ==
+ tag_list::of<tag_long>({11, 12, 13, 14, 15}));
+ TS_ASSERT(comp.at("listTest (end)") == tag_list());
+
+ TS_ASSERT(
+ (comp.at("nested compound test") ==
+ tag_compound{
+ {"egg", tag_compound{{"value", 0.5f}, {"name", "Eggbert"}}},
+ {"ham", tag_compound{{"value", 0.75f}, {"name", "Hampus"}}}}));
+
+ TS_ASSERT(
+ comp.at("intArrayTest") ==
+ tag_int_array({0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f}));
+ }
+
+ void test_read_bigtest()
+ {
+ // Uses an extended variant of Notch's original bigtest file
+ std::string input(__binary_bigtest_uncompr_start,
+ __binary_bigtest_uncompr_end);
+ std::istringstream file(input, std::ios::binary);
+
+ auto pair = nbt::io::read_compound(file);
+ TS_ASSERT_EQUALS(pair.first, "Level");
+ verify_bigtest_structure(*pair.second);
+ }
+
+ void test_read_littletest()
+ {
+ // Same as bigtest, but little endian
+ std::string input(__binary_littletest_uncompr_start,
+ __binary_littletest_uncompr_end);
+ std::istringstream file(input, std::ios::binary);
+
+ auto pair = nbt::io::read_compound(file, endian::little);
+ TS_ASSERT_EQUALS(pair.first, "Level");
+ TS_ASSERT_EQUALS(pair.second->get_type(), tag_type::Compound);
+ verify_bigtest_structure(*pair.second);
+ }
+
+ void test_read_eof1()
+ {
+ std::string input(__binary_errortest_eof1_start,
+ __binary_errortest_eof1_end);
+ std::istringstream file(input, std::ios::binary);
+ nbt::io::stream_reader reader(file);
+
+ // EOF within a tag_double payload
+ TS_ASSERT(file);
+ TS_ASSERT_THROWS(reader.read_tag(), io::input_error);
+ TS_ASSERT(!file);
+ }
+
+ void test_read_eof2()
+ {
+ std::string input(__binary_errortest_eof2_start,
+ __binary_errortest_eof2_end);
+ std::istringstream file(input, std::ios::binary);
+ nbt::io::stream_reader reader(file);
+
+ // EOF within a key in a compound
+ TS_ASSERT(file);
+ TS_ASSERT_THROWS(reader.read_tag(), io::input_error);
+ TS_ASSERT(!file);
+ }
+
+ void test_read_errortest_noend()
+ {
+ std::string input(__binary_errortest_noend_start,
+ __binary_errortest_noend_end);
+ std::istringstream file(input, std::ios::binary);
+ nbt::io::stream_reader reader(file);
+
+ // Missing tag_end
+ TS_ASSERT(file);
+ TS_ASSERT_THROWS(reader.read_tag(), io::input_error);
+ TS_ASSERT(!file);
+ }
+
+ void test_read_errortest_neg_length()
+ {
+ std::string input(__binary_errortest_neg_length_start,
+ __binary_errortest_neg_length_end);
+ std::istringstream file(input, std::ios::binary);
+ nbt::io::stream_reader reader(file);
+
+ // Negative list length
+ TS_ASSERT(file);
+ TS_ASSERT_THROWS(reader.read_tag(), io::input_error);
+ TS_ASSERT(!file);
+ }
+
+ void test_read_misc()
+ {
+ std::string input(__binary_toplevel_string_start,
+ __binary_toplevel_string_end);
+ std::istringstream file(input, std::ios::binary);
+ nbt::io::stream_reader reader(file);
+
+ // Toplevel tag other than compound
+ TS_ASSERT(file);
+ TS_ASSERT_THROWS(reader.read_compound(), io::input_error);
+ TS_ASSERT(!file);
+
+ // Rewind and try again with read_tag
+ file.clear();
+ TS_ASSERT(file.seekg(0));
+ auto pair = reader.read_tag();
+ TS_ASSERT_EQUALS(pair.first, "Test (toplevel tag_string)");
+ TS_ASSERT(*pair.second ==
+ tag_string("Even though unprovided for by NBT, the library "
+ "should also handle "
+ "the case where the file consists of something "
+ "else than tag_compound"));
+ }
+ void test_read_gzip()
+ {
#ifdef NBT_HAVE_ZLIB
- std::string input(__binary_bigtest_nbt_start, __binary_bigtest_nbt_end);
- std::istringstream file(input, std::ios::binary);
- zlib::izlibstream igzs(file);
- TS_ASSERT(file && igzs);
-
- auto pair = nbt::io::read_compound(igzs);
- TS_ASSERT(igzs);
- TS_ASSERT_EQUALS(pair.first, "Level");
- verify_bigtest_structure(*pair.second);
+ std::string input(__binary_bigtest_nbt_start, __binary_bigtest_nbt_end);
+ std::istringstream file(input, std::ios::binary);
+ zlib::izlibstream igzs(file);
+ TS_ASSERT(file && igzs);
+
+ auto pair = nbt::io::read_compound(igzs);
+ TS_ASSERT(igzs);
+ TS_ASSERT_EQUALS(pair.first, "Level");
+ verify_bigtest_structure(*pair.second);
#endif
- }
+ }
};
diff --git a/test/test_value.h b/test/test_value.h
index f63d051d28..590c5bf2cc 100644
--- a/test/test_value.h
+++ b/test/test_value.h
@@ -6,31 +6,31 @@ using namespace nbt;
class value_assignment_test : public CxxTest::TestSuite
{
-public:
- void test_numeric_assignments()
- {
- value v;
+ public:
+ void test_numeric_assignments()
+ {
+ value v;
- v = int8_t(-5);
- TS_ASSERT_EQUALS(int32_t(v), int32_t(-5));
- TS_ASSERT_EQUALS(double(v), 5.);
+ v = int8_t(-5);
+ TS_ASSERT_EQUALS(int32_t(v), int32_t(-5));
+ TS_ASSERT_EQUALS(double(v), 5.);
- v = value();
- v = int16_t(12345);
- TS_ASSERT_EQUALS(int32_t(v), int32_t(12345));
- TS_ASSERT_EQUALS(double(v), 12345.);
+ v = value();
+ v = int16_t(12345);
+ TS_ASSERT_EQUALS(int32_t(v), int32_t(12345));
+ TS_ASSERT_EQUALS(double(v), 12345.);
- v = value();
- v = int32_t(100000);
- TS_ASSERT_EQUALS(int64_t(v), int64_t(100000));
- TS_ASSERT_EQUALS(double(v), 100000.);
+ v = value();
+ v = int32_t(100000);
+ TS_ASSERT_EQUALS(int64_t(v), int64_t(100000));
+ TS_ASSERT_EQUALS(double(v), 100000.);
- v = value();
- v = float(3.14f);
- TS_ASSERT_EQUALS(double(v), 3.14);
+ v = value();
+ v = float(3.14f);
+ TS_ASSERT_EQUALS(double(v), 3.14);
- v = value();
- v = double(2.718281828);
- TS_ASSERT_EQUALS(double(v), 2.718281828);
- }
+ v = value();
+ v = double(2.718281828);
+ TS_ASSERT_EQUALS(double(v), 2.718281828);
+ }
};
diff --git a/test/write_test.h b/test/write_test.h
index 376eafa59d..fa41a6092d 100644
--- a/test/write_test.h
+++ b/test/write_test.h
@@ -41,238 +41,227 @@ using namespace nbt;
class read_test : public CxxTest::TestSuite
{
-public:
- void test_stream_writer_big()
- {
- std::ostringstream os;
- nbt::io::stream_writer writer(os);
-
- TS_ASSERT_EQUALS(&writer.get_ostr(), &os);
- TS_ASSERT_EQUALS(writer.get_endian(), endian::big);
-
- writer.write_type(tag_type::End);
- writer.write_type(tag_type::Long);
- writer.write_type(tag_type::Int_Array);
-
- writer.write_num(int64_t(0x0102030405060708));
-
- writer.write_string("foobar");
-
- TS_ASSERT(os);
- std::string expected{
- 0, //tag_type::End
- 4, //tag_type::Long
- 11, //tag_type::Int_Array
-
- 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, //0x0102030405060708 in Big Endian
-
- 0x00, 0x06, //string length in Big Endian
- 'f', 'o', 'o', 'b', 'a', 'r'
- };
- TS_ASSERT_EQUALS(os.str(), expected);
-
- //too long for NBT
- TS_ASSERT_THROWS(writer.write_string(std::string(65536, '.')), std::length_error);
- TS_ASSERT(!os);
- }
-
- void test_stream_writer_little()
- {
- std::ostringstream os;
- nbt::io::stream_writer writer(os, endian::little);
-
- TS_ASSERT_EQUALS(writer.get_endian(), endian::little);
-
- writer.write_num(int32_t(0x0a0b0c0d));
-
- writer.write_string("foobar");
-
- TS_ASSERT(os);
- std::string expected{
- 0x0d, 0x0c, 0x0b, 0x0a, //0x0a0b0c0d in Little Endian
-
- 0x06, 0x00, //string length in Little Endian
- 'f', 'o', 'o', 'b', 'a', 'r'
- };
- TS_ASSERT_EQUALS(os.str(), expected);
-
- TS_ASSERT_THROWS(writer.write_string(std::string(65536, '.')), std::length_error);
- TS_ASSERT(!os);
- }
-
- void test_write_payload_big()
- {
- std::ostringstream os;
- nbt::io::stream_writer writer(os);
-
- //tag_primitive
- writer.write_payload(tag_byte(127));
- writer.write_payload(tag_short(32767));
- writer.write_payload(tag_int(2147483647));
- writer.write_payload(tag_long(9223372036854775807));
-
- //Same values as in endian_str_test
- writer.write_payload(tag_float(std::stof("-0xCDEF01p-63")));
- writer.write_payload(tag_double(std::stod("-0x1DEF0102030405p-375")));
-
- TS_ASSERT_EQUALS(os.str(), (std::string{
- '\x7F',
- '\x7F', '\xFF',
- '\x7F', '\xFF', '\xFF', '\xFF',
- '\x7F', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF',
-
- '\xAB', '\xCD', '\xEF', '\x01',
- '\xAB', '\xCD', '\xEF', '\x01', '\x02', '\x03', '\x04', '\x05'
- }));
- os.str(""); //clear and reuse the stream
-
- //tag_string
- writer.write_payload(tag_string("barbaz"));
- TS_ASSERT_EQUALS(os.str(), (std::string{
- 0x00, 0x06, //string length in Big Endian
- 'b', 'a', 'r', 'b', 'a', 'z'
- }));
- TS_ASSERT_THROWS(writer.write_payload(tag_string(std::string(65536, '.'))), std::length_error);
- TS_ASSERT(!os);
- os.clear();
-
- //tag_byte_array
- os.str("");
- writer.write_payload(tag_byte_array{0, 1, 127, -128, -127});
- TS_ASSERT_EQUALS(os.str(), (std::string{
- 0x00, 0x00, 0x00, 0x05, //length in Big Endian
- 0, 1, 127, -128, -127
- }));
- os.str("");
-
- //tag_int_array
- writer.write_payload(tag_int_array{0x01020304, 0x05060708, 0x090a0b0c});
- TS_ASSERT_EQUALS(os.str(), (std::string{
- 0x00, 0x00, 0x00, 0x03, //length in Big Endian
- 0x01, 0x02, 0x03, 0x04,
- 0x05, 0x06, 0x07, 0x08,
- 0x09, 0x0a, 0x0b, 0x0c
- }));
- os.str("");
-
- //tag_list
- writer.write_payload(tag_list()); //empty list with undetermined type, should be written as list of tag_end
- writer.write_payload(tag_list(tag_type::Int)); //empty list of tag_int
- writer.write_payload(tag_list{ //nested list
- tag_list::of<tag_short>({0x3456, 0x789a}),
- tag_list::of<tag_byte>({0x0a, 0x0b, 0x0c, 0x0d})
- });
- TS_ASSERT_EQUALS(os.str(), (std::string{
- 0, //tag_type::End
- 0x00, 0x00, 0x00, 0x00, //length
-
- 3, //tag_type::Int
- 0x00, 0x00, 0x00, 0x00, //length
-
- 9, //tag_type::List
- 0x00, 0x00, 0x00, 0x02, //length
- //list 0
- 2, //tag_type::Short
- 0x00, 0x00, 0x00, 0x02, //length
- '\x34', '\x56',
- '\x78', '\x9a',
- //list 1
- 1, //tag_type::Byte
- 0x00, 0x00, 0x00, 0x04, //length
- 0x0a,
- 0x0b,
- 0x0c,
- 0x0d
- }));
- os.str("");
-
- //tag_compound
- /* Testing if writing compounds works properly is problematic because the
- order of the tags is not guaranteed. However with only two tags in a
- compound we only have two possible orderings.
- See below for a more thorough test that uses writing and re-reading. */
- writer.write_payload(tag_compound{});
- writer.write_payload(tag_compound{
- {"foo", "quux"},
- {"bar", tag_int(0x789abcde)}
- });
-
- std::string endtag{0x00};
- std::string subtag1{
- 8, //tag_type::String
- 0x00, 0x03, //key length
- 'f', 'o', 'o',
- 0x00, 0x04, //string length
- 'q', 'u', 'u', 'x'
- };
- std::string subtag2{
- 3, //tag_type::Int
- 0x00, 0x03, //key length
- 'b', 'a', 'r',
- '\x78', '\x9A', '\xBC', '\xDE'
- };
-
- TS_ASSERT(os.str() == endtag + subtag1 + subtag2 + endtag
- || os.str() == endtag + subtag2 + subtag1 + endtag);
-
- //Now for write_tag:
- os.str("");
- writer.write_tag("foo", tag_string("quux"));
- TS_ASSERT_EQUALS(os.str(), subtag1);
- TS_ASSERT(os);
-
- //too long key for NBT
- TS_ASSERT_THROWS(writer.write_tag(std::string(65536, '.'), tag_long(-1)), std::length_error);
- TS_ASSERT(!os);
- }
-
- void test_write_bigtest()
- {
- /* Like already stated above, because no order is guaranteed for
- tag_compound, we cannot simply test it by writing into a stream and directly
- comparing the output to a reference value.
- Instead, we assume that reading already works correctly and re-read the
- written tag.
- Smaller-grained tests are already done above. */
- std::string input(__binary_bigtest_uncompr_start, __binary_bigtest_uncompr_end);
- std::istringstream file(input, std::ios::binary);
-
- const auto orig_pair = io::read_compound(file);
- std::stringstream sstr;
-
- //Write into stream in Big Endian
- io::write_tag(orig_pair.first, *orig_pair.second, sstr);
- TS_ASSERT(sstr);
-
- //Read from stream in Big Endian and compare
- auto written_pair = io::read_compound(sstr);
- TS_ASSERT_EQUALS(orig_pair.first, written_pair.first);
- TS_ASSERT(*orig_pair.second == *written_pair.second);
-
- sstr.str(""); //Reset and reuse stream
- //Write into stream in Little Endian
- io::write_tag(orig_pair.first, *orig_pair.second, sstr, endian::little);
- TS_ASSERT(sstr);
-
- //Read from stream in Little Endian and compare
- written_pair = io::read_compound(sstr, endian::little);
- TS_ASSERT_EQUALS(orig_pair.first, written_pair.first);
- TS_ASSERT(*orig_pair.second == *written_pair.second);
+ public:
+ void test_stream_writer_big()
+ {
+ std::ostringstream os;
+ nbt::io::stream_writer writer(os);
+
+ TS_ASSERT_EQUALS(&writer.get_ostr(), &os);
+ TS_ASSERT_EQUALS(writer.get_endian(), endian::big);
+
+ writer.write_type(tag_type::End);
+ writer.write_type(tag_type::Long);
+ writer.write_type(tag_type::Int_Array);
+
+ writer.write_num(int64_t(0x0102030405060708));
+
+ writer.write_string("foobar");
+
+ TS_ASSERT(os);
+ std::string expected{0, // tag_type::End
+ 4, // tag_type::Long
+ 11, // tag_type::Int_Array
+
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, // 0x0102030405060708 in Big Endian
+
+ 0x00, 0x06, // string length in Big Endian
+ 'f', 'o', 'o', 'b', 'a', 'r'};
+ TS_ASSERT_EQUALS(os.str(), expected);
+
+ // too long for NBT
+ TS_ASSERT_THROWS(writer.write_string(std::string(65536, '.')),
+ std::length_error);
+ TS_ASSERT(!os);
+ }
+
+ void test_stream_writer_little()
+ {
+ std::ostringstream os;
+ nbt::io::stream_writer writer(os, endian::little);
+
+ TS_ASSERT_EQUALS(writer.get_endian(), endian::little);
+
+ writer.write_num(int32_t(0x0a0b0c0d));
+
+ writer.write_string("foobar");
+
+ TS_ASSERT(os);
+ std::string expected{
+ 0x0d, 0x0c, 0x0b, 0x0a, // 0x0a0b0c0d in Little Endian
+
+ 0x06, 0x00, // string length in Little Endian
+ 'f', 'o', 'o', 'b', 'a', 'r'};
+ TS_ASSERT_EQUALS(os.str(), expected);
+
+ TS_ASSERT_THROWS(writer.write_string(std::string(65536, '.')),
+ std::length_error);
+ TS_ASSERT(!os);
+ }
+
+ void test_write_payload_big()
+ {
+ std::ostringstream os;
+ nbt::io::stream_writer writer(os);
+
+ // tag_primitive
+ writer.write_payload(tag_byte(127));
+ writer.write_payload(tag_short(32767));
+ writer.write_payload(tag_int(2147483647));
+ writer.write_payload(tag_long(9223372036854775807));
+
+ // Same values as in endian_str_test
+ writer.write_payload(tag_float(std::stof("-0xCDEF01p-63")));
+ writer.write_payload(tag_double(std::stod("-0x1DEF0102030405p-375")));
+
+ TS_ASSERT_EQUALS(
+ os.str(),
+ (std::string{'\x7F', '\x7F', '\xFF', '\x7F', '\xFF', '\xFF',
+ '\xFF', '\x7F', '\xFF', '\xFF', '\xFF', '\xFF',
+ '\xFF', '\xFF', '\xFF',
+
+ '\xAB', '\xCD', '\xEF', '\x01', '\xAB', '\xCD',
+ '\xEF', '\x01', '\x02', '\x03', '\x04', '\x05'}));
+ os.str(""); // clear and reuse the stream
+
+ // tag_string
+ writer.write_payload(tag_string("barbaz"));
+ TS_ASSERT_EQUALS(os.str(),
+ (std::string{0x00, 0x06, // string length in Big Endian
+ 'b', 'a', 'r', 'b', 'a', 'z'}));
+ TS_ASSERT_THROWS(
+ writer.write_payload(tag_string(std::string(65536, '.'))),
+ std::length_error);
+ TS_ASSERT(!os);
+ os.clear();
+
+ // tag_byte_array
+ os.str("");
+ writer.write_payload(tag_byte_array{0, 1, 127, -128, -127});
+ TS_ASSERT_EQUALS(os.str(), (std::string{0x00, 0x00, 0x00,
+ 0x05, // length in Big Endian
+ 0, 1, 127, -128, -127}));
+ os.str("");
+
+ // tag_int_array
+ writer.write_payload(tag_int_array{0x01020304, 0x05060708, 0x090a0b0c});
+ TS_ASSERT_EQUALS(
+ os.str(),
+ (std::string{0x00, 0x00, 0x00, 0x03, // length in Big Endian
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c}));
+ os.str("");
+
+ // tag_list
+ writer.write_payload(
+ tag_list()); // empty list with undetermined type, should be written
+ // as list of tag_end
+ writer.write_payload(tag_list(tag_type::Int)); // empty list of tag_int
+ writer.write_payload(
+ tag_list{// nested list
+ tag_list::of<tag_short>({0x3456, 0x789a}),
+ tag_list::of<tag_byte>({0x0a, 0x0b, 0x0c, 0x0d})});
+ TS_ASSERT_EQUALS(os.str(),
+ (std::string{0, // tag_type::End
+ 0x00, 0x00, 0x00, 0x00, // length
+
+ 3, // tag_type::Int
+ 0x00, 0x00, 0x00, 0x00, // length
+
+ 9, // tag_type::List
+ 0x00, 0x00, 0x00, 0x02, // length
+ // list 0
+ 2, // tag_type::Short
+ 0x00, 0x00, 0x00, 0x02, // length
+ '\x34', '\x56', '\x78', '\x9a',
+ // list 1
+ 1, // tag_type::Byte
+ 0x00, 0x00, 0x00, 0x04, // length
+ 0x0a, 0x0b, 0x0c, 0x0d}));
+ os.str("");
+
+ // tag_compound
+ /* Testing if writing compounds works properly is problematic because
+ the order of the tags is not guaranteed. However with only two tags in a
+ compound we only have two possible orderings.
+ See below for a more thorough test that uses writing and re-reading. */
+ writer.write_payload(tag_compound{});
+ writer.write_payload(
+ tag_compound{{"foo", "quux"}, {"bar", tag_int(0x789abcde)}});
+
+ std::string endtag{0x00};
+ std::string subtag1{8, // tag_type::String
+ 0x00, 0x03, // key length
+ 'f', 'o', 'o', 0x00, 0x04, // string length
+ 'q', 'u', 'u', 'x'};
+ std::string subtag2{3, // tag_type::Int
+ 0x00, 0x03, // key length
+ 'b', 'a', 'r', '\x78', '\x9A', '\xBC', '\xDE'};
+
+ TS_ASSERT(os.str() == endtag + subtag1 + subtag2 + endtag ||
+ os.str() == endtag + subtag2 + subtag1 + endtag);
+
+ // Now for write_tag:
+ os.str("");
+ writer.write_tag("foo", tag_string("quux"));
+ TS_ASSERT_EQUALS(os.str(), subtag1);
+ TS_ASSERT(os);
+
+ // too long key for NBT
+ TS_ASSERT_THROWS(
+ writer.write_tag(std::string(65536, '.'), tag_long(-1)),
+ std::length_error);
+ TS_ASSERT(!os);
+ }
+
+ void test_write_bigtest()
+ {
+ /* Like already stated above, because no order is guaranteed for
+ tag_compound, we cannot simply test it by writing into a stream and
+ directly comparing the output to a reference value. Instead, we assume
+ that reading already works correctly and re-read the written tag.
+ Smaller-grained tests are already done above. */
+ std::string input(__binary_bigtest_uncompr_start,
+ __binary_bigtest_uncompr_end);
+ std::istringstream file(input, std::ios::binary);
+
+ const auto orig_pair = io::read_compound(file);
+ std::stringstream sstr;
+
+ // Write into stream in Big Endian
+ io::write_tag(orig_pair.first, *orig_pair.second, sstr);
+ TS_ASSERT(sstr);
+
+ // Read from stream in Big Endian and compare
+ auto written_pair = io::read_compound(sstr);
+ TS_ASSERT_EQUALS(orig_pair.first, written_pair.first);
+ TS_ASSERT(*orig_pair.second == *written_pair.second);
+
+ sstr.str(""); // Reset and reuse stream
+ // Write into stream in Little Endian
+ io::write_tag(orig_pair.first, *orig_pair.second, sstr, endian::little);
+ TS_ASSERT(sstr);
+
+ // Read from stream in Little Endian and compare
+ written_pair = io::read_compound(sstr, endian::little);
+ TS_ASSERT_EQUALS(orig_pair.first, written_pair.first);
+ TS_ASSERT(*orig_pair.second == *written_pair.second);
#ifdef NBT_HAVE_ZLIB
- //Now with gzip compression
- sstr.str("");
- zlib::ozlibstream ogzs(sstr, -1, true);
- io::write_tag(orig_pair.first, *orig_pair.second, ogzs);
- ogzs.close();
- TS_ASSERT(ogzs);
- TS_ASSERT(sstr);
- //Read and compare
- zlib::izlibstream igzs(sstr);
- written_pair = io::read_compound(igzs);
- TS_ASSERT(igzs);
- TS_ASSERT_EQUALS(orig_pair.first, written_pair.first);
- TS_ASSERT(*orig_pair.second == *written_pair.second);
+ // Now with gzip compression
+ sstr.str("");
+ zlib::ozlibstream ogzs(sstr, -1, true);
+ io::write_tag(orig_pair.first, *orig_pair.second, ogzs);
+ ogzs.close();
+ TS_ASSERT(ogzs);
+ TS_ASSERT(sstr);
+ // Read and compare
+ zlib::izlibstream igzs(sstr);
+ written_pair = io::read_compound(igzs);
+ TS_ASSERT(igzs);
+ TS_ASSERT_EQUALS(orig_pair.first, written_pair.first);
+ TS_ASSERT(*orig_pair.second == *written_pair.second);
#endif
- }
+ }
};
diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h
index 67a608e2ad..c96797ce90 100644
--- a/test/zlibstream_test.h
+++ b/test/zlibstream_test.h
@@ -35,249 +35,260 @@ using namespace zlib;
class zlibstream_test : public CxxTest::TestSuite
{
-private:
- std::string bigtest;
+ private:
+ std::string bigtest;
-public:
- zlibstream_test()
- {
- std::string input(__binary_bigtest_uncompr_start, __binary_bigtest_uncompr_end);
- std::istringstream bigtest_f(input, std::ios::binary);
- std::stringbuf bigtest_b;
- bigtest_f >> &bigtest_b;
- bigtest = bigtest_b.str();
- if(!bigtest_f || bigtest.size() == 0)
- throw std::runtime_error("Could not read bigtest_uncompr file");
- }
+ public:
+ zlibstream_test()
+ {
+ std::string input(__binary_bigtest_uncompr_start,
+ __binary_bigtest_uncompr_end);
+ std::istringstream bigtest_f(input, std::ios::binary);
+ std::stringbuf bigtest_b;
+ bigtest_f >> &bigtest_b;
+ bigtest = bigtest_b.str();
+ if (!bigtest_f || bigtest.size() == 0)
+ throw std::runtime_error("Could not read bigtest_uncompr file");
+ }
- void test_inflate_gzip()
- {
- std::string input(__binary_bigtest_nbt_start, __binary_bigtest_nbt_end);
- std::istringstream gzip_in(input, std::ios::binary);
- TS_ASSERT(gzip_in);
+ void test_inflate_gzip()
+ {
+ std::string input(__binary_bigtest_nbt_start, __binary_bigtest_nbt_end);
+ std::istringstream gzip_in(input, std::ios::binary);
+ TS_ASSERT(gzip_in);
- std::stringbuf data;
- //Small buffer so not all fits at once (the compressed file is 561 bytes)
- {
- izlibstream igzs(gzip_in, 256);
- igzs.exceptions(std::ios::failbit | std::ios::badbit);
- TS_ASSERT(igzs.good());
+ std::stringbuf data;
+ // Small buffer so not all fits at once (the compressed file is 561
+ // bytes)
+ {
+ izlibstream igzs(gzip_in, 256);
+ igzs.exceptions(std::ios::failbit | std::ios::badbit);
+ TS_ASSERT(igzs.good());
- TS_ASSERT_THROWS_NOTHING(igzs >> &data);
- TS_ASSERT(igzs);
- TS_ASSERT(igzs.eof());
- TS_ASSERT_EQUALS(data.str(), bigtest);
- }
+ TS_ASSERT_THROWS_NOTHING(igzs >> &data);
+ TS_ASSERT(igzs);
+ TS_ASSERT(igzs.eof());
+ TS_ASSERT_EQUALS(data.str(), bigtest);
+ }
- //Clear and reuse buffers
- data.str("");
- gzip_in.clear();
- gzip_in.seekg(0);
- //Now try the same with larger buffer (but not large enough for all output, uncompressed size 1561 bytes)
- {
- izlibstream igzs(gzip_in, 1000);
- igzs.exceptions(std::ios::failbit | std::ios::badbit);
- TS_ASSERT(igzs.good());
+ // Clear and reuse buffers
+ data.str("");
+ gzip_in.clear();
+ gzip_in.seekg(0);
+ // Now try the same with larger buffer (but not large enough for all
+ // output, uncompressed size 1561 bytes)
+ {
+ izlibstream igzs(gzip_in, 1000);
+ igzs.exceptions(std::ios::failbit | std::ios::badbit);
+ TS_ASSERT(igzs.good());
- TS_ASSERT_THROWS_NOTHING(igzs >> &data);
- TS_ASSERT(igzs);
- TS_ASSERT(igzs.eof());
- TS_ASSERT_EQUALS(data.str(), bigtest);
- }
+ TS_ASSERT_THROWS_NOTHING(igzs >> &data);
+ TS_ASSERT(igzs);
+ TS_ASSERT(igzs.eof());
+ TS_ASSERT_EQUALS(data.str(), bigtest);
+ }
- data.str("");
- gzip_in.clear();
- gzip_in.seekg(0);
- //Now with large buffer
- {
- izlibstream igzs(gzip_in, 4000);
- igzs.exceptions(std::ios::failbit | std::ios::badbit);
- TS_ASSERT(igzs.good());
+ data.str("");
+ gzip_in.clear();
+ gzip_in.seekg(0);
+ // Now with large buffer
+ {
+ izlibstream igzs(gzip_in, 4000);
+ igzs.exceptions(std::ios::failbit | std::ios::badbit);
+ TS_ASSERT(igzs.good());
- TS_ASSERT_THROWS_NOTHING(igzs >> &data);
- TS_ASSERT(igzs);
- TS_ASSERT(igzs.eof());
- TS_ASSERT_EQUALS(data.str(), bigtest);
- }
- }
+ TS_ASSERT_THROWS_NOTHING(igzs >> &data);
+ TS_ASSERT(igzs);
+ TS_ASSERT(igzs.eof());
+ TS_ASSERT_EQUALS(data.str(), bigtest);
+ }
+ }
- void test_inflate_zlib()
- {
- std::string input(__binary_bigtest_zlib_start, __binary_bigtest_zlib_end);
- std::istringstream zlib_in(input, std::ios::binary);
- TS_ASSERT(zlib_in);
+ void test_inflate_zlib()
+ {
+ std::string input(__binary_bigtest_zlib_start,
+ __binary_bigtest_zlib_end);
+ std::istringstream zlib_in(input, std::ios::binary);
+ TS_ASSERT(zlib_in);
- std::stringbuf data;
- izlibstream izls(zlib_in, 256);
- izls.exceptions(std::ios::failbit | std::ios::badbit);
- TS_ASSERT(izls.good());
+ std::stringbuf data;
+ izlibstream izls(zlib_in, 256);
+ izls.exceptions(std::ios::failbit | std::ios::badbit);
+ TS_ASSERT(izls.good());
- TS_ASSERT_THROWS_NOTHING(izls >> &data);
- TS_ASSERT(izls);
- TS_ASSERT(izls.eof());
- TS_ASSERT_EQUALS(data.str(), bigtest);
- }
+ TS_ASSERT_THROWS_NOTHING(izls >> &data);
+ TS_ASSERT(izls);
+ TS_ASSERT(izls.eof());
+ TS_ASSERT_EQUALS(data.str(), bigtest);
+ }
- void test_inflate_corrupt()
- {
- std::string input(__binary_bigtest_corrupt_nbt_start, __binary_bigtest_corrupt_nbt_end);
- std::istringstream gzip_in(input, std::ios::binary);
- TS_ASSERT(gzip_in);
+ void test_inflate_corrupt()
+ {
+ std::string input(__binary_bigtest_corrupt_nbt_start,
+ __binary_bigtest_corrupt_nbt_end);
+ std::istringstream gzip_in(input, std::ios::binary);
+ TS_ASSERT(gzip_in);
- std::vector<char> buf(bigtest.size());
- izlibstream igzs(gzip_in);
- igzs.exceptions(std::ios::failbit | std::ios::badbit);
- TS_ASSERT_THROWS(igzs.read(buf.data(), buf.size()), zlib_error);
- TS_ASSERT(igzs.bad());
- }
+ std::vector<char> buf(bigtest.size());
+ izlibstream igzs(gzip_in);
+ igzs.exceptions(std::ios::failbit | std::ios::badbit);
+ TS_ASSERT_THROWS(igzs.read(buf.data(), buf.size()), zlib_error);
+ TS_ASSERT(igzs.bad());
+ }
- void test_inflate_eof()
- {
- std::string input(__binary_bigtest_eof_nbt_start, __binary_bigtest_eof_nbt_end);
- std::istringstream gzip_in(input, std::ios::binary);
- TS_ASSERT(gzip_in);
+ void test_inflate_eof()
+ {
+ std::string input(__binary_bigtest_eof_nbt_start,
+ __binary_bigtest_eof_nbt_end);
+ std::istringstream gzip_in(input, std::ios::binary);
+ TS_ASSERT(gzip_in);
- std::vector<char> buf(bigtest.size());
- izlibstream igzs(gzip_in);
- igzs.exceptions(std::ios::failbit | std::ios::badbit);
- TS_ASSERT_THROWS(igzs.read(buf.data(), buf.size()), zlib_error);
- TS_ASSERT(igzs.bad());
- }
+ std::vector<char> buf(bigtest.size());
+ izlibstream igzs(gzip_in);
+ igzs.exceptions(std::ios::failbit | std::ios::badbit);
+ TS_ASSERT_THROWS(igzs.read(buf.data(), buf.size()), zlib_error);
+ TS_ASSERT(igzs.bad());
+ }
- void test_inflate_trailing()
- {
- //This file contains additional uncompressed data after the zlib-compressed data
- std::string input(__binary_trailing_data_zlib_start, __binary_trailing_data_zlib_end);
- std::istringstream file(input, std::ios::binary);
- izlibstream izls(file, 32);
- TS_ASSERT(file && izls);
+ void test_inflate_trailing()
+ {
+ // This file contains additional uncompressed data after the
+ // zlib-compressed data
+ std::string input(__binary_trailing_data_zlib_start,
+ __binary_trailing_data_zlib_end);
+ std::istringstream file(input, std::ios::binary);
+ izlibstream izls(file, 32);
+ TS_ASSERT(file && izls);
- std::string str;
- izls >> str;
- TS_ASSERT(izls);
- TS_ASSERT(izls.eof());
- TS_ASSERT_EQUALS(str, "foobar");
+ std::string str;
+ izls >> str;
+ TS_ASSERT(izls);
+ TS_ASSERT(izls.eof());
+ TS_ASSERT_EQUALS(str, "foobar");
- //Now read the uncompressed data
- TS_ASSERT(file);
- TS_ASSERT(!file.eof());
- file >> str;
- TS_ASSERT(!file.bad());
- TS_ASSERT_EQUALS(str, "barbaz");
- }
+ // Now read the uncompressed data
+ TS_ASSERT(file);
+ TS_ASSERT(!file.eof());
+ file >> str;
+ TS_ASSERT(!file.bad());
+ TS_ASSERT_EQUALS(str, "barbaz");
+ }
- void test_deflate_zlib()
- {
- //Here we assume that inflating works and has already been tested
- std::stringstream str;
- std::stringbuf output;
- //Small buffer
- {
- ozlibstream ozls(str, -1, false, 256);
- ozls.exceptions(std::ios::failbit | std::ios::badbit);
- TS_ASSERT_THROWS_NOTHING(ozls << bigtest);
- TS_ASSERT(ozls.good());
- TS_ASSERT_THROWS_NOTHING(ozls.close());
- TS_ASSERT(ozls.good());
- }
- TS_ASSERT(str.good());
- {
- izlibstream izls(str);
- TS_ASSERT_THROWS_NOTHING(izls >> &output);
- TS_ASSERT(izls);
- }
- TS_ASSERT_EQUALS(output.str(), bigtest);
+ void test_deflate_zlib()
+ {
+ // Here we assume that inflating works and has already been tested
+ std::stringstream str;
+ std::stringbuf output;
+ // Small buffer
+ {
+ ozlibstream ozls(str, -1, false, 256);
+ ozls.exceptions(std::ios::failbit | std::ios::badbit);
+ TS_ASSERT_THROWS_NOTHING(ozls << bigtest);
+ TS_ASSERT(ozls.good());
+ TS_ASSERT_THROWS_NOTHING(ozls.close());
+ TS_ASSERT(ozls.good());
+ }
+ TS_ASSERT(str.good());
+ {
+ izlibstream izls(str);
+ TS_ASSERT_THROWS_NOTHING(izls >> &output);
+ TS_ASSERT(izls);
+ }
+ TS_ASSERT_EQUALS(output.str(), bigtest);
- str.clear(); str.str("");
- output.str("");
- //Medium sized buffer
- //Write first half, then flush and write second half
- {
- ozlibstream ozls(str, 9, false, 512);
- ozls.exceptions(std::ios::failbit | std::ios::badbit);
+ str.clear();
+ str.str("");
+ output.str("");
+ // Medium sized buffer
+ // Write first half, then flush and write second half
+ {
+ ozlibstream ozls(str, 9, false, 512);
+ ozls.exceptions(std::ios::failbit | std::ios::badbit);
- std::string half1 = bigtest.substr(0, bigtest.size()/2);
- std::string half2 = bigtest.substr(bigtest.size()/2);
- TS_ASSERT_THROWS_NOTHING(ozls << half1 << std::flush << half2);
- TS_ASSERT(ozls.good());
- TS_ASSERT_THROWS_NOTHING(ozls.close());
- TS_ASSERT(ozls.good());
- }
- TS_ASSERT(str.good());
- {
- izlibstream izls(str);
- izls >> &output;
- TS_ASSERT(izls);
- }
- TS_ASSERT_EQUALS(output.str(), bigtest);
+ std::string half1 = bigtest.substr(0, bigtest.size() / 2);
+ std::string half2 = bigtest.substr(bigtest.size() / 2);
+ TS_ASSERT_THROWS_NOTHING(ozls << half1 << std::flush << half2);
+ TS_ASSERT(ozls.good());
+ TS_ASSERT_THROWS_NOTHING(ozls.close());
+ TS_ASSERT(ozls.good());
+ }
+ TS_ASSERT(str.good());
+ {
+ izlibstream izls(str);
+ izls >> &output;
+ TS_ASSERT(izls);
+ }
+ TS_ASSERT_EQUALS(output.str(), bigtest);
- str.clear(); str.str("");
- output.str("");
- //Large buffer
- {
- ozlibstream ozls(str, 1, false, 4000);
- ozls.exceptions(std::ios::failbit | std::ios::badbit);
- TS_ASSERT_THROWS_NOTHING(ozls << bigtest);
- TS_ASSERT(ozls.good());
- TS_ASSERT_THROWS_NOTHING(ozls.close());
- TS_ASSERT_THROWS_NOTHING(ozls.close()); //closing twice shouldn't be a problem
- TS_ASSERT(ozls.good());
- }
- TS_ASSERT(str.good());
- {
- izlibstream izls(str);
- izls >> &output;
- TS_ASSERT(izls);
- }
- TS_ASSERT_EQUALS(output.str(), bigtest);
- }
+ str.clear();
+ str.str("");
+ output.str("");
+ // Large buffer
+ {
+ ozlibstream ozls(str, 1, false, 4000);
+ ozls.exceptions(std::ios::failbit | std::ios::badbit);
+ TS_ASSERT_THROWS_NOTHING(ozls << bigtest);
+ TS_ASSERT(ozls.good());
+ TS_ASSERT_THROWS_NOTHING(ozls.close());
+ TS_ASSERT_THROWS_NOTHING(
+ ozls.close()); // closing twice shouldn't be a problem
+ TS_ASSERT(ozls.good());
+ }
+ TS_ASSERT(str.good());
+ {
+ izlibstream izls(str);
+ izls >> &output;
+ TS_ASSERT(izls);
+ }
+ TS_ASSERT_EQUALS(output.str(), bigtest);
+ }
- void test_deflate_gzip()
- {
- std::stringstream str;
- std::stringbuf output;
- {
- ozlibstream ozls(str, -1, true);
- ozls.exceptions(std::ios::failbit | std::ios::badbit);
- TS_ASSERT_THROWS_NOTHING(ozls << bigtest);
- TS_ASSERT(ozls.good());
- TS_ASSERT_THROWS_NOTHING(ozls.close());
- TS_ASSERT(ozls.good());
- }
- TS_ASSERT(str.good());
- {
- izlibstream izls(str);
- izls >> &output;
- TS_ASSERT(izls);
- }
- TS_ASSERT_EQUALS(output.str(), bigtest);
- }
+ void test_deflate_gzip()
+ {
+ std::stringstream str;
+ std::stringbuf output;
+ {
+ ozlibstream ozls(str, -1, true);
+ ozls.exceptions(std::ios::failbit | std::ios::badbit);
+ TS_ASSERT_THROWS_NOTHING(ozls << bigtest);
+ TS_ASSERT(ozls.good());
+ TS_ASSERT_THROWS_NOTHING(ozls.close());
+ TS_ASSERT(ozls.good());
+ }
+ TS_ASSERT(str.good());
+ {
+ izlibstream izls(str);
+ izls >> &output;
+ TS_ASSERT(izls);
+ }
+ TS_ASSERT_EQUALS(output.str(), bigtest);
+ }
- void test_deflate_closed()
- {
- std::stringstream str;
- {
- ozlibstream ozls(str);
- ozls.exceptions(std::ios::failbit | std::ios::badbit);
- TS_ASSERT_THROWS_NOTHING(ozls << bigtest);
- TS_ASSERT_THROWS_NOTHING(ozls.close());
- TS_ASSERT_THROWS_NOTHING(ozls << "foo");
- TS_ASSERT_THROWS_ANYTHING(ozls.close());
- TS_ASSERT(ozls.bad());
- TS_ASSERT(!str);
- }
- str.clear();
- str.seekp(0);
- {
- ozlibstream ozls(str);
- //this time without exceptions
- TS_ASSERT_THROWS_NOTHING(ozls << bigtest);
- TS_ASSERT_THROWS_NOTHING(ozls.close());
- TS_ASSERT_THROWS_NOTHING(ozls << "foo" << std::flush);
- TS_ASSERT(ozls.bad());
- TS_ASSERT_THROWS_NOTHING(ozls.close());
- TS_ASSERT(ozls.bad());
- TS_ASSERT(!str);
- }
- }
+ void test_deflate_closed()
+ {
+ std::stringstream str;
+ {
+ ozlibstream ozls(str);
+ ozls.exceptions(std::ios::failbit | std::ios::badbit);
+ TS_ASSERT_THROWS_NOTHING(ozls << bigtest);
+ TS_ASSERT_THROWS_NOTHING(ozls.close());
+ TS_ASSERT_THROWS_NOTHING(ozls << "foo");
+ TS_ASSERT_THROWS_ANYTHING(ozls.close());
+ TS_ASSERT(ozls.bad());
+ TS_ASSERT(!str);
+ }
+ str.clear();
+ str.seekp(0);
+ {
+ ozlibstream ozls(str);
+ // this time without exceptions
+ TS_ASSERT_THROWS_NOTHING(ozls << bigtest);
+ TS_ASSERT_THROWS_NOTHING(ozls.close());
+ TS_ASSERT_THROWS_NOTHING(ozls << "foo" << std::flush);
+ TS_ASSERT(ozls.bad());
+ TS_ASSERT_THROWS_NOTHING(ozls.close());
+ TS_ASSERT(ozls.bad());
+ TS_ASSERT(!str);
+ }
+ }
};