diff options
| author | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 17:35:01 +0300 |
|---|---|---|
| committer | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 17:35:01 +0300 |
| commit | 1a0ffe372f4da8408c5d08a36013536a3396b9e6 (patch) | |
| tree | 440f2a9a325c9ab4cb5b16dc6a8193317cabf8d7 /include | |
| parent | f4d8ea0fa76174843adf7f77ebad0ad17d2377ed (diff) | |
| download | Project-Tick-1a0ffe372f4da8408c5d08a36013536a3396b9e6.tar.gz Project-Tick-1a0ffe372f4da8408c5d08a36013536a3396b9e6.zip | |
NOISSUE reformat libnbtplusplus to new MeshMC clang format rules
Signed-off-by: Mehmet Samet Duman <yongdohyun@projecttick.org>
Diffstat (limited to 'include')
| -rw-r--r-- | include/crtp_tag.h | 71 | ||||
| -rw-r--r-- | include/endian_str.h | 132 | ||||
| -rw-r--r-- | include/io/izlibstream.h | 120 | ||||
| -rw-r--r-- | include/io/ozlibstream.h | 131 | ||||
| -rw-r--r-- | include/io/stream_reader.h | 213 | ||||
| -rw-r--r-- | include/io/stream_writer.h | 195 | ||||
| -rw-r--r-- | include/io/zlib_streambuf.h | 63 | ||||
| -rw-r--r-- | include/make_unique.h | 14 | ||||
| -rw-r--r-- | include/nbt_visitor.h | 94 | ||||
| -rw-r--r-- | include/primitive_detail.h | 48 | ||||
| -rw-r--r-- | include/tag.h | 274 | ||||
| -rw-r--r-- | include/tag_array.h | 467 | ||||
| -rw-r--r-- | include/tag_compound.h | 257 | ||||
| -rw-r--r-- | include/tag_list.h | 416 | ||||
| -rw-r--r-- | include/tag_primitive.h | 170 | ||||
| -rw-r--r-- | include/tag_string.h | 101 | ||||
| -rw-r--r-- | include/tagfwd.h | 32 | ||||
| -rw-r--r-- | include/text/json_formatter.h | 31 | ||||
| -rw-r--r-- | include/value.h | 408 | ||||
| -rw-r--r-- | include/value_initializer.h | 72 |
20 files changed, 1814 insertions, 1495 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 |
