From af7c4d99882e02b24698d3a2beac36f637f674c7 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Fri, 19 Jun 2015 22:28:45 +0200 Subject: Initial commit --- .gitattributes | 2 ++ .gitignore | 9 +++++++++ README.md | 12 ++++++++++++ 3 files changed, 23 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 README.md diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..dfe0770424 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..6c2e80ec8d --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.cbp +*.depend +*.layout +*.cbtemp +/bin +/lib +/obj +/build +/doxygen/html diff --git a/README.md b/README.md new file mode 100644 index 0000000000..477ab72e15 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# libnbt++ 2 (WIP) + +libnbt++ is a free C++ library for Minecraft's file format Named Binary Tag +(NBT). It can read and write compressed and uncompressed NBT files and +provides a code interface for working with NBT data. + +---------- + +libnbt++2 is a remake of the old libnbt++ library with the goal of making it +more easily usable and fixing some problems. The old libnbt++ especially +suffered from a very convoluted syntax and boilerplate code needed to work +with NBT data. -- cgit 0.0.5-2-1-g0f52 From 8458a8d07f5990c33c15acfa2ac316b0a9d6ede0 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sat, 20 Jun 2015 14:56:29 +0200 Subject: Add a Product Requirements Document --- Requirements.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Requirements.md diff --git a/Requirements.md b/Requirements.md new file mode 100644 index 0000000000..d869e3cecc --- /dev/null +++ b/Requirements.md @@ -0,0 +1,53 @@ +# libnbt++2 Product Requirements Document + +### Purpose +Provide a C++ interface for working with NBT data, particularly originating +from Minecraft. + +### Scope +External Minecraft utilities that read or manipulate parts of savefiles, +such as: +- (Graphical) NBT editors +- Inventory editors +- Tools for reading or changing world metadata +- Map editors and visualizers + +### Definitions, Acronyms and Abbreviations +- **libnbt++1:** The predecessor of libnbt++2. +- **Minecraft:** A sandbox voxel world game written in Java, developed by + Mojang. +- **Mojang's implementation:** The NBT library written in Java that Mojang + uses in Minecraft. +- **NBT:** Named Binary Tag. A binary serialization format used by Minecraft. +- **Tag:** A data unit in NBT. Can be a number, string, array, list or + compound. + +### Product Functions +- /RF10/ Reading and writing NBT data files/streams with or without + compression. +- /RF20/ Representing NBT data in memory and allowing programs to read or + manipulate it in all the ways that Mojang's implementation and libnbt++1 + provide. +- /RF30/ A shorter syntax than in libnbt++1 and preferrably also Mojang's + implementation. +- /RF40/ The need for insecure operations and manual memory management should + be minimized; references and `std::unique_ptr` should be preferred before + raw pointers. +- /RF50/ Move semantics are preferred before copy semantics. + +### Product Performance +- /RP10/ All operations on (not too large) NBT data should not be slower + than their counterparts in Mojang's implementation. +- /RP20/ The library must be able to handle all possible NBT data that + Mojang's implementation can create and handle. +- /RP30/ Often used operations on large Lists, Compounds and Arrays must + be of at most O(log n) time complexity if reasonable. Other operations + should be at most O(n). + +### Quality Requirements +- Functionality: good +- Reliability: normal +- Usability: very good +- Efficiency: good +- Changeability: normal +- Transferability: normal -- cgit 0.0.5-2-1-g0f52 From 483714e26ef6fca97353ca34654e5588bc61dda0 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 21 Jun 2015 10:55:43 +0200 Subject: Add tag.h --- .gitignore | 2 +- CMakeLists.txt | 12 ++++++++++++ include/tag.h | 37 +++++++++++++++++++++++++++++++++++++ test/CMakeLists.txt | 6 ++++++ test/microtest.h | 14 ++++++++++++++ test/nbttest.cpp | 9 +++++++++ 6 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 CMakeLists.txt create mode 100644 include/tag.h create mode 100644 test/CMakeLists.txt create mode 100644 test/microtest.h create mode 100644 test/nbttest.cpp diff --git a/.gitignore b/.gitignore index 6c2e80ec8d..a7f172c739 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ /lib /obj /build -/doxygen/html +/doxygen diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000..9f105284ea --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 2.6) +project(libnbt++) +set(libnbt++_VERSION_MAJOR 2) +set(libnbt++_VERSION_MINOR 0) + +add_definitions(-std=c++11) +include_directories(include) +add_library(nbt++ STATIC + ) + +enable_testing() +add_subdirectory(test) diff --git a/include/tag.h b/include/tag.h new file mode 100644 index 0000000000..a17406ff2b --- /dev/null +++ b/include/tag.h @@ -0,0 +1,37 @@ +#ifndef TAG_H_INCLUDED +#define TAG_H_INCLUDED + +#include + +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 +}; + +/** Base class for all tag classes */ +class tag +{ +public: + virtual ~tag() noexcept {} + + virtual tag_type get_type() const noexcept = 0; +}; + +} + +#endif // TAG_H_INCLUDED diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000000..04672d2666 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,6 @@ +enable_testing() +include_directories(${libnbt++_SOURCE_DIR}/include) + +add_executable(nbttest nbttest.cpp) +target_link_libraries(nbttest nbt++) +add_test(nbttest nbttest) diff --git a/test/microtest.h b/test/microtest.h new file mode 100644 index 0000000000..8691ebb748 --- /dev/null +++ b/test/microtest.h @@ -0,0 +1,14 @@ +#ifndef MICROTEST_H +#define MICROTEST_H + +#include + +#define FAIL_TEST { std::cerr << "Assertion failed at " __FILE__ ":" << __LINE__ << std::endl; \ + exit(EXIT_FAILURE); } +#define ASSERT(expr) { if(!(expr)) FAIL_TEST } +#define EXPECT_EXCEPTION(expr, type) { \ + try { (expr); std::cerr << "Expected " #type " to be thrown" << std::endl; FAIL_TEST } \ + catch(type&) {} \ + catch(...) { std::cerr << "Expected " #type " to be thrown" << std::endl; FAIL_TEST } } + +#endif diff --git a/test/nbttest.cpp b/test/nbttest.cpp new file mode 100644 index 0000000000..eead43efd0 --- /dev/null +++ b/test/nbttest.cpp @@ -0,0 +1,9 @@ +#include "microtest.h" +#include "tag.h" +#include +#include + +int main() +{ + return EXIT_SUCCESS; +} -- cgit 0.0.5-2-1-g0f52 From 1a85198341c0f54493039c62b67e864d086c56af Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 21 Jun 2015 12:00:40 +0200 Subject: Add tag_primitive --- include/libnbt.h | 6 ++++ include/primitive_detail.h | 22 ++++++++++++++ include/tag.h | 6 ++-- include/tag_primitive.h | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 include/libnbt.h create mode 100644 include/primitive_detail.h create mode 100644 include/tag_primitive.h diff --git a/include/libnbt.h b/include/libnbt.h new file mode 100644 index 0000000000..19d5de77e4 --- /dev/null +++ b/include/libnbt.h @@ -0,0 +1,6 @@ +#ifndef LIBNBT_H_INCLUDED +#define LIBNBT_H_INCLUDED + +#include "tag_primitive.h" + +#endif // LIBNBT_H_INCLUDED diff --git a/include/primitive_detail.h b/include/primitive_detail.h new file mode 100644 index 0000000000..2930da43c8 --- /dev/null +++ b/include/primitive_detail.h @@ -0,0 +1,22 @@ +#ifndef PRIMITIVE_DETAIL_H_INCLUDED +#define PRIMITIVE_DETAIL_H_INCLUDED + +namespace nbt +{ + +namespace detail +{ + ///Meta-struct that holds the tag_type value for a specific primitive type + template struct get_primitive_type; + + template<> struct get_primitive_type : public std::integral_constant {}; + template<> struct get_primitive_type : public std::integral_constant {}; + template<> struct get_primitive_type : public std::integral_constant {}; + template<> struct get_primitive_type : public std::integral_constant {}; + template<> struct get_primitive_type : public std::integral_constant {}; + template<> struct get_primitive_type : public std::integral_constant {}; +} + +} + +#endif // PRIMITIVE_DETAIL_H_INCLUDED diff --git a/include/tag.h b/include/tag.h index a17406ff2b..c65292c436 100644 --- a/include/tag.h +++ b/include/tag.h @@ -6,7 +6,7 @@ namespace nbt { -/** Tag type values used in the binary format */ +///Tag type values used in the binary format enum class tag_type : int8_t { End = 0, @@ -23,12 +23,14 @@ enum class tag_type : int8_t Int_Array = 11 }; -/** Base class for all tag classes */ +///Base class for all NBT tag classes class tag { public: + //Virtual destructor virtual ~tag() noexcept {} + ///Returns the type of the tag virtual tag_type get_type() const noexcept = 0; }; diff --git a/include/tag_primitive.h b/include/tag_primitive.h new file mode 100644 index 0000000000..f5caef143b --- /dev/null +++ b/include/tag_primitive.h @@ -0,0 +1,72 @@ +#ifndef TAG_PRIMITIVE_H_INCLUDED +#define TAG_PRIMITIVE_H_INCLUDED + +#include "tag.h" +#include "primitive_detail.h" +#include + +namespace nbt +{ + +/** + * @brief 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 tag_primitive : public tag +{ +public: + ///The type of the value + typedef T value_type; + + ///The type of the tag + static constexpr tag_type type = detail::get_primitive_type::value; + + tag_primitive(T value = 0); + + tag_primitive& operator=(T value); + + operator T() const; + + tag_type get_type() const noexcept override; + +private: + T value; +}; + +//Typedefs that should be used instead of the template tag_primitive. +typedef tag_primitive tag_byte; +typedef tag_primitive tag_short; +typedef tag_primitive tag_int; +typedef tag_primitive tag_long; +typedef tag_primitive tag_float; +typedef tag_primitive tag_double; + +template +tag_primitive::tag_primitive(T val): + value(val) +{} + +template +tag_primitive& tag_primitive::operator=(T val) +{ + value = val; + return *this; +} + +template +tag_primitive::operator T() const +{ + return value; +} + +template +tag_type tag_primitive::get_type() const noexcept +{ + return type; +} + +} + +#endif // TAG_PRIMITIVE_H_INCLUDED -- cgit 0.0.5-2-1-g0f52 From 47fefee88e83425b9098293fa07877f7d41c18d6 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 21 Jun 2015 12:16:40 +0200 Subject: Add tests for get_type --- test/microtest.h | 2 +- test/nbttest.cpp | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/test/microtest.h b/test/microtest.h index 8691ebb748..ec9309f8be 100644 --- a/test/microtest.h +++ b/test/microtest.h @@ -1,7 +1,7 @@ #ifndef MICROTEST_H #define MICROTEST_H -#include +#include #define FAIL_TEST { std::cerr << "Assertion failed at " __FILE__ ":" << __LINE__ << std::endl; \ exit(EXIT_FAILURE); } diff --git a/test/nbttest.cpp b/test/nbttest.cpp index eead43efd0..23e6a1a80b 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -1,9 +1,20 @@ #include "microtest.h" -#include "tag.h" -#include +#include "libnbt.h" #include +using namespace nbt; + +void test_get_type() +{ + ASSERT(tag_byte().get_type() == tag_type::Byte); + ASSERT(tag_short().get_type() == tag_type::Short); + ASSERT(tag_int().get_type() == tag_type::Int); + ASSERT(tag_long().get_type() == tag_type::Long); + ASSERT(tag_float().get_type() == tag_type::Float); + ASSERT(tag_double().get_type() == tag_type::Double); +} + int main() { - return EXIT_SUCCESS; + test_get_type(); } -- cgit 0.0.5-2-1-g0f52 From d5739acc664d1b877c8f48e17046fd5e825a4059 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 23 Jun 2015 14:30:42 +0200 Subject: Add GNU LGPL --- COPYING | 674 +++++++++++++++++++++++++++++++++++++++++++++ COPYING.LESSER | 165 +++++++++++ include/libnbt.h | 19 ++ include/primitive_detail.h | 19 ++ include/tag.h | 19 ++ include/tag_primitive.h | 19 ++ test/microtest.h | 19 ++ test/nbttest.cpp | 19 ++ 8 files changed, 953 insertions(+) create mode 100644 COPYING create mode 100644 COPYING.LESSER diff --git a/COPYING b/COPYING new file mode 100644 index 0000000000..94a9ed024d --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/COPYING.LESSER b/COPYING.LESSER new file mode 100644 index 0000000000..65c5ca88a6 --- /dev/null +++ b/COPYING.LESSER @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/include/libnbt.h b/include/libnbt.h index 19d5de77e4..7eb0d1341f 100644 --- a/include/libnbt.h +++ b/include/libnbt.h @@ -1,3 +1,22 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ #ifndef LIBNBT_H_INCLUDED #define LIBNBT_H_INCLUDED diff --git a/include/primitive_detail.h b/include/primitive_detail.h index 2930da43c8..9d6ed95b03 100644 --- a/include/primitive_detail.h +++ b/include/primitive_detail.h @@ -1,3 +1,22 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ #ifndef PRIMITIVE_DETAIL_H_INCLUDED #define PRIMITIVE_DETAIL_H_INCLUDED diff --git a/include/tag.h b/include/tag.h index c65292c436..56d3594474 100644 --- a/include/tag.h +++ b/include/tag.h @@ -1,3 +1,22 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ #ifndef TAG_H_INCLUDED #define TAG_H_INCLUDED diff --git a/include/tag_primitive.h b/include/tag_primitive.h index f5caef143b..3b8ac51237 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -1,3 +1,22 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ #ifndef TAG_PRIMITIVE_H_INCLUDED #define TAG_PRIMITIVE_H_INCLUDED diff --git a/test/microtest.h b/test/microtest.h index ec9309f8be..084a85496c 100644 --- a/test/microtest.h +++ b/test/microtest.h @@ -1,3 +1,22 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ #ifndef MICROTEST_H #define MICROTEST_H diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 23e6a1a80b..93a31c0375 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -1,3 +1,22 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ #include "microtest.h" #include "libnbt.h" #include -- cgit 0.0.5-2-1-g0f52 From 669a1c4b24d3993dc29917daa60bb46dd9e4fd3b Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 23 Jun 2015 21:14:19 +0200 Subject: Add missing include --- include/primitive_detail.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/primitive_detail.h b/include/primitive_detail.h index 9d6ed95b03..b8c41a9675 100644 --- a/include/primitive_detail.h +++ b/include/primitive_detail.h @@ -20,6 +20,8 @@ #ifndef PRIMITIVE_DETAIL_H_INCLUDED #define PRIMITIVE_DETAIL_H_INCLUDED +#include + namespace nbt { -- cgit 0.0.5-2-1-g0f52 From 50d94e7d513f68d25cd5e72de880bb6f78de50e7 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 23 Jun 2015 21:33:17 +0200 Subject: Add methods and tests to tag_primitive --- include/tag_primitive.h | 21 +++++++++++++++++++++ test/nbttest.cpp | 17 +++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/include/tag_primitive.h b/include/tag_primitive.h index 3b8ac51237..397ea64da3 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -45,8 +45,11 @@ public: tag_primitive(T value = 0); tag_primitive& operator=(T value); + void set(T value); + operator T&(); operator T() const; + T get() const; tag_type get_type() const noexcept override; @@ -74,12 +77,30 @@ tag_primitive& tag_primitive::operator=(T val) return *this; } +template +void tag_primitive::set(T val) +{ + value = val; +} + +template +tag_primitive::operator T&() +{ + return value; +} + template tag_primitive::operator T() const { return value; } +template +T tag_primitive::get() const +{ + return value; +} + template tag_type tag_primitive::get_type() const noexcept { diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 93a31c0375..482e28ab64 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -33,7 +33,24 @@ void test_get_type() ASSERT(tag_double().get_type() == tag_type::Double); } +void test_tag_primitive() +{ + tag_int tag(6); + ASSERT(tag.get() == 6); + int& ref = tag; + ref = 12; + ASSERT(tag == 12); + ASSERT(tag != 6); + tag.set(24); + ASSERT(ref == 24); + tag = 7; + ASSERT(7 == static_cast(tag)); + + ASSERT(tag_double() == 0.0); +} + int main() { test_get_type(); + test_tag_primitive(); } -- cgit 0.0.5-2-1-g0f52 From 2b3f7e4b90b5787dbe8b4b40dcfc806bcb3f014e Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 23 Jun 2015 22:14:00 +0200 Subject: Create tag_string.h --- include/libnbt.h | 1 + include/tag_primitive.h | 6 ++--- include/tag_string.h | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ test/nbttest.cpp | 21 ++++++++++++++++++ 4 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 include/tag_string.h diff --git a/include/libnbt.h b/include/libnbt.h index 7eb0d1341f..4105be7560 100644 --- a/include/libnbt.h +++ b/include/libnbt.h @@ -21,5 +21,6 @@ #define LIBNBT_H_INCLUDED #include "tag_primitive.h" +#include "tag_string.h" #endif // LIBNBT_H_INCLUDED diff --git a/include/tag_primitive.h b/include/tag_primitive.h index 397ea64da3..f5fab1bdf9 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -44,13 +44,13 @@ public: tag_primitive(T value = 0); - tag_primitive& operator=(T value); - void set(T value); - operator T&(); operator T() const; T get() const; + tag_primitive& operator=(T value); + void set(T value); + tag_type get_type() const noexcept override; private: diff --git a/include/tag_string.h b/include/tag_string.h new file mode 100644 index 0000000000..1e4c76ae6c --- /dev/null +++ b/include/tag_string.h @@ -0,0 +1,59 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#ifndef TAG_STRING_H_INCLUDED +#define TAG_STRING_H_INCLUDED + +#include "tag.h" +#include + +namespace nbt +{ + +///Contains a UTF-8 string +class tag_string : public tag +{ +public: + ///The type of the tag + static constexpr tag_type type = tag_type::String; + + tag_string(const std::string& str); + tag_string(std::string&& str = ""); + + bool operator==(const std::string& rhs) const; + bool operator!=(const std::string& rhs) const; + + operator std::string&(); + operator const std::string&() const; + const std::string& get() const; + + tag_string& operator=(const std::string& str); + tag_string& operator=(std::string&& str); + void set(const std::string& str); + void set(std::string&& str); + + tag_type get_type() const noexcept override; + +private: + std::string value; +}; + +} + +#endif // TAG_STRING_H_INCLUDED diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 482e28ab64..24846e84da 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -49,8 +49,29 @@ void test_tag_primitive() ASSERT(tag_double() == 0.0); } +void test_tag_string() +{ + tag_string tag("foo"); + ASSERT(tag.get() == "foo"); + std::string& ref = tag; + ref = "bar"; + ASSERT(tag == "bar"); + ASSERT(tag != "foo"); + tag.set("baz"); + ASSERT(ref == "baz"); + tag = "quux"; + ASSERT("quux" == static_cast(tag)); + std::string str("foo"); + tag = str; + ASSERT(tag == str); + + ASSERT(tag_string(str) == "foo"); + ASSERT(tag_string() == ""); +} + int main() { test_get_type(); test_tag_primitive(); + test_tag_string(); } -- cgit 0.0.5-2-1-g0f52 From 748c9ed7f0a05b21df831ac09171b44902460ba8 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 23 Jun 2015 22:31:15 +0200 Subject: Add to test_get_type --- test/nbttest.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 24846e84da..ea4ad85629 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -25,12 +25,17 @@ using namespace nbt; void test_get_type() { - ASSERT(tag_byte().get_type() == tag_type::Byte); - ASSERT(tag_short().get_type() == tag_type::Short); - ASSERT(tag_int().get_type() == tag_type::Int); - ASSERT(tag_long().get_type() == tag_type::Long); - ASSERT(tag_float().get_type() == tag_type::Float); - ASSERT(tag_double().get_type() == tag_type::Double); + ASSERT(tag_byte().get_type() == tag_type::Byte); + ASSERT(tag_short().get_type() == tag_type::Short); + ASSERT(tag_int().get_type() == tag_type::Int); + ASSERT(tag_long().get_type() == tag_type::Long); + ASSERT(tag_float().get_type() == tag_type::Float); + ASSERT(tag_double().get_type() == tag_type::Double); + //ASSERT(tag_byte_array().get_type() == tag_type::Byte_Array); + ASSERT(tag_string().get_type() == tag_type::String); + //ASSERT(tag_list().get_type() == tag_type::List); + //ASSERT(tag_compound().get_type() == tag_type::Compound); + //ASSERT(tag_int_array().get_type() == tag_type::Int_Array); } void test_tag_primitive() -- cgit 0.0.5-2-1-g0f52 From de01e4f26fc400b4194319ccf985cc5714bee1fe Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 25 Jun 2015 23:13:50 +0200 Subject: Create preliminary tag_compound --- include/libnbt.h | 1 + include/tag_compound.h | 136 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 include/tag_compound.h diff --git a/include/libnbt.h b/include/libnbt.h index 4105be7560..9d873254f6 100644 --- a/include/libnbt.h +++ b/include/libnbt.h @@ -22,5 +22,6 @@ #include "tag_primitive.h" #include "tag_string.h" +#include "tag_compound.h" #endif // LIBNBT_H_INCLUDED diff --git a/include/tag_compound.h b/include/tag_compound.h new file mode 100644 index 0000000000..7d369da65f --- /dev/null +++ b/include/tag_compound.h @@ -0,0 +1,136 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#ifndef TAG_COMPOUND_H_INCLUDED +#define TAG_COMPOUND_H_INCLUDED + +#include "tag.h" +#include +#include +#include + +namespace nbt +{ + +//TODO: Create actual proxy class +typedef tag& tag_ref_proxy; +typedef const tag& const_tag_ref_proxy; + +///Tag that contains multiple unordered named tags of arbitrary types +class tag_compound : public tag +{ +public: + //Iterator types + typedef void* iterator; //FIXME + typedef const void* const_iterator; + + ///The type of the tag + static constexpr tag_type type = tag_type::Compound; + + ///Constructs an empty compound + tag_compound() {} + + /** + * @brief Accesses a tag by key with bounds checking + * + * Returns a reference 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 + */ + tag& at(const std::string& key); + const tag& at(const std::string& key) const; + + /** + * @brief Accesses a tag by key + * + * Returns a proxy value that can be converted to \c nbt::tag&. + */ + tag_ref_proxy operator[](const std::string& key); + const_tag_ref_proxy operator[](const std::string& key) const; + + /** + * @brief Inserts a tag into the compound + * + * If the given key does not already exist, moves the pointed tag + * into the compound. + * @return true if the tag was inserted + */ + bool insert(const std::string& key, std::unique_ptr&& ptr); + + /** + * @brief Inserts or assigns a tag + * + * If the given key already exists, assigns the pointed tag to it. + * Otherwise, it is inserted under the given key. + * @return true if the key did not exist + */ + bool put(const std::string& key, std::unique_ptr&& ptr); + + /** + * @brief Constructs and inserts a tag into the compound + * + * If the given key does not exist, constructs a new tag of type \c T + * with the given args and inserts it into the compound. + * @return true if the tag was inserted + */ + template + bool emplace(const std::string& key, Args&&... args); + + /** + * @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. + * @return true if the key did not already exist. + */ + template + bool emplace_put(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); + + ///Returns the number of tags in the compound + size_t size() const; + + ///Erases all tags from the compound + void clear(); + + //Iterators + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + const_iterator cbegin() const; + const_iterator cend() const; + + tag_type get_type() const noexcept override; + +private: + +}; + +} + +#endif // TAG_COMPOUND_H_INCLUDED -- cgit 0.0.5-2-1-g0f52 From c91712ffecaf41a76df1f0909e26638e916b1d48 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 25 Jun 2015 23:14:09 +0200 Subject: Minor comment changes --- include/tag_primitive.h | 2 +- include/tag_string.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/tag_primitive.h b/include/tag_primitive.h index f5fab1bdf9..5704da4430 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -28,7 +28,7 @@ namespace nbt { /** - * @brief Contains an integral or floating-point 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. */ diff --git a/include/tag_string.h b/include/tag_string.h index 1e4c76ae6c..28d1b77085 100644 --- a/include/tag_string.h +++ b/include/tag_string.h @@ -26,7 +26,7 @@ namespace nbt { -///Contains a UTF-8 string +///Tag that contains a UTF-8 string class tag_string : public tag { public: -- cgit 0.0.5-2-1-g0f52 From 944e66a76521452607d2b2e2bbcd3fe700eec126 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Fri, 26 Jun 2015 10:44:05 +0200 Subject: Make doxygen ignore the details --- include/primitive_detail.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/primitive_detail.h b/include/primitive_detail.h index b8c41a9675..c7e2a05206 100644 --- a/include/primitive_detail.h +++ b/include/primitive_detail.h @@ -22,6 +22,7 @@ #include +///@cond namespace nbt { @@ -39,5 +40,6 @@ namespace detail } } +///@endcond #endif // PRIMITIVE_DETAIL_H_INCLUDED -- cgit 0.0.5-2-1-g0f52 From c455aeed051a1843cb0cc8907fb534f6cca72698 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Fri, 26 Jun 2015 11:08:52 +0200 Subject: Use Javadoc-like tags everywhere --- include/tag_compound.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/tag_compound.h b/include/tag_compound.h index 7d369da65f..2333713dc5 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -59,7 +59,7 @@ public: /** * @brief Accesses a tag by key * - * Returns a proxy value that can be converted to \c nbt::tag&. + * Returns a proxy value that can be converted to @ref tag&. */ tag_ref_proxy operator[](const std::string& key); const_tag_ref_proxy operator[](const std::string& key) const; @@ -85,7 +85,7 @@ public: /** * @brief Constructs and inserts a tag into the compound * - * If the given key does not exist, constructs a new tag of type \c T + * If the given key does not exist, constructs a new tag of type @c T * with the given args and inserts it into the compound. * @return true if the tag was inserted */ @@ -95,7 +95,7 @@ public: /** * @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 + * Constructs a new tag of type @c T with the given args and inserts * or assigns it to the given key. * @return true if the key did not already exist. */ -- cgit 0.0.5-2-1-g0f52 From b600712fad86f186730ff4f9e6a52dde959187ce Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sat, 27 Jun 2015 00:10:21 +0200 Subject: Create preliminary tag_compound tests not working yet, the class design needs to be fleshed out first --- test/nbttest.cpp | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index ea4ad85629..326ddb66de 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -34,7 +34,7 @@ void test_get_type() //ASSERT(tag_byte_array().get_type() == tag_type::Byte_Array); ASSERT(tag_string().get_type() == tag_type::String); //ASSERT(tag_list().get_type() == tag_type::List); - //ASSERT(tag_compound().get_type() == tag_type::Compound); + ASSERT(tag_compound().get_type() == tag_type::Compound); //ASSERT(tag_int_array().get_type() == tag_type::Int_Array); } @@ -74,9 +74,39 @@ void test_tag_string() ASSERT(tag_string() == ""); } +void test_tag_compound() +{ + //Preliminary + //Doesn't work yet, but this is the syntax I would like to have: + tag_compound comp{{"foo", int16_t(12)}, {"bar", "baz"}, {"baz", -2.0}}; + + ASSERT(comp["foo"].get_type() == tag_type::Short); + ASSERT((int)comp["foo"] == 12); + ASSERT((int16_t)comp["foo"] == int16_t(12)); + ASSERT(comp["foo"] == int8_t(12)); + ASSERT(comp["foo"] == 12); + ASSERT(comp["foo"] != "12"); + + ASSERT(comp["bar"].get_type() == tag_type::String); + ASSERT((std::string)comp["bar"] == "baz"); + ASSERT(comp["bar"] == "baz"); + ASSERT(comp["bar"] != 0); + + ASSERT(comp["baz"].get_type() == tag_type::Double); + ASSERT((double)comp["baz"] == -2.0); + ASSERT(comp["baz"] == -2.0f); + ASSERT(comp["baz"] == -2); + ASSERT(comp["baz"] != "-2"); + + comp["quux"] = tag_compound{{"Hello", "World"}}; + ASSERT(comp["quux"].get_type() == tag_type::Compound); + ASSERT(comp["quux"]["Hello"] == "World"); +} + int main() { test_get_type(); test_tag_primitive(); test_tag_string(); + test_tag_compound(); } -- cgit 0.0.5-2-1-g0f52 From 34ac833956f32e3041919123fe41077eedd5e632 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sat, 27 Jun 2015 22:28:07 +0200 Subject: Create nbt_value and further refine tag_compound --- include/nbt_value.h | 107 +++++++++++++++++++++++++++++++++++++++++++++++++ include/tag_compound.h | 59 +++++---------------------- test/nbttest.cpp | 32 ++++++++++----- 3 files changed, 141 insertions(+), 57 deletions(-) create mode 100644 include/nbt_value.h diff --git a/include/nbt_value.h b/include/nbt_value.h new file mode 100644 index 0000000000..5af322b4d2 --- /dev/null +++ b/include/nbt_value.h @@ -0,0 +1,107 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#ifndef TAG_REF_PROXY_H_INCLUDED +#define TAG_REF_PROXY_H_INCLUDED + +#include "tag.h" +#include +#include + +namespace nbt +{ + +//Forward declarations +class tag_compound; +class tag_list; + +/** + * @brief Represents a reference to a tag of fixed type + * + * This can contain a tag of arbitrary type, but once the tag's type + * is set it is immutable. + * It can be casted to or assigned values, but if the types do not match it + * will throw an exception. + * It can also refer to an uninitialized value (e.g. when using tag_compound::operator[] + * with a non-existant key). + */ +class nbt_value +{ +public: + //Movable but not (implicitly) copyable + nbt_value& operator=(nbt_value&&) = default; + nbt_value& operator=(const nbt_value&) = delete; + + //nbt_value& operator=(std::unique_ptr&& ptr); + + //Assignment + /** + * @brief Assigns the given value to the tag if the type matches + * @throw std::bad_cast if the value is not convertible to the tag type + * via a widening conversion + */ + nbt_value& operator=(int8_t val); + nbt_value& operator=(int16_t val); + nbt_value& operator=(int32_t val); + nbt_value& operator=(int64_t val); + nbt_value& operator=(float val); + nbt_value& operator=(double val); + nbt_value& operator=(const std::string& str); + nbt_value& operator=(tag_compound&& comp); + nbt_value& operator=(tag_list&& list); + + //Conversion to tag + operator tag&(); + operator const tag&() const; + + //Conversions to primitives and string + /** + * @brief Casts the value if the type matches + * @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; + explicit operator const std::string&() const; + + /** + * @brief In case of a tag_compound, accesses a tag by key + * @throw std::bad_cast if the tag type is not tag_compound + * @sa tag_compound::operator[] + */ + nbt_value& operator[](const std::string& key); + const nbt_value& operator[](const std::string& key) const; + + ///@sa tag::get_type + tag_type get_type() const; + + friend bool operator==(const nbt_value& lhs, const nbt_value& rhs); + friend bool operator!=(const nbt_value& lhs, const nbt_value& rhs); + +private: + std::unique_ptr value; +}; + +} + +#endif // TAG_REF_PROXY_H_INCLUDED diff --git a/include/tag_compound.h b/include/tag_compound.h index 2333713dc5..8fdc2f6617 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -21,6 +21,7 @@ #define TAG_COMPOUND_H_INCLUDED #include "tag.h" +#include "nbt_value.h" #include #include #include @@ -28,10 +29,6 @@ namespace nbt { -//TODO: Create actual proxy class -typedef tag& tag_ref_proxy; -typedef const tag& const_tag_ref_proxy; - ///Tag that contains multiple unordered named tags of arbitrary types class tag_compound : public tag { @@ -46,61 +43,27 @@ public: ///Constructs an empty compound tag_compound() {} + //TODO: Make a separate class similar to and convertible to nbt_value for initializing tag values + //tag_compound(std::initializer_list> init); + /** * @brief Accesses a tag by key with bounds checking * - * Returns a reference to the tag with the specified key, or throws an + * Returns a nbt_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 */ - tag& at(const std::string& key); - const tag& at(const std::string& key) const; + nbt_value& at(const std::string& key); + const nbt_value& at(const std::string& key) const; /** * @brief Accesses a tag by key * - * Returns a proxy value that can be converted to @ref tag&. - */ - tag_ref_proxy operator[](const std::string& key); - const_tag_ref_proxy operator[](const std::string& key) const; - - /** - * @brief Inserts a tag into the compound - * - * If the given key does not already exist, moves the pointed tag - * into the compound. - * @return true if the tag was inserted - */ - bool insert(const std::string& key, std::unique_ptr&& ptr); - - /** - * @brief Inserts or assigns a tag - * - * If the given key already exists, assigns the pointed tag to it. - * Otherwise, it is inserted under the given key. - * @return true if the key did not exist - */ - bool put(const std::string& key, std::unique_ptr&& ptr); - - /** - * @brief Constructs and inserts a tag into the compound - * - * If the given key does not exist, constructs a new tag of type @c T - * with the given args and inserts it into the compound. - * @return true if the tag was inserted - */ - template - bool emplace(const std::string& key, Args&&... args); - - /** - * @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. - * @return true if the key did not already exist. + * If the key exists, returns a nbt_value to the corresponding tag. + * Else, a new uninitalized entry is created under this key. */ - template - bool emplace_put(const std::string& key, Args&&... args); + nbt_value& operator[](const std::string& key); + const nbt_value& operator[](const std::string& key) const; /** * @brief Erases a tag from the compound diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 326ddb66de..b77c9b85bf 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -78,29 +78,43 @@ void test_tag_compound() { //Preliminary //Doesn't work yet, but this is the syntax I would like to have: - tag_compound comp{{"foo", int16_t(12)}, {"bar", "baz"}, {"baz", -2.0}}; + tag_compound comp/*{ + {"foo", int16_t(12)}, + {"bar", "baz"}, + {"baz", -2.0} + }*/; ASSERT(comp["foo"].get_type() == tag_type::Short); ASSERT((int)comp["foo"] == 12); ASSERT((int16_t)comp["foo"] == int16_t(12)); - ASSERT(comp["foo"] == int8_t(12)); + /*ASSERT(comp["foo"] == int8_t(12)); ASSERT(comp["foo"] == 12); - ASSERT(comp["foo"] != "12"); + ASSERT(comp["foo"] != "12");*/ ASSERT(comp["bar"].get_type() == tag_type::String); ASSERT((std::string)comp["bar"] == "baz"); - ASSERT(comp["bar"] == "baz"); - ASSERT(comp["bar"] != 0); + /*ASSERT(comp["bar"] == "baz"); + ASSERT(comp["bar"] != 0);*/ ASSERT(comp["baz"].get_type() == tag_type::Double); ASSERT((double)comp["baz"] == -2.0); - ASSERT(comp["baz"] == -2.0f); + /*ASSERT(comp["baz"] == -2.0f); ASSERT(comp["baz"] == -2); - ASSERT(comp["baz"] != "-2"); + ASSERT(comp["baz"] != "-2");*/ - comp["quux"] = tag_compound{{"Hello", "World"}}; + comp["quux"] = tag_compound{/*{"Hello", "World"}, {"zero", 0}*/}; ASSERT(comp["quux"].get_type() == tag_type::Compound); - ASSERT(comp["quux"]["Hello"] == "World"); + ASSERT((std::string)comp["quux"]["Hello"] == "World"); + + tag_compound comp2/*{ + {"foo", int16_t(12)}, + {"bar", "baz"}, + {"baz", -2.0}, + {"quux", tag_compound{{"Hello", "World"}, {"zero", 0}}} + }*/; + //ASSERT(comp == comp2); + ASSERT(comp != (const tag_compound&)comp2["quux"]); + ASSERT(comp != comp2["quux"]); } int main() -- cgit 0.0.5-2-1-g0f52 From 6c1c90d668915ce698fe71a53891986e82d4af7a Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sat, 27 Jun 2015 22:48:55 +0200 Subject: Small change of wording --- include/nbt_value.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/nbt_value.h b/include/nbt_value.h index 5af322b4d2..2a3769f118 100644 --- a/include/nbt_value.h +++ b/include/nbt_value.h @@ -32,7 +32,7 @@ class tag_compound; class tag_list; /** - * @brief Represents a reference to a tag of fixed type + * @brief Contains an NBT value of fixed type * * This can contain a tag of arbitrary type, but once the tag's type * is set it is immutable. -- cgit 0.0.5-2-1-g0f52 From e040454ca3ba94b4a4c13c51a2cb3dd5cba8749b Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 28 Jun 2015 16:54:16 +0200 Subject: Add operator== and equals for tag --- include/tag.h | 6 ++++++ include/tag_compound.h | 2 ++ include/tag_primitive.h | 8 ++++++++ include/tag_string.h | 2 ++ test/nbttest.cpp | 2 +- 5 files changed, 19 insertions(+), 1 deletion(-) diff --git a/include/tag.h b/include/tag.h index 56d3594474..2bca595f9d 100644 --- a/include/tag.h +++ b/include/tag.h @@ -51,6 +51,12 @@ public: ///Returns the type of the tag virtual tag_type get_type() const noexcept = 0; + + friend bool operator==(const tag& lhs, const tag& rhs); + friend bool operator!=(const tag& lhs, const tag& rhs); + +private: + virtual bool equals(const tag& rhs) const = 0; }; } diff --git a/include/tag_compound.h b/include/tag_compound.h index 8fdc2f6617..51f0cd2709 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -92,6 +92,8 @@ public: private: + + bool equals(const tag& rhs) const override; }; } diff --git a/include/tag_primitive.h b/include/tag_primitive.h index 5704da4430..4c210ff0a8 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -55,6 +55,8 @@ public: private: T value; + + bool equals(const tag& rhs) const override; }; //Typedefs that should be used instead of the template tag_primitive. @@ -107,6 +109,12 @@ tag_type tag_primitive::get_type() const noexcept return type; } +template +bool tag_primitive::equals(const tag& rhs) const +{ + return value == static_cast&>(rhs).value; +} + } #endif // TAG_PRIMITIVE_H_INCLUDED diff --git a/include/tag_string.h b/include/tag_string.h index 28d1b77085..1daa0c2c6b 100644 --- a/include/tag_string.h +++ b/include/tag_string.h @@ -52,6 +52,8 @@ public: private: std::string value; + + bool equals(const tag& rhs) const override; }; } diff --git a/test/nbttest.cpp b/test/nbttest.cpp index b77c9b85bf..a596259050 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -112,7 +112,7 @@ void test_tag_compound() {"baz", -2.0}, {"quux", tag_compound{{"Hello", "World"}, {"zero", 0}}} }*/; - //ASSERT(comp == comp2); + ASSERT(comp == comp2); ASSERT(comp != (const tag_compound&)comp2["quux"]); ASSERT(comp != comp2["quux"]); } -- cgit 0.0.5-2-1-g0f52 From d888face4b4029718909847d3686650b871acd83 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 28 Jun 2015 16:55:57 +0200 Subject: Specify default move constructor for nbt_value --- include/nbt_value.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/nbt_value.h b/include/nbt_value.h index 2a3769f118..a1e98b4117 100644 --- a/include/nbt_value.h +++ b/include/nbt_value.h @@ -45,8 +45,10 @@ class nbt_value { public: //Movable but not (implicitly) copyable - nbt_value& operator=(nbt_value&&) = default; + nbt_value(const nbt_value&) = delete; + nbt_value(nbt_value&&) = default; nbt_value& operator=(const nbt_value&) = delete; + nbt_value& operator=(nbt_value&&) = default; //nbt_value& operator=(std::unique_ptr&& ptr); -- cgit 0.0.5-2-1-g0f52 From 87b3d6c2e151297860d039d614b36cc98238f47a Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 28 Jun 2015 16:58:12 +0200 Subject: Add doxygen comment for equals --- include/tag.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/tag.h b/include/tag.h index 2bca595f9d..29e63a9c54 100644 --- a/include/tag.h +++ b/include/tag.h @@ -56,6 +56,10 @@ public: friend 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; }; -- cgit 0.0.5-2-1-g0f52 From 401087dfca03d9641cd3b62b004496e38246ae2f Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 28 Jun 2015 17:30:02 +0200 Subject: Revise test_tag_compound regarding comparison --- test/nbttest.cpp | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index a596259050..8b4329d64f 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -85,26 +85,19 @@ void test_tag_compound() }*/; ASSERT(comp["foo"].get_type() == tag_type::Short); - ASSERT((int)comp["foo"] == 12); - ASSERT((int16_t)comp["foo"] == int16_t(12)); - /*ASSERT(comp["foo"] == int8_t(12)); - ASSERT(comp["foo"] == 12); - ASSERT(comp["foo"] != "12");*/ + ASSERT(int(comp["foo"]) == 12); + ASSERT(int16_t(comp["foo"]) == int16_t(12)); ASSERT(comp["bar"].get_type() == tag_type::String); - ASSERT((std::string)comp["bar"] == "baz"); - /*ASSERT(comp["bar"] == "baz"); - ASSERT(comp["bar"] != 0);*/ + ASSERT(std::string(comp["bar"]) == "baz"); ASSERT(comp["baz"].get_type() == tag_type::Double); - ASSERT((double)comp["baz"] == -2.0); - /*ASSERT(comp["baz"] == -2.0f); - ASSERT(comp["baz"] == -2); - ASSERT(comp["baz"] != "-2");*/ + ASSERT(double(comp["baz"]) == -2.0); + ASSERT(float(comp["baz"]) == -2.0f); comp["quux"] = tag_compound{/*{"Hello", "World"}, {"zero", 0}*/}; ASSERT(comp["quux"].get_type() == tag_type::Compound); - ASSERT((std::string)comp["quux"]["Hello"] == "World"); + ASSERT(std::string(comp["quux"]["Hello"]) == "World"); tag_compound comp2/*{ {"foo", int16_t(12)}, -- cgit 0.0.5-2-1-g0f52 From 298c01eaebb50a5756455ff6ffd49289a214b624 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 28 Jun 2015 17:36:02 +0200 Subject: Reword doxygen comment for nbt_value --- include/nbt_value.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/nbt_value.h b/include/nbt_value.h index a1e98b4117..786d5e81eb 100644 --- a/include/nbt_value.h +++ b/include/nbt_value.h @@ -34,10 +34,8 @@ class tag_list; /** * @brief Contains an NBT value of fixed type * - * This can contain a tag of arbitrary type, but once the tag's type - * is set it is immutable. - * It can be casted to or assigned values, but if the types do not match it - * will throw an exception. + * A wrapper class that can contain a value of an arbitrary but fixed type. + * Casting or assigning incompatible types will throw an exception. * It can also refer to an uninitialized value (e.g. when using tag_compound::operator[] * with a non-existant key). */ -- cgit 0.0.5-2-1-g0f52 From 1393bc318e61b57e2cd6906a6abd3fb4607111c7 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 28 Jun 2015 17:43:33 +0200 Subject: Rename nbt_value to value --- include/nbt_value.h | 107 ------------------------------------------------- include/tag_compound.h | 18 ++++----- include/value.h | 107 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 116 deletions(-) delete mode 100644 include/nbt_value.h create mode 100644 include/value.h diff --git a/include/nbt_value.h b/include/nbt_value.h deleted file mode 100644 index 786d5e81eb..0000000000 --- a/include/nbt_value.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * libnbt++ - A library for the Minecraft Named Binary Tag format. - * Copyright (C) 2013, 2015 ljfa-ag - * - * This file is part of libnbt++. - * - * libnbt++ is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * libnbt++ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with libnbt++. If not, see . - */ -#ifndef TAG_REF_PROXY_H_INCLUDED -#define TAG_REF_PROXY_H_INCLUDED - -#include "tag.h" -#include -#include - -namespace nbt -{ - -//Forward declarations -class tag_compound; -class tag_list; - -/** - * @brief Contains an NBT value of fixed type - * - * A wrapper class that can contain a value of an arbitrary but fixed type. - * Casting or assigning incompatible types will throw an exception. - * It can also refer to an uninitialized value (e.g. when using tag_compound::operator[] - * with a non-existant key). - */ -class nbt_value -{ -public: - //Movable but not (implicitly) copyable - nbt_value(const nbt_value&) = delete; - nbt_value(nbt_value&&) = default; - nbt_value& operator=(const nbt_value&) = delete; - nbt_value& operator=(nbt_value&&) = default; - - //nbt_value& operator=(std::unique_ptr&& ptr); - - //Assignment - /** - * @brief Assigns the given value to the tag if the type matches - * @throw std::bad_cast if the value is not convertible to the tag type - * via a widening conversion - */ - nbt_value& operator=(int8_t val); - nbt_value& operator=(int16_t val); - nbt_value& operator=(int32_t val); - nbt_value& operator=(int64_t val); - nbt_value& operator=(float val); - nbt_value& operator=(double val); - nbt_value& operator=(const std::string& str); - nbt_value& operator=(tag_compound&& comp); - nbt_value& operator=(tag_list&& list); - - //Conversion to tag - operator tag&(); - operator const tag&() const; - - //Conversions to primitives and string - /** - * @brief Casts the value if the type matches - * @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; - explicit operator const std::string&() const; - - /** - * @brief In case of a tag_compound, accesses a tag by key - * @throw std::bad_cast if the tag type is not tag_compound - * @sa tag_compound::operator[] - */ - nbt_value& operator[](const std::string& key); - const nbt_value& operator[](const std::string& key) const; - - ///@sa tag::get_type - tag_type get_type() const; - - friend bool operator==(const nbt_value& lhs, const nbt_value& rhs); - friend bool operator!=(const nbt_value& lhs, const nbt_value& rhs); - -private: - std::unique_ptr value; -}; - -} - -#endif // TAG_REF_PROXY_H_INCLUDED diff --git a/include/tag_compound.h b/include/tag_compound.h index 51f0cd2709..e0426dbf74 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -21,7 +21,7 @@ #define TAG_COMPOUND_H_INCLUDED #include "tag.h" -#include "nbt_value.h" +#include "value.h" #include #include #include @@ -43,27 +43,27 @@ public: ///Constructs an empty compound tag_compound() {} - //TODO: Make a separate class similar to and convertible to nbt_value for initializing tag values - //tag_compound(std::initializer_list> init); + //TODO: Make a separate class similar to and convertible to value for initializing tag values + //tag_compound(std::initializer_list> init); /** * @brief Accesses a tag by key with bounds checking * - * Returns a nbt_value to the tag with the specified key, or throws an + * 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 */ - nbt_value& at(const std::string& key); - const nbt_value& at(const std::string& key) const; + value& at(const std::string& key); + const value& at(const std::string& key) const; /** * @brief Accesses a tag by key * - * If the key exists, returns a nbt_value to the corresponding tag. + * If the key exists, returns a value to the corresponding tag. * Else, a new uninitalized entry is created under this key. */ - nbt_value& operator[](const std::string& key); - const nbt_value& operator[](const std::string& key) const; + value& operator[](const std::string& key); + const value& operator[](const std::string& key) const; /** * @brief Erases a tag from the compound diff --git a/include/value.h b/include/value.h new file mode 100644 index 0000000000..4553b3e8f4 --- /dev/null +++ b/include/value.h @@ -0,0 +1,107 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#ifndef TAG_REF_PROXY_H_INCLUDED +#define TAG_REF_PROXY_H_INCLUDED + +#include "tag.h" +#include +#include + +namespace nbt +{ + +//Forward declarations +class tag_compound; +class tag_list; + +/** + * @brief Contains an NBT value of fixed type + * + * A wrapper class that can contain a tag of an arbitrary but fixed type. + * Casting or assigning incompatible types will throw an exception. + * It can also refer to an uninitialized value (e.g. when using tag_compound::operator[] + * with a non-existant key). + */ +class value +{ +public: + //Movable but not (implicitly) copyable + value(const value&) = delete; + value(value&&) = default; + value& operator=(const value&) = delete; + value& operator=(value&&) = default; + + //value& operator=(std::unique_ptr&& ptr); + + //Assignment + /** + * @brief Assigns the given value to the tag if the type matches + * @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); + value& operator=(const std::string& str); + value& operator=(tag_compound&& comp); + value& operator=(tag_list&& list); + + //Conversion to tag + operator tag&(); + operator const tag&() const; + + //Conversions to primitives and string + /** + * @brief Casts the value if the type matches + * @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; + explicit operator const std::string&() const; + + /** + * @brief In case of a tag_compound, accesses a tag by key + * @throw std::bad_cast if the tag type is not tag_compound + * @sa tag_compound::operator[] + */ + value& operator[](const std::string& key); + const value& operator[](const std::string& key) const; + + ///@sa tag::get_type + tag_type get_type() const; + + friend bool operator==(const value& lhs, const value& rhs); + friend bool operator!=(const value& lhs, const value& rhs); + +private: + std::unique_ptr tag_; +}; + +} + +#endif // TAG_REF_PROXY_H_INCLUDED -- cgit 0.0.5-2-1-g0f52 From 9337c5455222c878019ab1cd4779cc5023e73507 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 28 Jun 2015 17:48:20 +0200 Subject: Add at method to value --- include/value.h | 9 +++++++++ test/nbttest.cpp | 6 +++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/include/value.h b/include/value.h index 4553b3e8f4..2a9ecc0d1a 100644 --- a/include/value.h +++ b/include/value.h @@ -84,6 +84,15 @@ public: explicit operator double() const; explicit operator const std::string&() const; + /** + * @brief In case of a tag_compound, accesses a tag by key with bounds checking + * @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 * @throw std::bad_cast if the tag type is not tag_compound diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 8b4329d64f..60f090dd43 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -86,7 +86,7 @@ void test_tag_compound() ASSERT(comp["foo"].get_type() == tag_type::Short); ASSERT(int(comp["foo"]) == 12); - ASSERT(int16_t(comp["foo"]) == int16_t(12)); + ASSERT(int16_t(comp.at("foo")) == int16_t(12)); ASSERT(comp["bar"].get_type() == tag_type::String); ASSERT(std::string(comp["bar"]) == "baz"); @@ -96,8 +96,8 @@ void test_tag_compound() ASSERT(float(comp["baz"]) == -2.0f); comp["quux"] = tag_compound{/*{"Hello", "World"}, {"zero", 0}*/}; - ASSERT(comp["quux"].get_type() == tag_type::Compound); - ASSERT(std::string(comp["quux"]["Hello"]) == "World"); + ASSERT(comp.at("quux").get_type() == tag_type::Compound); + ASSERT(std::string(comp["quux"].at("Hello")) == "World"); tag_compound comp2/*{ {"foo", int16_t(12)}, -- cgit 0.0.5-2-1-g0f52 From 6374857bfd25a033ff31316157c985645ae4519c Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 28 Jun 2015 19:23:40 +0200 Subject: Iterators typedefs for tag_compound --- include/tag_compound.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/include/tag_compound.h b/include/tag_compound.h index e0426dbf74..51ca877a06 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -23,7 +23,6 @@ #include "tag.h" #include "value.h" #include -#include #include namespace nbt @@ -34,8 +33,8 @@ class tag_compound : public tag { public: //Iterator types - typedef void* iterator; //FIXME - typedef const void* const_iterator; + typedef std::map::iterator iterator; + typedef std::map::const_iterator const_iterator; ///The type of the tag static constexpr tag_type type = tag_type::Compound; @@ -91,7 +90,7 @@ public: tag_type get_type() const noexcept override; private: - + std::map tags; bool equals(const tag& rhs) const override; }; -- cgit 0.0.5-2-1-g0f52 From ff868f238b58323465505c907a62064d4cbfb0a1 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 28 Jun 2015 19:41:36 +0200 Subject: Clarify fail messages on EXPECT_EXCEPTION --- test/microtest.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/microtest.h b/test/microtest.h index 084a85496c..8806775f12 100644 --- a/test/microtest.h +++ b/test/microtest.h @@ -26,8 +26,8 @@ exit(EXIT_FAILURE); } #define ASSERT(expr) { if(!(expr)) FAIL_TEST } #define EXPECT_EXCEPTION(expr, type) { \ - try { (expr); std::cerr << "Expected " #type " to be thrown" << std::endl; FAIL_TEST } \ + try { (expr); std::cerr << "Expected " #type " to be thrown, got no exception instead" << std::endl; FAIL_TEST } \ catch(type&) {} \ - catch(...) { std::cerr << "Expected " #type " to be thrown" << std::endl; FAIL_TEST } } + catch(...) { std::cerr << "Expected " #type " to be thrown, got something else instead" << std::endl; FAIL_TEST } } #endif -- cgit 0.0.5-2-1-g0f52 From 61b44772d7390cbffed3e2fa4af0138727d58bcf Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 28 Jun 2015 22:43:44 +0200 Subject: Update Requirements.md --- Requirements.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Requirements.md b/Requirements.md index d869e3cecc..f7b46f1a8a 100644 --- a/Requirements.md +++ b/Requirements.md @@ -30,10 +30,17 @@ such as: provide. - /RF30/ A shorter syntax than in libnbt++1 and preferrably also Mojang's implementation. +- /RF35/ Typesafe operations (no possibly unwanted implicit casts), in case + of incompatible types exceptions should be thrown. - /RF40/ The need for insecure operations and manual memory management should be minimized; references and `std::unique_ptr` should be preferred before raw pointers. +- /RF55/ A wrapper for tags that provides syntactic sugar is preferred + before raw `std::unique_ptr` values. - /RF50/ Move semantics are preferred before copy semantics. +- /RF55/ Copying tags should be possible, but only in an explicit manner. +- /RF60/ Checked conversions are preferred, unchecked conversions may be + possible but discouraged. ### Product Performance - /RP10/ All operations on (not too large) NBT data should not be slower -- cgit 0.0.5-2-1-g0f52 From 69fcbd0fd080aa3e7522040c1abc6394e7a1e390 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 29 Jun 2015 22:00:44 +0200 Subject: Add more tests for tag_compound --- test/nbttest.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 60f090dd43..7b47d42f8f 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -20,6 +20,7 @@ #include "microtest.h" #include "libnbt.h" #include +#include using namespace nbt; @@ -76,8 +77,6 @@ void test_tag_string() void test_tag_compound() { - //Preliminary - //Doesn't work yet, but this is the syntax I would like to have: tag_compound comp/*{ {"foo", int16_t(12)}, {"bar", "baz"}, @@ -85,20 +84,25 @@ void test_tag_compound() }*/; ASSERT(comp["foo"].get_type() == tag_type::Short); - ASSERT(int(comp["foo"]) == 12); + ASSERT(int32_t(comp["foo"]) == 12); ASSERT(int16_t(comp.at("foo")) == int16_t(12)); + EXPECT_EXCEPTION(int8_t(comp["foo"]), std::bad_cast); + EXPECT_EXCEPTION(std::string(comp["foo"]), std::bad_cast); ASSERT(comp["bar"].get_type() == tag_type::String); ASSERT(std::string(comp["bar"]) == "baz"); + EXPECT_EXCEPTION(int(comp["bar"]), std::bad_cast); ASSERT(comp["baz"].get_type() == tag_type::Double); ASSERT(double(comp["baz"]) == -2.0); - ASSERT(float(comp["baz"]) == -2.0f); + EXPECT_EXCEPTION(float(comp["baz"]), std::bad_cast); comp["quux"] = tag_compound{/*{"Hello", "World"}, {"zero", 0}*/}; ASSERT(comp.at("quux").get_type() == tag_type::Compound); ASSERT(std::string(comp["quux"].at("Hello")) == "World"); + EXPECT_EXCEPTION(comp.at("nothing"), std::out_of_range); + tag_compound comp2/*{ {"foo", int16_t(12)}, {"bar", "baz"}, @@ -108,6 +112,16 @@ void test_tag_compound() ASSERT(comp == comp2); ASSERT(comp != (const tag_compound&)comp2["quux"]); ASSERT(comp != comp2["quux"]); + + ASSERT(comp.size() == 4); + + ASSERT(comp.erase("nothing") == false); + ASSERT(comp.has_key("quux")); + ASSERT(comp.erase("quux") == true); + ASSERT(!comp.has_key("quux")); + + comp.clear(); + ASSERT(comp == tag_compound{}); } int main() -- cgit 0.0.5-2-1-g0f52 From 66b64d57b711dd4b3bf805bc4a325d9448378fe3 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 29 Jun 2015 22:26:48 +0200 Subject: Remove comparison operators for tag_string --- include/tag_string.h | 3 --- test/nbttest.cpp | 10 +++++----- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/include/tag_string.h b/include/tag_string.h index 1daa0c2c6b..964e1532ee 100644 --- a/include/tag_string.h +++ b/include/tag_string.h @@ -36,9 +36,6 @@ public: tag_string(const std::string& str); tag_string(std::string&& str = ""); - bool operator==(const std::string& rhs) const; - bool operator!=(const std::string& rhs) const; - operator std::string&(); operator const std::string&() const; const std::string& get() const; diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 7b47d42f8f..419421f1b8 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -61,18 +61,18 @@ void test_tag_string() ASSERT(tag.get() == "foo"); std::string& ref = tag; ref = "bar"; - ASSERT(tag == "bar"); - ASSERT(tag != "foo"); + ASSERT(tag.get() == "bar"); + ASSERT(tag.get() != "foo"); tag.set("baz"); ASSERT(ref == "baz"); tag = "quux"; ASSERT("quux" == static_cast(tag)); std::string str("foo"); tag = str; - ASSERT(tag == str); + ASSERT(tag.get() == str); - ASSERT(tag_string(str) == "foo"); - ASSERT(tag_string() == ""); + ASSERT(tag_string(str).get() == "foo"); + ASSERT(tag_string().get() == ""); } void test_tag_compound() -- cgit 0.0.5-2-1-g0f52 From 8525040283ddb6190903b2a1d00481123daf4004 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 29 Jun 2015 22:49:30 +0200 Subject: Implement tag_string --- CMakeLists.txt | 2 +- src/tag_string.cpp | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 src/tag_string.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f105284ea..576f0a5b5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(libnbt++_VERSION_MINOR 0) add_definitions(-std=c++11) include_directories(include) add_library(nbt++ STATIC - ) + src/tag_string.cpp) enable_testing() add_subdirectory(test) diff --git a/src/tag_string.cpp b/src/tag_string.cpp new file mode 100644 index 0000000000..6a759088e5 --- /dev/null +++ b/src/tag_string.cpp @@ -0,0 +1,80 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include "tag_string.h" + +namespace nbt +{ + +tag_string::tag_string(const std::string& str): + value(str) +{} + +tag_string::tag_string(std::string&& str): + value(std::move(str)) +{} + +tag_string::operator std::string&() +{ + return value; +} + +tag_string::operator const std::string&() const +{ + return value; + +} +const std::string& tag_string::get() const +{ + return value; +} + +tag_string& tag_string::operator=(const std::string& str) +{ + value = str; + return *this; +} + +tag_string& tag_string::operator=(std::string&& str) +{ + value = std::move(str); + return *this; +} + +void tag_string::set(const std::string& str) +{ + value = str; +} + +void tag_string::set(std::string&& str) +{ + value = std::move(str); +} + +tag_type tag_string::get_type() const noexcept +{ + return type; +} + +bool tag_string::equals(const tag& rhs) const +{ + return value == static_cast(rhs).value; +} + +} -- cgit 0.0.5-2-1-g0f52 From b81ebe6cba109a215254bfae074f8fc6f334eca8 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 29 Jun 2015 23:09:20 +0200 Subject: Fix tag_compound and value --- include/tag_compound.h | 3 +-- include/value.h | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/tag_compound.h b/include/tag_compound.h index 51ca877a06..3750e8a557 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -62,7 +62,6 @@ public: * Else, a new uninitalized entry is created under this key. */ value& operator[](const std::string& key); - const value& operator[](const std::string& key) const; /** * @brief Erases a tag from the compound @@ -71,7 +70,7 @@ public: bool erase(const std::string& key); ///Returns true if the given key exists in the compound - bool has_key(const std::string& key); + bool has_key(const std::string& key) const; ///Returns the number of tags in the compound size_t size() const; diff --git a/include/value.h b/include/value.h index 2a9ecc0d1a..d55acf3c80 100644 --- a/include/value.h +++ b/include/value.h @@ -42,6 +42,8 @@ class tag_list; class value { public: + explicit value() {} + //Movable but not (implicitly) copyable value(const value&) = delete; value(value&&) = default; -- cgit 0.0.5-2-1-g0f52 From 5390c8e5a84aa4bbbf87981dbe13f60b0ee96655 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 29 Jun 2015 23:12:51 +0200 Subject: Implement tag_compound --- CMakeLists.txt | 1 + src/tag_compound.cpp | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 src/tag_compound.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 576f0a5b5e..0884cf2a11 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ set(libnbt++_VERSION_MINOR 0) add_definitions(-std=c++11) include_directories(include) add_library(nbt++ STATIC + src/tag_compound.cpp src/tag_string.cpp) enable_testing() diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp new file mode 100644 index 0000000000..af0d685826 --- /dev/null +++ b/src/tag_compound.cpp @@ -0,0 +1,77 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include "tag_compound.h" + +namespace nbt +{ + +value& tag_compound::at(const std::string& key) +{ + return tags.at(key); +} + +const value& tag_compound::at(const std::string& key) const +{ + return tags.at(key); +} + +value& tag_compound::operator[](const std::string& key) +{ + return tags[key]; +} + +bool tag_compound::erase(const std::string& key) +{ + return tags.erase(key) != 0; +} + +bool tag_compound::has_key(const std::string& key) const +{ + return tags.find(key) != tags.end(); +} + +size_t tag_compound::size() const +{ + return tags.size(); +} + +void tag_compound::clear() +{ + tags.clear(); +} + +auto tag_compound::begin() -> iterator { return tags.begin(); } +auto tag_compound::end() -> iterator { return tags.end(); } +auto tag_compound::begin() const -> const_iterator { return tags.begin(); } +auto tag_compound::end() const -> const_iterator { return tags.end(); } +auto tag_compound::cbegin() const -> const_iterator { return tags.cbegin(); } +auto tag_compound::cend() const -> const_iterator { return tags.cend(); } + +tag_type tag_compound::get_type() const noexcept +{ + return type; +} + +bool tag_compound::equals(const tag& rhs) const +{ + return tags == static_cast(rhs).tags; +} + +} -- cgit 0.0.5-2-1-g0f52 From 49ce5a019c5f8e4d862432734a42bc7aa2a18387 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 30 Jun 2015 17:45:50 +0200 Subject: Change interfaces of tag_compound and value once again --- include/tag_compound.h | 31 +++++++++++++++++++++++++++++-- include/value.h | 29 ++++++++++++++--------------- src/tag_compound.cpp | 15 +++++++++++++++ 3 files changed, 58 insertions(+), 17 deletions(-) diff --git a/include/tag_compound.h b/include/tag_compound.h index 3750e8a557..8d627fca67 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -58,11 +58,32 @@ public: /** * @brief Accesses a tag by key * - * If the key exists, returns a value to the corresponding tag. - * Else, a new uninitalized entry is created under this key. + * 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 + * @todo Make it create a new entry rather than throwing an exception */ value& operator[](const std::string& 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 true if the key did not exist + */ + bool put(const std::string& key, std::unique_ptr&& t); + + /** + * @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. + * @return true if the key did not exist + */ + template + bool emplace(const std::string& key, Args&&... args); + /** * @brief Erases a tag from the compound * @return true if a tag was erased @@ -94,6 +115,12 @@ private: bool equals(const tag& rhs) const override; }; +template +bool tag_compound::emplace(const std::string& key, Args&&... args) +{ + return put(key, new T(std::forward(args)...)); +} + } #endif // TAG_COMPOUND_H_INCLUDED diff --git a/include/value.h b/include/value.h index d55acf3c80..21e945625a 100644 --- a/include/value.h +++ b/include/value.h @@ -34,15 +34,15 @@ class tag_list; /** * @brief Contains an NBT value of fixed type * - * A wrapper class that can contain a tag of an arbitrary but fixed type. - * Casting or assigning incompatible types will throw an exception. - * It can also refer to an uninitialized value (e.g. when using tag_compound::operator[] - * with a non-existant key). + * A wrapper class that contains a dynamically allocated tag of a fixed type. + * Casting or assigning incompatible types will throw a exceptions. */ class value { public: explicit value() {} + explicit value(std::unique_ptr&& t); + explicit value(tag&& t); //Movable but not (implicitly) copyable value(const value&) = delete; @@ -50,9 +50,10 @@ public: value& operator=(const value&) = delete; value& operator=(value&&) = default; - //value& operator=(std::unique_ptr&& ptr); + value& operator=(std::unique_ptr&& t); + value& operator=(tag&& t); - //Assignment + //Assignment of primitives and string /** * @brief Assigns the given value to the tag if the type matches * @throw std::bad_cast if the value is not convertible to the tag type @@ -65,8 +66,6 @@ public: value& operator=(float val); value& operator=(double val); value& operator=(const std::string& str); - value& operator=(tag_compound&& comp); - value& operator=(tag_list&& list); //Conversion to tag operator tag&(); @@ -78,13 +77,13 @@ public: * @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; - explicit operator const std::string&() const; + operator int8_t() const; + operator int16_t() const; + operator int32_t() const; + operator int64_t() const; + operator float() const; + operator double() const; + operator const std::string&() const; /** * @brief In case of a tag_compound, accesses a tag by key with bounds checking diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index af0d685826..5e9d81aaf7 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -37,6 +37,21 @@ value& tag_compound::operator[](const std::string& key) return tags[key]; } +bool tag_compound::put(const std::string& key, std::unique_ptr&& t) +{ + auto it = tags.find(key); + if(it != tags.end()) + { + it->second = std::move(t); + return false; + } + else + { + tags.emplace(key, value(std::move(t))); + return true; + } +} + bool tag_compound::erase(const std::string& key) { return tags.erase(key) != 0; -- cgit 0.0.5-2-1-g0f52 From 955bdfe3ae22039923d8d4df2ba3892031c55064 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 30 Jun 2015 17:48:41 +0200 Subject: Make tag classes final --- include/tag_compound.h | 2 +- include/tag_primitive.h | 2 +- include/tag_string.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/tag_compound.h b/include/tag_compound.h index 8d627fca67..e1d5218a9a 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -29,7 +29,7 @@ namespace nbt { ///Tag that contains multiple unordered named tags of arbitrary types -class tag_compound : public tag +class tag_compound final : public tag { public: //Iterator types diff --git a/include/tag_primitive.h b/include/tag_primitive.h index 4c210ff0a8..bf8600c9f1 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -33,7 +33,7 @@ namespace nbt * Common class for tag_byte, tag_short, tag_int, tag_long, tag_float and tag_double. */ template -class tag_primitive : public tag +class tag_primitive final : public tag { public: ///The type of the value diff --git a/include/tag_string.h b/include/tag_string.h index 964e1532ee..05a2e1cb4d 100644 --- a/include/tag_string.h +++ b/include/tag_string.h @@ -27,7 +27,7 @@ namespace nbt { ///Tag that contains a UTF-8 string -class tag_string : public tag +class tag_string final : public tag { public: ///The type of the tag -- cgit 0.0.5-2-1-g0f52 From 26a08e6eeabec972cf57c6eb4e8f91516ad07f0c Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 30 Jun 2015 19:28:35 +0200 Subject: Create move_clone method --- include/tag.h | 3 +++ include/tag_compound.h | 2 ++ include/tag_primitive.h | 8 ++++++++ include/tag_string.h | 2 ++ src/tag_compound.cpp | 5 +++++ src/tag_string.cpp | 5 +++++ 6 files changed, 25 insertions(+) diff --git a/include/tag.h b/include/tag.h index 29e63a9c54..acc537500f 100644 --- a/include/tag.h +++ b/include/tag.h @@ -21,6 +21,7 @@ #define TAG_H_INCLUDED #include +#include namespace nbt { @@ -52,6 +53,8 @@ public: ///Returns the type of the tag virtual tag_type get_type() const noexcept = 0; + virtual std::unique_ptr move_clone() && = 0; + friend bool operator==(const tag& lhs, const tag& rhs); friend bool operator!=(const tag& lhs, const tag& rhs); diff --git a/include/tag_compound.h b/include/tag_compound.h index e1d5218a9a..435fdbdff6 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -109,6 +109,8 @@ public: tag_type get_type() const noexcept override; + std::unique_ptr move_clone() && override; + private: std::map tags; diff --git a/include/tag_primitive.h b/include/tag_primitive.h index bf8600c9f1..e35d462c48 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -53,6 +53,8 @@ public: tag_type get_type() const noexcept override; + std::unique_ptr move_clone() && override; + private: T value; @@ -109,6 +111,12 @@ tag_type tag_primitive::get_type() const noexcept return type; } +template +std::unique_ptr tag_primitive::move_clone() && +{ + return std::unique_ptr(new tag_primitive(std::move(*this))); +} + template bool tag_primitive::equals(const tag& rhs) const { diff --git a/include/tag_string.h b/include/tag_string.h index 05a2e1cb4d..456fa8ad08 100644 --- a/include/tag_string.h +++ b/include/tag_string.h @@ -47,6 +47,8 @@ public: tag_type get_type() const noexcept override; + std::unique_ptr move_clone() && override; + private: std::string value; diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index 5e9d81aaf7..20cb021df8 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -84,6 +84,11 @@ tag_type tag_compound::get_type() const noexcept return type; } +std::unique_ptr tag_compound::move_clone() && +{ + return std::unique_ptr(new tag_compound(std::move(*this))); +} + bool tag_compound::equals(const tag& rhs) const { return tags == static_cast(rhs).tags; diff --git a/src/tag_string.cpp b/src/tag_string.cpp index 6a759088e5..6322d4a42c 100644 --- a/src/tag_string.cpp +++ b/src/tag_string.cpp @@ -72,6 +72,11 @@ tag_type tag_string::get_type() const noexcept return type; } +std::unique_ptr tag_string::move_clone() && +{ + return std::unique_ptr(new tag_string(std::move(*this))); +} + bool tag_string::equals(const tag& rhs) const { return value == static_cast(rhs).value; -- cgit 0.0.5-2-1-g0f52 From 5f157da65896a8dc0a6c326255dd8bb7405df82e Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 30 Jun 2015 19:32:43 +0200 Subject: Partial implementation of value --- CMakeLists.txt | 3 ++- include/value.h | 6 +++--- src/value.cpp | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 src/value.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0884cf2a11..1c4ab33380 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,8 @@ add_definitions(-std=c++11) include_directories(include) add_library(nbt++ STATIC src/tag_compound.cpp - src/tag_string.cpp) + src/tag_string.cpp + src/value.cpp) enable_testing() add_subdirectory(test) diff --git a/include/value.h b/include/value.h index 21e945625a..bcad7f3468 100644 --- a/include/value.h +++ b/include/value.h @@ -44,15 +44,15 @@ public: explicit value(std::unique_ptr&& t); explicit value(tag&& t); + value& operator=(std::unique_ptr&& t); + value& operator=(tag&& t); + //Movable but not (implicitly) copyable value(const value&) = delete; value(value&&) = default; value& operator=(const value&) = delete; value& operator=(value&&) = default; - value& operator=(std::unique_ptr&& t); - value& operator=(tag&& t); - //Assignment of primitives and string /** * @brief Assigns the given value to the tag if the type matches diff --git a/src/value.cpp b/src/value.cpp new file mode 100644 index 0000000000..574c23d087 --- /dev/null +++ b/src/value.cpp @@ -0,0 +1,60 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include "value.h" +#include "libnbt.h" + +namespace nbt +{ + +value::value(std::unique_ptr&& t): + tag_(std::move(t)) +{} + +value::value(tag&& t): + tag_(std::move(t).move_clone()) +{} + +value& value::operator=(std::unique_ptr&& t) +{ + tag_ = std::move(t); + return *this; +} + +value& value::operator=(tag&& t) +{ + return *this = std::move(t).move_clone(); +} + +tag_type value::get_type() const +{ + return tag_->get_type(); +} + +bool operator==(const value& lhs, const value& rhs) +{ + return *lhs.tag_ == *rhs.tag_; +} + +bool operator!=(const value& lhs, const value& rhs) +{ + return !(lhs == rhs); +} + +} -- cgit 0.0.5-2-1-g0f52 From 8fef19528e0f6a68621d58ef9dd9cb75fc163657 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 30 Jun 2015 19:38:22 +0200 Subject: Implement operator== for tag --- CMakeLists.txt | 1 + src/tag.cpp | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 src/tag.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c4ab33380..8f49a64ff7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ set(libnbt++_VERSION_MINOR 0) add_definitions(-std=c++11) include_directories(include) add_library(nbt++ STATIC + src/tag.cpp src/tag_compound.cpp src/tag_string.cpp src/value.cpp) diff --git a/src/tag.cpp b/src/tag.cpp new file mode 100644 index 0000000000..40565226cf --- /dev/null +++ b/src/tag.cpp @@ -0,0 +1,38 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include "tag.h" +#include + +namespace nbt +{ + +bool operator==(const tag& lhs, const tag& rhs) +{ + if(typeid(lhs) != typeid(rhs)) + return false; + return lhs.equals(rhs); +} + +bool operator!=(const tag& lhs, const tag& rhs) +{ + return !(lhs == rhs); +} + +} -- cgit 0.0.5-2-1-g0f52 From d648754b1f35e7a6465869157ae9afa0b0ee451d Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 1 Jul 2015 19:17:50 +0200 Subject: Overload operator== for each tag individually --- include/tag_compound.h | 3 +++ include/tag_primitive.h | 17 ++++++++++++++++- include/tag_string.h | 3 +++ src/tag_compound.cpp | 12 +++++++++++- src/tag_string.cpp | 12 +++++++++++- 5 files changed, 44 insertions(+), 3 deletions(-) diff --git a/include/tag_compound.h b/include/tag_compound.h index 435fdbdff6..fb2d27123a 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -115,6 +115,9 @@ private: std::map tags; bool equals(const tag& rhs) const override; + + friend bool operator==(const tag_compound& lhs, const tag_compound& rhs); + friend bool operator!=(const tag_compound& lhs, const tag_compound& rhs); }; template diff --git a/include/tag_primitive.h b/include/tag_primitive.h index e35d462c48..73c7b1e727 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -61,6 +61,9 @@ private: bool equals(const tag& rhs) const override; }; +template bool operator==(const tag_primitive& lhs, const tag_primitive& rhs); +template bool operator!=(const tag_primitive& lhs, const tag_primitive& rhs); + //Typedefs that should be used instead of the template tag_primitive. typedef tag_primitive tag_byte; typedef tag_primitive tag_short; @@ -120,7 +123,19 @@ std::unique_ptr tag_primitive::move_clone() && template bool tag_primitive::equals(const tag& rhs) const { - return value == static_cast&>(rhs).value; + return *this == static_cast&>(rhs); +} + +template +bool operator==(const tag_primitive& lhs, const tag_primitive& rhs) +{ + return lhs.get() == rhs.get(); +} + +template +bool operator!=(const tag_primitive& lhs, const tag_primitive& rhs) +{ + return !(lhs == rhs); } } diff --git a/include/tag_string.h b/include/tag_string.h index 456fa8ad08..f1b622eee9 100644 --- a/include/tag_string.h +++ b/include/tag_string.h @@ -55,6 +55,9 @@ private: bool equals(const tag& rhs) const override; }; +bool operator==(const tag_string& lhs, const tag_string& rhs); +bool operator!=(const tag_string& lhs, const tag_string& rhs); + } #endif // TAG_STRING_H_INCLUDED diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index 20cb021df8..53fd9df8ac 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -91,7 +91,17 @@ std::unique_ptr tag_compound::move_clone() && bool tag_compound::equals(const tag& rhs) const { - return tags == static_cast(rhs).tags; + return *this == static_cast(rhs); +} + +bool operator==(const tag_compound& lhs, const tag_compound& rhs) +{ + return lhs.tags == rhs.tags; +} + +bool operator!=(const tag_compound& lhs, const tag_compound& rhs) +{ + return !(lhs == rhs); } } diff --git a/src/tag_string.cpp b/src/tag_string.cpp index 6322d4a42c..b3ef075cd3 100644 --- a/src/tag_string.cpp +++ b/src/tag_string.cpp @@ -79,7 +79,17 @@ std::unique_ptr tag_string::move_clone() && bool tag_string::equals(const tag& rhs) const { - return value == static_cast(rhs).value; + return *this == static_cast(rhs); +} + +bool operator==(const tag_string& lhs, const tag_string& rhs) +{ + return lhs.get() == rhs.get(); +} + +bool operator!=(const tag_string& lhs, const tag_string& rhs) +{ + return !(lhs == rhs); } } -- cgit 0.0.5-2-1-g0f52 From 6168f3ad47a5eddd6b10a84455f8fbd5a9852818 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 1 Jul 2015 19:28:51 +0200 Subject: Change description of value::operator= Remove value constructor from tag&& --- include/value.h | 17 +++++++++++------ src/value.cpp | 4 ---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/include/value.h b/include/value.h index bcad7f3468..491a92b2a0 100644 --- a/include/value.h +++ b/include/value.h @@ -41,11 +41,6 @@ class value { public: explicit value() {} - explicit value(std::unique_ptr&& t); - explicit value(tag&& t); - - value& operator=(std::unique_ptr&& t); - value& operator=(tag&& t); //Movable but not (implicitly) copyable value(const value&) = delete; @@ -53,9 +48,19 @@ public: value& operator=(const value&) = delete; value& operator=(value&&) = default; - //Assignment of primitives and string + explicit value(std::unique_ptr&& t); + value& operator=(std::unique_ptr&& t); + /** * @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); + + //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 */ diff --git a/src/value.cpp b/src/value.cpp index 574c23d087..339c737505 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -27,10 +27,6 @@ value::value(std::unique_ptr&& t): tag_(std::move(t)) {} -value::value(tag&& t): - tag_(std::move(t).move_clone()) -{} - value& value::operator=(std::unique_ptr&& t) { tag_ = std::move(t); -- cgit 0.0.5-2-1-g0f52 From 2345716fbdf380dde60cccb71cbc335cf587fec6 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 1 Jul 2015 19:48:04 +0200 Subject: Create tag::assign method Implement value::operator= in terms of it --- include/tag.h | 8 ++++++++ include/tag_compound.h | 2 ++ include/tag_primitive.h | 8 ++++++++ include/tag_string.h | 2 ++ src/tag_compound.cpp | 5 +++++ src/tag_string.cpp | 5 +++++ src/value.cpp | 4 +++- 7 files changed, 33 insertions(+), 1 deletion(-) diff --git a/include/tag.h b/include/tag.h index acc537500f..a9cbc0019f 100644 --- a/include/tag.h +++ b/include/tag.h @@ -64,6 +64,14 @@ private: * @param rhs an instance of the same class as @c *this */ virtual bool equals(const tag& rhs) const = 0; + + /** + * @brief 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; + + friend class value; }; } diff --git a/include/tag_compound.h b/include/tag_compound.h index fb2d27123a..32380a0c6c 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -116,6 +116,8 @@ private: bool equals(const tag& rhs) const override; + tag_compound& assign(tag&& rhs) override; + friend bool operator==(const tag_compound& lhs, const tag_compound& rhs); friend bool operator!=(const tag_compound& lhs, const tag_compound& rhs); }; diff --git a/include/tag_primitive.h b/include/tag_primitive.h index 73c7b1e727..9c4cd3b2b9 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -59,6 +59,8 @@ private: T value; bool equals(const tag& rhs) const override; + + tag_primitive& assign(tag&& rhs) override; }; template bool operator==(const tag_primitive& lhs, const tag_primitive& rhs); @@ -126,6 +128,12 @@ bool tag_primitive::equals(const tag& rhs) const return *this == static_cast&>(rhs); } +template +tag_primitive& tag_primitive::assign(tag&& rhs) +{ + return *this = dynamic_cast&&>(rhs); +} + template bool operator==(const tag_primitive& lhs, const tag_primitive& rhs) { diff --git a/include/tag_string.h b/include/tag_string.h index f1b622eee9..7b71e9122a 100644 --- a/include/tag_string.h +++ b/include/tag_string.h @@ -53,6 +53,8 @@ private: std::string value; bool equals(const tag& rhs) const override; + + tag_string& assign(tag&& rhs) override; }; bool operator==(const tag_string& lhs, const tag_string& rhs); diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index 53fd9df8ac..1d4fd6d720 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -94,6 +94,11 @@ bool tag_compound::equals(const tag& rhs) const return *this == static_cast(rhs); } +tag_compound& tag_compound::assign(tag&& rhs) +{ + return *this = dynamic_cast(rhs); +} + bool operator==(const tag_compound& lhs, const tag_compound& rhs) { return lhs.tags == rhs.tags; diff --git a/src/tag_string.cpp b/src/tag_string.cpp index b3ef075cd3..aade42768b 100644 --- a/src/tag_string.cpp +++ b/src/tag_string.cpp @@ -82,6 +82,11 @@ bool tag_string::equals(const tag& rhs) const return *this == static_cast(rhs); } +tag_string& tag_string::assign(tag&& rhs) +{ + return *this = dynamic_cast(rhs); +} + bool operator==(const tag_string& lhs, const tag_string& rhs) { return lhs.get() == rhs.get(); diff --git a/src/value.cpp b/src/value.cpp index 339c737505..a5f8e3c894 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -19,6 +19,7 @@ */ #include "value.h" #include "libnbt.h" +#include namespace nbt { @@ -35,7 +36,8 @@ value& value::operator=(std::unique_ptr&& t) value& value::operator=(tag&& t) { - return *this = std::move(t).move_clone(); + tag_->assign(std::move(t)); + return *this; } tag_type value::get_type() const -- cgit 0.0.5-2-1-g0f52 From 900db15d5471fd68468baa31c822b2312054c435 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 1 Jul 2015 20:11:11 +0200 Subject: Clarify move-assignment --- include/tag.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/tag.h b/include/tag.h index a9cbc0019f..980c0437ff 100644 --- a/include/tag.h +++ b/include/tag.h @@ -66,7 +66,7 @@ private: virtual bool equals(const tag& rhs) const = 0; /** - * @brief Assigns the given tag if the class is the same + * @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; -- cgit 0.0.5-2-1-g0f52 From e0ff3b56c05225dd7da7d973af4c708966379a12 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 1 Jul 2015 20:15:06 +0200 Subject: Minor reformatting --- include/tag_compound.h | 7 +++---- include/tag_primitive.h | 1 - include/tag_string.h | 1 - 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/include/tag_compound.h b/include/tag_compound.h index 32380a0c6c..1b6e214b10 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -108,18 +108,17 @@ public: const_iterator cend() const; tag_type get_type() const noexcept override; - std::unique_ptr move_clone() && override; + friend bool operator==(const tag_compound& lhs, const tag_compound& rhs); + friend bool operator!=(const tag_compound& lhs, const tag_compound& rhs); + private: std::map tags; bool equals(const tag& rhs) const override; tag_compound& assign(tag&& rhs) override; - - friend bool operator==(const tag_compound& lhs, const tag_compound& rhs); - friend bool operator!=(const tag_compound& lhs, const tag_compound& rhs); }; template diff --git a/include/tag_primitive.h b/include/tag_primitive.h index 9c4cd3b2b9..c58cb891ff 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -52,7 +52,6 @@ public: void set(T value); tag_type get_type() const noexcept override; - std::unique_ptr move_clone() && override; private: diff --git a/include/tag_string.h b/include/tag_string.h index 7b71e9122a..b28476da2c 100644 --- a/include/tag_string.h +++ b/include/tag_string.h @@ -46,7 +46,6 @@ public: void set(std::string&& str); tag_type get_type() const noexcept override; - std::unique_ptr move_clone() && override; private: -- cgit 0.0.5-2-1-g0f52 From ffaf121d24428a9d822c1e3de512b9593be7aa0e Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 1 Jul 2015 21:26:28 +0200 Subject: Implement CRTP for tag --- include/crtp_tag.h | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ include/tag_compound.h | 9 ++----- include/tag_primitive.h | 27 ++------------------ include/tag_string.h | 9 ++----- src/tag_compound.cpp | 15 ----------- src/tag_string.cpp | 15 ----------- 6 files changed, 72 insertions(+), 69 deletions(-) create mode 100644 include/crtp_tag.h diff --git a/include/crtp_tag.h b/include/crtp_tag.h new file mode 100644 index 0000000000..f35b9d0c9a --- /dev/null +++ b/include/crtp_tag.h @@ -0,0 +1,66 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#ifndef CRTP_TAG_H_INCLUDED +#define CRTP_TAG_H_INCLUDED + +#include "tag.h" + +namespace nbt +{ + +namespace detail +{ + + template + class crtp_tag : public tag + { + public: + std::unique_ptr move_clone() && override final; + + private: + bool equals(const tag& rhs) const override final; + tag& assign(tag&& rhs) override final; + }; + + template + std::unique_ptr crtp_tag::move_clone() && + { + return std::unique_ptr( + new Sub(static_cast(*this)) + ); + } + + template + bool crtp_tag::equals(const tag& rhs) const + { + return *this == static_cast(rhs); + } + + template + tag& crtp_tag::assign(tag&& rhs) + { + return *this = dynamic_cast(rhs); + } + +} + +} + +#endif // CRTP_TAG_H_INCLUDED diff --git a/include/tag_compound.h b/include/tag_compound.h index 1b6e214b10..265479c8af 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -20,7 +20,7 @@ #ifndef TAG_COMPOUND_H_INCLUDED #define TAG_COMPOUND_H_INCLUDED -#include "tag.h" +#include "crtp_tag.h" #include "value.h" #include #include @@ -29,7 +29,7 @@ namespace nbt { ///Tag that contains multiple unordered named tags of arbitrary types -class tag_compound final : public tag +class tag_compound final : public detail::crtp_tag { public: //Iterator types @@ -108,17 +108,12 @@ public: const_iterator cend() const; tag_type get_type() const noexcept override; - std::unique_ptr move_clone() && override; friend bool operator==(const tag_compound& lhs, const tag_compound& rhs); friend bool operator!=(const tag_compound& lhs, const tag_compound& rhs); private: std::map tags; - - bool equals(const tag& rhs) const override; - - tag_compound& assign(tag&& rhs) override; }; template diff --git a/include/tag_primitive.h b/include/tag_primitive.h index c58cb891ff..6087495048 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -20,7 +20,7 @@ #ifndef TAG_PRIMITIVE_H_INCLUDED #define TAG_PRIMITIVE_H_INCLUDED -#include "tag.h" +#include "crtp_tag.h" #include "primitive_detail.h" #include @@ -33,7 +33,7 @@ namespace nbt * Common class for tag_byte, tag_short, tag_int, tag_long, tag_float and tag_double. */ template -class tag_primitive final : public tag +class tag_primitive final : public detail::crtp_tag> { public: ///The type of the value @@ -52,14 +52,9 @@ public: void set(T value); tag_type get_type() const noexcept override; - std::unique_ptr move_clone() && override; private: T value; - - bool equals(const tag& rhs) const override; - - tag_primitive& assign(tag&& rhs) override; }; template bool operator==(const tag_primitive& lhs, const tag_primitive& rhs); @@ -115,24 +110,6 @@ tag_type tag_primitive::get_type() const noexcept return type; } -template -std::unique_ptr tag_primitive::move_clone() && -{ - return std::unique_ptr(new tag_primitive(std::move(*this))); -} - -template -bool tag_primitive::equals(const tag& rhs) const -{ - return *this == static_cast&>(rhs); -} - -template -tag_primitive& tag_primitive::assign(tag&& rhs) -{ - return *this = dynamic_cast&&>(rhs); -} - template bool operator==(const tag_primitive& lhs, const tag_primitive& rhs) { diff --git a/include/tag_string.h b/include/tag_string.h index b28476da2c..3c514bae96 100644 --- a/include/tag_string.h +++ b/include/tag_string.h @@ -20,14 +20,14 @@ #ifndef TAG_STRING_H_INCLUDED #define TAG_STRING_H_INCLUDED -#include "tag.h" +#include "crtp_tag.h" #include namespace nbt { ///Tag that contains a UTF-8 string -class tag_string final : public tag +class tag_string final : public detail::crtp_tag { public: ///The type of the tag @@ -46,14 +46,9 @@ public: void set(std::string&& str); tag_type get_type() const noexcept override; - std::unique_ptr move_clone() && override; private: std::string value; - - bool equals(const tag& rhs) const override; - - tag_string& assign(tag&& rhs) override; }; bool operator==(const tag_string& lhs, const tag_string& rhs); diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index 1d4fd6d720..6aa12e64ff 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -84,21 +84,6 @@ tag_type tag_compound::get_type() const noexcept return type; } -std::unique_ptr tag_compound::move_clone() && -{ - return std::unique_ptr(new tag_compound(std::move(*this))); -} - -bool tag_compound::equals(const tag& rhs) const -{ - return *this == static_cast(rhs); -} - -tag_compound& tag_compound::assign(tag&& rhs) -{ - return *this = dynamic_cast(rhs); -} - bool operator==(const tag_compound& lhs, const tag_compound& rhs) { return lhs.tags == rhs.tags; diff --git a/src/tag_string.cpp b/src/tag_string.cpp index aade42768b..1f1e242e3a 100644 --- a/src/tag_string.cpp +++ b/src/tag_string.cpp @@ -72,21 +72,6 @@ tag_type tag_string::get_type() const noexcept return type; } -std::unique_ptr tag_string::move_clone() && -{ - return std::unique_ptr(new tag_string(std::move(*this))); -} - -bool tag_string::equals(const tag& rhs) const -{ - return *this == static_cast(rhs); -} - -tag_string& tag_string::assign(tag&& rhs) -{ - return *this = dynamic_cast(rhs); -} - bool operator==(const tag_string& lhs, const tag_string& rhs) { return lhs.get() == rhs.get(); -- cgit 0.0.5-2-1-g0f52 From 3dfa9f71eedd26eaaaba60573d2a6b7a2ab60b8c Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 1 Jul 2015 21:44:33 +0200 Subject: Also define get_type with CRTP --- include/crtp_tag.h | 14 ++++++++++++++ include/tag_compound.h | 2 -- include/tag_primitive.h | 8 -------- include/tag_string.h | 2 -- src/tag_compound.cpp | 5 ----- src/tag_string.cpp | 5 ----- 6 files changed, 14 insertions(+), 22 deletions(-) diff --git a/include/crtp_tag.h b/include/crtp_tag.h index f35b9d0c9a..fa28a74d41 100644 --- a/include/crtp_tag.h +++ b/include/crtp_tag.h @@ -32,6 +32,11 @@ namespace detail class crtp_tag : public tag { public: + //Pure virtual destructor to make the class abstract + virtual ~crtp_tag() = 0; + + tag_type get_type() const noexcept override final; + std::unique_ptr move_clone() && override final; private: @@ -39,6 +44,15 @@ namespace detail tag& assign(tag&& rhs) override final; }; + template + crtp_tag::~crtp_tag() {} + + template + tag_type crtp_tag::get_type() const noexcept + { + return Sub::type; + } + template std::unique_ptr crtp_tag::move_clone() && { diff --git a/include/tag_compound.h b/include/tag_compound.h index 265479c8af..ddd96e0a58 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -107,8 +107,6 @@ public: const_iterator cbegin() const; const_iterator cend() const; - tag_type get_type() const noexcept override; - friend bool operator==(const tag_compound& lhs, const tag_compound& rhs); friend bool operator!=(const tag_compound& lhs, const tag_compound& rhs); diff --git a/include/tag_primitive.h b/include/tag_primitive.h index 6087495048..e839be7da0 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -51,8 +51,6 @@ public: tag_primitive& operator=(T value); void set(T value); - tag_type get_type() const noexcept override; - private: T value; }; @@ -104,12 +102,6 @@ T tag_primitive::get() const return value; } -template -tag_type tag_primitive::get_type() const noexcept -{ - return type; -} - template bool operator==(const tag_primitive& lhs, const tag_primitive& rhs) { diff --git a/include/tag_string.h b/include/tag_string.h index 3c514bae96..f4476993e3 100644 --- a/include/tag_string.h +++ b/include/tag_string.h @@ -45,8 +45,6 @@ public: void set(const std::string& str); void set(std::string&& str); - tag_type get_type() const noexcept override; - private: std::string value; }; diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index 6aa12e64ff..6bc83301e2 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -79,11 +79,6 @@ auto tag_compound::end() const -> const_iterator { return tags.end(); } auto tag_compound::cbegin() const -> const_iterator { return tags.cbegin(); } auto tag_compound::cend() const -> const_iterator { return tags.cend(); } -tag_type tag_compound::get_type() const noexcept -{ - return type; -} - bool operator==(const tag_compound& lhs, const tag_compound& rhs) { return lhs.tags == rhs.tags; diff --git a/src/tag_string.cpp b/src/tag_string.cpp index 1f1e242e3a..6bd1465e84 100644 --- a/src/tag_string.cpp +++ b/src/tag_string.cpp @@ -67,11 +67,6 @@ void tag_string::set(std::string&& str) value = std::move(str); } -tag_type tag_string::get_type() const noexcept -{ - return type; -} - bool operator==(const tag_string& lhs, const tag_string& rhs) { return lhs.get() == rhs.get(); -- cgit 0.0.5-2-1-g0f52 From 452a495e3c8cc09525dca777f0ead6f24b904a1c Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 1 Jul 2015 22:27:09 +0200 Subject: Give meaningful fail message for wrong primitive --- include/primitive_detail.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/primitive_detail.h b/include/primitive_detail.h index c7e2a05206..dea7ff225f 100644 --- a/include/primitive_detail.h +++ b/include/primitive_detail.h @@ -29,7 +29,10 @@ namespace nbt namespace detail { ///Meta-struct that holds the tag_type value for a specific primitive type - template struct get_primitive_type; + template struct get_primitive_type + { + static_assert(sizeof(T) != sizeof(T), "Can only use primitive types used by NBT as parameter for tag_primitive"); + }; template<> struct get_primitive_type : public std::integral_constant {}; template<> struct get_primitive_type : public std::integral_constant {}; -- cgit 0.0.5-2-1-g0f52 From ba86b862d679b358c52aa925044365c8d6057e08 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 2 Jul 2015 09:04:06 +0200 Subject: Add get/set_ptr and operator bool to value --- include/value.h | 15 ++++++++++----- src/tag_compound.cpp | 2 +- src/value.cpp | 24 +++++++++++++++++++----- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/include/value.h b/include/value.h index 491a92b2a0..a4ccf22873 100644 --- a/include/value.h +++ b/include/value.h @@ -34,13 +34,14 @@ class tag_list; /** * @brief Contains an NBT value of fixed type * - * A wrapper class that contains a dynamically allocated tag of a fixed type. - * Casting or assigning incompatible types will throw a exceptions. + * A convenience wrapper around @c std::unique_ptr, contains a tag of + * fixed type. */ class value { public: explicit value() {} + explicit value(std::unique_ptr&& t); //Movable but not (implicitly) copyable value(const value&) = delete; @@ -48,9 +49,6 @@ public: value& operator=(const value&) = delete; value& operator=(value&&) = default; - explicit value(std::unique_ptr&& t); - value& operator=(std::unique_ptr&& t); - /** * @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 @@ -90,6 +88,9 @@ public: operator double() const; operator const std::string&() const; + ///Returns true if the contained tag is not @c nullptr + explicit operator bool() const; + /** * @brief In case of a tag_compound, accesses a tag by key with bounds checking * @throw std::bad_cast if the tag type is not tag_compound @@ -107,6 +108,10 @@ public: value& operator[](const std::string& key); const value& operator[](const std::string& key) const; + std::unique_ptr& get_ptr(); + const std::unique_ptr& get_ptr() const; + void set_ptr(std::unique_ptr&& t); + ///@sa tag::get_type tag_type get_type() const; diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index 6bc83301e2..94dc2dd6f1 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -42,7 +42,7 @@ bool tag_compound::put(const std::string& key, std::unique_ptr&& t) auto it = tags.find(key); if(it != tags.end()) { - it->second = std::move(t); + it->second.set_ptr(std::move(t)); return false; } else diff --git a/src/value.cpp b/src/value.cpp index a5f8e3c894..f062e08a09 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -28,16 +28,30 @@ value::value(std::unique_ptr&& t): tag_(std::move(t)) {} -value& value::operator=(std::unique_ptr&& t) +value& value::operator=(tag&& t) { - tag_ = std::move(t); + tag_->assign(std::move(t)); return *this; } -value& value::operator=(tag&& t) +value::operator bool() const { - tag_->assign(std::move(t)); - return *this; + return tag_ != nullptr; +} + +std::unique_ptr& value::get_ptr() +{ + return tag_; +} + +const std::unique_ptr& value::get_ptr() const +{ + return tag_; +} + +void value::set_ptr(std::unique_ptr&& t) +{ + tag_ = std::move(t); } tag_type value::get_type() const -- cgit 0.0.5-2-1-g0f52 From eaced354e778313f31efb3aedb62d52c0f761829 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Fri, 3 Jul 2015 09:24:05 +0200 Subject: Fix tag_compound::emplace --- include/tag_compound.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/tag_compound.h b/include/tag_compound.h index ddd96e0a58..526e57d559 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -117,7 +117,7 @@ private: template bool tag_compound::emplace(const std::string& key, Args&&... args) { - return put(key, new T(std::forward(args)...)); + return put(key, std::unique_ptr(new T(std::forward(args)...))); } } -- cgit 0.0.5-2-1-g0f52 From 7942ff1e82dc305f09c744fad590d4d6783a0c90 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Fri, 3 Jul 2015 09:37:08 +0200 Subject: Add more tests for tag_compound --- test/nbttest.cpp | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 419421f1b8..4f05d30068 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -89,10 +89,18 @@ void test_tag_compound() EXPECT_EXCEPTION(int8_t(comp["foo"]), std::bad_cast); EXPECT_EXCEPTION(std::string(comp["foo"]), std::bad_cast); + EXPECT_EXCEPTION(comp["foo"] = 32, std::bad_cast); + comp["foo"] = int8_t(32); + ASSERT(int8_t(comp["foo"]) == 32); + ASSERT(comp["bar"].get_type() == tag_type::String); ASSERT(std::string(comp["bar"]) == "baz"); EXPECT_EXCEPTION(int(comp["bar"]), std::bad_cast); + EXPECT_EXCEPTION(comp["bar"] = -128, std::bad_cast); + comp["bar"] = "barbaz"; + ASSERT(std::string(comp["foo"]) == "barbaz"); + ASSERT(comp["baz"].get_type() == tag_type::Double); ASSERT(double(comp["baz"]) == -2.0); EXPECT_EXCEPTION(float(comp["baz"]), std::bad_cast); @@ -104,8 +112,8 @@ void test_tag_compound() EXPECT_EXCEPTION(comp.at("nothing"), std::out_of_range); tag_compound comp2/*{ - {"foo", int16_t(12)}, - {"bar", "baz"}, + {"foo", int16_t(32)}, + {"bar", "barbaz"}, {"baz", -2.0}, {"quux", tag_compound{{"Hello", "World"}, {"zero", 0}}} }*/; @@ -113,7 +121,16 @@ void test_tag_compound() ASSERT(comp != (const tag_compound&)comp2["quux"]); ASSERT(comp != comp2["quux"]); - ASSERT(comp.size() == 4); + ASSERT(comp2.size() == 4); + const char* keys[] = {"bar", "baz", "foo", "quux"}; //alphabetic order + unsigned i = 0; + for(const std::pair& val: comp2) + { + ASSERT(i < comp2.size()); + ASSERT(val.first == keys[i]); + ASSERT(val.second == comp2[keys[i]]); + ++i; + } ASSERT(comp.erase("nothing") == false); ASSERT(comp.has_key("quux")); @@ -122,6 +139,12 @@ void test_tag_compound() comp.clear(); ASSERT(comp == tag_compound{}); + + ASSERT(comp.put("abc", std::unique_ptr(new tag_double(6.0))) == true); + ASSERT(comp.put("abc", std::unique_ptr(new tag_long(-28))) == false); + ASSERT(comp.emplace("def", "ghi") == true); + ASSERT(comp.emplace("def", 4) == false); + ASSERT((comp == tag_compound{/*{"abc", tag_long(-28)}, {"def", tag_byte(4)}*/})); } int main() -- cgit 0.0.5-2-1-g0f52 From bb41bd0013c9c0fec657d352bfcbea116c4c696f Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Fri, 3 Jul 2015 11:09:53 +0200 Subject: Remove useless explicit --- include/value.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/value.h b/include/value.h index a4ccf22873..afcc7c2624 100644 --- a/include/value.h +++ b/include/value.h @@ -40,7 +40,7 @@ class tag_list; class value { public: - explicit value() {} + value() {} explicit value(std::unique_ptr&& t); //Movable but not (implicitly) copyable -- cgit 0.0.5-2-1-g0f52 From 56da3e25fa303ce509e5953db4cdd8a7985cf2c5 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Fri, 3 Jul 2015 11:34:02 +0200 Subject: Reorder and change documentation for value --- include/value.h | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/include/value.h b/include/value.h index afcc7c2624..06f86e3a21 100644 --- a/include/value.h +++ b/include/value.h @@ -56,6 +56,10 @@ public: */ value& operator=(tag&& t); + //Conversion to tag + operator tag&(); + operator const tag&() const; + //Assignment of primitives and string /** * @brief Assigns the given value to the tag if the type is compatible @@ -68,15 +72,17 @@ public: value& operator=(int64_t val); value& operator=(float val); value& operator=(double val); - value& operator=(const std::string& str); - //Conversion to tag - operator tag&(); - operator const tag&() const; + /** + * @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 Casts the value if the type matches + * @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 */ @@ -86,9 +92,14 @@ public: operator int64_t() const; operator float() const; operator double() const; + + /** + * @brief Returns the contained string if the type is tag_string + * @throw std::bad_cast if the tag type is not tag_string + */ operator const std::string&() const; - ///Returns true if the contained tag is not @c nullptr + ///Returns true if the value is not uninitialized explicit operator bool() const; /** -- cgit 0.0.5-2-1-g0f52 From 3bf8cd98635d57e3c6d132bb3d9ffcc6eceab51e Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Fri, 3 Jul 2015 11:35:23 +0200 Subject: Implement cast and assignment of string for value --- src/value.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/value.cpp b/src/value.cpp index f062e08a09..1f8e522e83 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -34,6 +34,22 @@ value& value::operator=(tag&& t) return *this; } +value& value::operator=(const std::string& str) +{ + return *this = std::move(std::string(str)); +} + +value& value::operator=(std::string&& str) +{ + dynamic_cast(*tag_).set(std::move(str)); + return *this; +} + +value::operator const std::string&() const +{ + return dynamic_cast(*tag_).get(); +} + value::operator bool() const { return tag_ != nullptr; -- cgit 0.0.5-2-1-g0f52 From a511f7fa197fa417646fa9852bf245b45fa17119 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Fri, 3 Jul 2015 16:38:48 +0200 Subject: Preliminarily implement primitive conversion needs to be done in a better way for sure --- src/value.cpp | 234 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 234 insertions(+) diff --git a/src/value.cpp b/src/value.cpp index 1f8e522e83..77358a9182 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -34,6 +34,240 @@ value& value::operator=(tag&& t) return *this; } +//Primitive assignment +//FIXME: Make this less copypaste! +value& value::operator=(int8_t val) +{ + switch(tag_->get_type()) + { + case tag_type::Byte: + static_cast(*tag_).set(val); + break; + case tag_type::Short: + static_cast(*tag_).set(val); + break; + case tag_type::Int: + static_cast(*tag_).set(val); + break; + case tag_type::Long: + static_cast(*tag_).set(val); + break; + case tag_type::Float: + static_cast(*tag_).set(val); + break; + case tag_type::Double: + static_cast(*tag_).set(val); + break; + + default: + throw std::bad_cast(); + } + return *this; +} + +value& value::operator=(int16_t val) +{ + switch(tag_->get_type()) + { + case tag_type::Short: + static_cast(*tag_).set(val); + break; + case tag_type::Int: + static_cast(*tag_).set(val); + break; + case tag_type::Long: + static_cast(*tag_).set(val); + break; + case tag_type::Float: + static_cast(*tag_).set(val); + break; + case tag_type::Double: + static_cast(*tag_).set(val); + break; + + default: + throw std::bad_cast(); + } + return *this; +} + +value& value::operator=(int32_t val) +{ + switch(tag_->get_type()) + { + case tag_type::Int: + static_cast(*tag_).set(val); + break; + case tag_type::Long: + static_cast(*tag_).set(val); + break; + case tag_type::Float: + static_cast(*tag_).set(val); + break; + case tag_type::Double: + static_cast(*tag_).set(val); + break; + + default: + throw std::bad_cast(); + } + return *this; +} + +value& value::operator=(int64_t val) +{ + switch(tag_->get_type()) + { + case tag_type::Long: + static_cast(*tag_).set(val); + break; + case tag_type::Float: + static_cast(*tag_).set(val); + break; + case tag_type::Double: + static_cast(*tag_).set(val); + break; + + default: + throw std::bad_cast(); + } + return *this; +} + +value& value::operator=(float val) +{ + switch(tag_->get_type()) + { + case tag_type::Float: + static_cast(*tag_).set(val); + break; + case tag_type::Double: + static_cast(*tag_).set(val); + break; + + default: + throw std::bad_cast(); + } + return *this; +} + +value& value::operator=(double val) +{ + switch(tag_->get_type()) + { + case tag_type::Double: + static_cast(*tag_).set(val); + break; + + default: + throw std::bad_cast(); + } + return *this; +} + +//Primitive conversion +value::operator int8_t() const +{ + switch(tag_->get_type()) + { + case tag_type::Byte: + return static_cast(*tag_).get(); + + default: + throw std::bad_cast(); + } +} + +value::operator int16_t() const +{ + switch(tag_->get_type()) + { + case tag_type::Byte: + return static_cast(*tag_).get(); + case tag_type::Short: + return static_cast(*tag_).get(); + + default: + throw std::bad_cast(); + } +} + +value::operator int32_t() const +{ + switch(tag_->get_type()) + { + case tag_type::Byte: + return static_cast(*tag_).get(); + case tag_type::Short: + return static_cast(*tag_).get(); + case tag_type::Int: + return static_cast(*tag_).get(); + + default: + throw std::bad_cast(); + } +} + +value::operator int64_t() const +{ + switch(tag_->get_type()) + { + case tag_type::Byte: + return static_cast(*tag_).get(); + case tag_type::Short: + return static_cast(*tag_).get(); + case tag_type::Int: + return static_cast(*tag_).get(); + case tag_type::Long: + return static_cast(*tag_).get(); + + default: + throw std::bad_cast(); + } +} + +value::operator float() const +{ + switch(tag_->get_type()) + { + case tag_type::Byte: + return static_cast(*tag_).get(); + case tag_type::Short: + return static_cast(*tag_).get(); + case tag_type::Int: + return static_cast(*tag_).get(); + case tag_type::Long: + return static_cast(*tag_).get(); + case tag_type::Float: + return static_cast(*tag_).get(); + + default: + throw std::bad_cast(); + } +} + +value::operator double() const +{ + switch(tag_->get_type()) + { + case tag_type::Byte: + return static_cast(*tag_).get(); + case tag_type::Short: + return static_cast(*tag_).get(); + case tag_type::Int: + return static_cast(*tag_).get(); + case tag_type::Long: + return static_cast(*tag_).get(); + case tag_type::Float: + return static_cast(*tag_).get(); + case tag_type::Double: + return static_cast(*tag_).get(); + + default: + throw std::bad_cast(); + } +} + value& value::operator=(const std::string& str) { return *this = std::move(std::string(str)); -- cgit 0.0.5-2-1-g0f52 From 33c26a33d1cf30f36c1d9c641f6ffc4dd77a5861 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Fri, 3 Jul 2015 16:43:40 +0200 Subject: Create and implement get/set with tag for value --- include/value.h | 3 +++ src/value.cpp | 27 ++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/include/value.h b/include/value.h index 06f86e3a21..3b03cc0d43 100644 --- a/include/value.h +++ b/include/value.h @@ -55,10 +55,13 @@ public: * of this value */ value& operator=(tag&& t); + void set(tag&& t); //Conversion to tag operator tag&(); operator const tag&() const; + tag& get(); + const tag& get() const; //Assignment of primitives and string /** diff --git a/src/value.cpp b/src/value.cpp index 77358a9182..90a67b79f3 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -30,10 +30,35 @@ value::value(std::unique_ptr&& t): value& value::operator=(tag&& t) { - tag_->assign(std::move(t)); + set(std::move(t)); return *this; } +void value::set(tag&& t) +{ + tag_->assign(std::move(t)); +} + +value::operator tag&() +{ + return get(); +} + +value::operator const tag&() const +{ + return get(); +} + +tag& value::get() +{ + return *tag_; +} + +const tag& value::get() const +{ + return *tag_; +} + //Primitive assignment //FIXME: Make this less copypaste! value& value::operator=(int8_t val) -- cgit 0.0.5-2-1-g0f52 From b64e3db8d02ba4668a3175163fe438c57cff16e4 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Fri, 3 Jul 2015 16:51:34 +0200 Subject: Implement at and operator[] for value --- include/value.h | 1 - src/value.cpp | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/include/value.h b/include/value.h index 3b03cc0d43..a46d79e7c5 100644 --- a/include/value.h +++ b/include/value.h @@ -120,7 +120,6 @@ public: * @sa tag_compound::operator[] */ value& operator[](const std::string& key); - const value& operator[](const std::string& key) const; std::unique_ptr& get_ptr(); const std::unique_ptr& get_ptr() const; diff --git a/src/value.cpp b/src/value.cpp index 90a67b79f3..8196ce16e2 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -314,6 +314,21 @@ value::operator bool() const return tag_ != nullptr; } +value& value::at(const std::string& key) +{ + return dynamic_cast(*tag_).at(key); +} + +const value& value::at(const std::string& key) const +{ + return dynamic_cast(*tag_).at(key); +} + +value& value::operator[](const std::string& key) +{ + return dynamic_cast(*tag_)[key]; +} + std::unique_ptr& value::get_ptr() { return tag_; -- cgit 0.0.5-2-1-g0f52 From c9bbfc47734709221a64381e04b3e823a98b06ff Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Fri, 3 Jul 2015 18:45:44 +0200 Subject: Readd value constructor from tag&& --- include/value.h | 1 + src/value.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/include/value.h b/include/value.h index a46d79e7c5..3aa056b35c 100644 --- a/include/value.h +++ b/include/value.h @@ -42,6 +42,7 @@ class value public: value() {} explicit value(std::unique_ptr&& t); + explicit value(tag&& t); //Movable but not (implicitly) copyable value(const value&) = delete; diff --git a/src/value.cpp b/src/value.cpp index 8196ce16e2..ae40587324 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -28,6 +28,10 @@ value::value(std::unique_ptr&& t): tag_(std::move(t)) {} +value::value(tag&& t): + tag_(std::move(t).move_clone()) +{} + value& value::operator=(tag&& t) { set(std::move(t)); -- cgit 0.0.5-2-1-g0f52 From 09dca802bdfc076ac8457df0b8f657fdce915a75 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Fri, 3 Jul 2015 18:59:13 +0200 Subject: Remove unneeded forward declarations --- include/value.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/value.h b/include/value.h index 3aa056b35c..4116e2d0c1 100644 --- a/include/value.h +++ b/include/value.h @@ -27,10 +27,6 @@ namespace nbt { -//Forward declarations -class tag_compound; -class tag_list; - /** * @brief Contains an NBT value of fixed type * -- cgit 0.0.5-2-1-g0f52 From 36c1393a5682a7cab500d3f0ca2cb45ddb0a844f Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Fri, 3 Jul 2015 19:38:52 +0200 Subject: Create value_initializer --- include/tag_compound.h | 4 ++-- include/value_initializer.h | 58 +++++++++++++++++++++++++++++++++++++++++++++ src/tag_compound.cpp | 6 +++++ test/nbttest.cpp | 10 ++++---- 4 files changed, 71 insertions(+), 7 deletions(-) create mode 100644 include/value_initializer.h diff --git a/include/tag_compound.h b/include/tag_compound.h index 526e57d559..3c68fc1b12 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -22,6 +22,7 @@ #include "crtp_tag.h" #include "value.h" +#include "value_initializer.h" #include #include @@ -42,8 +43,7 @@ public: ///Constructs an empty compound tag_compound() {} - //TODO: Make a separate class similar to and convertible to value for initializing tag values - //tag_compound(std::initializer_list> init); + tag_compound(std::initializer_list> init); /** * @brief Accesses a tag by key with bounds checking diff --git a/include/value_initializer.h b/include/value_initializer.h new file mode 100644 index 0000000000..86fdd2d1e1 --- /dev/null +++ b/include/value_initializer.h @@ -0,0 +1,58 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#ifndef VALUE_INITIALIZER_H_INCLUDED +#define VALUE_INITIALIZER_H_INCLUDED + +#include "value.h" +#include +#include + +namespace nbt +{ + +/** + * @brief Helper class for implicitly constructing value objects + */ +class value_initializer +{ +public: + value_initializer(std::unique_ptr&& t); + value_initializer(value&& val); + value_initializer(tag&& 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); + + operator value&&(); + +private: + value val; +}; + +} + +#endif // VALUE_INITIALIZER_H_INCLUDED diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index 94dc2dd6f1..f26044ce20 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -22,6 +22,12 @@ namespace nbt { +tag_compound::tag_compound(std::initializer_list> init) +{ + for(auto& pair: init) + tags.emplace(pair); +} + value& tag_compound::at(const std::string& key) { return tags.at(key); diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 4f05d30068..859b41f7a8 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -77,11 +77,11 @@ void test_tag_string() void test_tag_compound() { - tag_compound comp/*{ + tag_compound comp{ {"foo", int16_t(12)}, {"bar", "baz"}, {"baz", -2.0} - }*/; + }; ASSERT(comp["foo"].get_type() == tag_type::Short); ASSERT(int32_t(comp["foo"]) == 12); @@ -111,12 +111,12 @@ void test_tag_compound() EXPECT_EXCEPTION(comp.at("nothing"), std::out_of_range); - tag_compound comp2/*{ + tag_compound comp2{ {"foo", int16_t(32)}, {"bar", "barbaz"}, {"baz", -2.0}, {"quux", tag_compound{{"Hello", "World"}, {"zero", 0}}} - }*/; + }; ASSERT(comp == comp2); ASSERT(comp != (const tag_compound&)comp2["quux"]); ASSERT(comp != comp2["quux"]); @@ -144,7 +144,7 @@ void test_tag_compound() ASSERT(comp.put("abc", std::unique_ptr(new tag_long(-28))) == false); ASSERT(comp.emplace("def", "ghi") == true); ASSERT(comp.emplace("def", 4) == false); - ASSERT((comp == tag_compound{/*{"abc", tag_long(-28)}, {"def", tag_byte(4)}*/})); + ASSERT((comp == tag_compound{{"abc", tag_long(-28)}, {"def", tag_byte(4)}})); } int main() -- cgit 0.0.5-2-1-g0f52 From cdcbcd5adf8b987baaf602450d129afcf4d8fdf7 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sat, 4 Jul 2015 10:04:45 +0200 Subject: Implement value_initializer --- CMakeLists.txt | 3 ++- include/tag_compound.h | 2 +- include/value_initializer.h | 5 +++-- src/tag_compound.cpp | 6 +++--- src/value_initializer.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 src/value_initializer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f49a64ff7..8474392677 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,8 @@ add_library(nbt++ STATIC src/tag.cpp src/tag_compound.cpp src/tag_string.cpp - src/value.cpp) + src/value.cpp + src/value_initializer.cpp) enable_testing() add_subdirectory(test) diff --git a/include/tag_compound.h b/include/tag_compound.h index 3c68fc1b12..ea0df236c9 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -43,7 +43,7 @@ public: ///Constructs an empty compound tag_compound() {} - tag_compound(std::initializer_list> init); + tag_compound(std::initializer_list> init); /** * @brief Accesses a tag by key with bounds checking diff --git a/include/value_initializer.h b/include/value_initializer.h index 86fdd2d1e1..7ae2cb13f4 100644 --- a/include/value_initializer.h +++ b/include/value_initializer.h @@ -47,10 +47,11 @@ public: value_initializer(std::string&& str); value_initializer(const char* str); - operator value&&(); + value& get() const; private: - value val; + mutable value value_; //FIXME: std::initializer_list objects are constant. + //This solution will probably fail }; } diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index f26044ce20..a2b91826b6 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -22,10 +22,10 @@ namespace nbt { -tag_compound::tag_compound(std::initializer_list> init) +tag_compound::tag_compound(std::initializer_list> init) { - for(auto& pair: init) - tags.emplace(pair); + for(const auto& pair: init) + tags.emplace(std::move(pair.first), std::move(pair.second.get())); } value& tag_compound::at(const std::string& key) diff --git a/src/value_initializer.cpp b/src/value_initializer.cpp new file mode 100644 index 0000000000..8d57933e09 --- /dev/null +++ b/src/value_initializer.cpp @@ -0,0 +1,42 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include "value_initializer.h" +#include "libnbt.h" + +namespace nbt +{ + +value_initializer::value_initializer(std::unique_ptr&& t): value_(std::move(t)) {} +value_initializer::value_initializer(value&& val): value_(std::move(val)) {} +value_initializer::value_initializer(tag&& t): value_(std::move(t)) {} + +value_initializer::value_initializer(int8_t val): value_(tag_byte(val)) {} +value_initializer::value_initializer(int16_t val): value_(tag_short(val)) {} +value_initializer::value_initializer(int32_t val): value_(tag_int(val)) {} +value_initializer::value_initializer(int64_t val): value_(tag_long(val)) {} +value_initializer::value_initializer(float val): value_(tag_float(val)) {} +value_initializer::value_initializer(double val): value_(tag_double(val)) {} +value_initializer::value_initializer(const std::string& str): value_(tag_string(str)) {} +value_initializer::value_initializer(std::string&& str): value_(tag_string(std::move(str))) {} +value_initializer::value_initializer(const char* str): value_(tag_string(str)) {} + +value& value_initializer::get() const { return value_; } + +} -- cgit 0.0.5-2-1-g0f52 From 3a360962bc6a9c5b105cab490cf3bcf74e6959e3 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sat, 4 Jul 2015 10:04:52 +0200 Subject: Fix test --- test/nbttest.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 859b41f7a8..d8f0f68d15 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -91,7 +91,7 @@ void test_tag_compound() EXPECT_EXCEPTION(comp["foo"] = 32, std::bad_cast); comp["foo"] = int8_t(32); - ASSERT(int8_t(comp["foo"]) == 32); + ASSERT(int16_t(comp["foo"]) == 32); ASSERT(comp["bar"].get_type() == tag_type::String); ASSERT(std::string(comp["bar"]) == "baz"); @@ -99,13 +99,13 @@ void test_tag_compound() EXPECT_EXCEPTION(comp["bar"] = -128, std::bad_cast); comp["bar"] = "barbaz"; - ASSERT(std::string(comp["foo"]) == "barbaz"); + ASSERT(std::string(comp["bar"]) == "barbaz"); ASSERT(comp["baz"].get_type() == tag_type::Double); ASSERT(double(comp["baz"]) == -2.0); EXPECT_EXCEPTION(float(comp["baz"]), std::bad_cast); - comp["quux"] = tag_compound{/*{"Hello", "World"}, {"zero", 0}*/}; + comp["quux"] = tag_compound{{"Hello", "World"}, {"zero", 0}}; ASSERT(comp.at("quux").get_type() == tag_type::Compound); ASSERT(std::string(comp["quux"].at("Hello")) == "World"); -- cgit 0.0.5-2-1-g0f52 From 88a7e206f1093f5a638efc74327b364d009d5f8f Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sat, 4 Jul 2015 20:13:55 +0200 Subject: Fix crtp_tag::equals --- include/crtp_tag.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/crtp_tag.h b/include/crtp_tag.h index fa28a74d41..abd8b3b1ce 100644 --- a/include/crtp_tag.h +++ b/include/crtp_tag.h @@ -64,7 +64,7 @@ namespace detail template bool crtp_tag::equals(const tag& rhs) const { - return *this == static_cast(rhs); + return static_cast(*this) == static_cast(rhs); } template -- cgit 0.0.5-2-1-g0f52 From e1a24c2f2b3a84073a9988df43fbd63577c3c4f8 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sat, 4 Jul 2015 20:14:38 +0200 Subject: Make some value methods null-safe --- include/tag.h | 3 ++- src/value.cpp | 41 +++++++++++++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/include/tag.h b/include/tag.h index 980c0437ff..6b037b9a32 100644 --- a/include/tag.h +++ b/include/tag.h @@ -40,7 +40,8 @@ enum class tag_type : int8_t String = 8, List = 9, Compound = 10, - Int_Array = 11 + Int_Array = 11, + Null = -1 }; ///Base class for all NBT tag classes diff --git a/src/value.cpp b/src/value.cpp index ae40587324..c1a7a91e2b 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -40,7 +40,10 @@ value& value::operator=(tag&& t) void value::set(tag&& t) { - tag_->assign(std::move(t)); + if(tag_) + tag_->assign(std::move(t)); + else + tag_ = std::move(t).move_clone(); } value::operator tag&() @@ -67,7 +70,9 @@ const tag& value::get() const //FIXME: Make this less copypaste! value& value::operator=(int8_t val) { - switch(tag_->get_type()) + if(!tag_) + set(tag_byte(val)); + else switch(tag_->get_type()) { case tag_type::Byte: static_cast(*tag_).set(val); @@ -96,7 +101,9 @@ value& value::operator=(int8_t val) value& value::operator=(int16_t val) { - switch(tag_->get_type()) + if(!tag_) + set(tag_short(val)); + else switch(tag_->get_type()) { case tag_type::Short: static_cast(*tag_).set(val); @@ -122,7 +129,9 @@ value& value::operator=(int16_t val) value& value::operator=(int32_t val) { - switch(tag_->get_type()) + if(!tag_) + set(tag_int(val)); + else switch(tag_->get_type()) { case tag_type::Int: static_cast(*tag_).set(val); @@ -145,7 +154,9 @@ value& value::operator=(int32_t val) value& value::operator=(int64_t val) { - switch(tag_->get_type()) + if(!tag_) + set(tag_long(val)); + else switch(tag_->get_type()) { case tag_type::Long: static_cast(*tag_).set(val); @@ -165,7 +176,9 @@ value& value::operator=(int64_t val) value& value::operator=(float val) { - switch(tag_->get_type()) + if(!tag_) + set(tag_float(val)); + else switch(tag_->get_type()) { case tag_type::Float: static_cast(*tag_).set(val); @@ -182,7 +195,9 @@ value& value::operator=(float val) value& value::operator=(double val) { - switch(tag_->get_type()) + if(!tag_) + set(tag_double(val)); + else switch(tag_->get_type()) { case tag_type::Double: static_cast(*tag_).set(val); @@ -304,7 +319,10 @@ value& value::operator=(const std::string& str) value& value::operator=(std::string&& str) { - dynamic_cast(*tag_).set(std::move(str)); + if(!tag_) + set(tag_string(std::move(str))); + else + dynamic_cast(*tag_).set(std::move(str)); return *this; } @@ -350,12 +368,15 @@ void value::set_ptr(std::unique_ptr&& t) tag_type value::get_type() const { - return tag_->get_type(); + return tag_ ? tag_->get_type() : tag_type::Null; } bool operator==(const value& lhs, const value& rhs) { - return *lhs.tag_ == *rhs.tag_; + if(lhs.tag_ != nullptr && rhs.tag_ != nullptr) + return *lhs.tag_ == *rhs.tag_; + else + return lhs.tag_ == nullptr && rhs.tag_ == nullptr; } bool operator!=(const value& lhs, const value& rhs) -- cgit 0.0.5-2-1-g0f52 From 792bbf09c4ab7a37664ebaed8314c4f543fc17b0 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 5 Jul 2015 11:37:52 +0200 Subject: Correct documentations for operator[] --- include/tag_compound.h | 6 ++---- include/value.h | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/include/tag_compound.h b/include/tag_compound.h index ea0df236c9..acde3a1e2b 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -58,10 +58,8 @@ public: /** * @brief Accesses a tag by key * - * 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 - * @todo Make it create a new entry rather than throwing an exception + * 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); diff --git a/include/value.h b/include/value.h index 4116e2d0c1..b2a7a21b1f 100644 --- a/include/value.h +++ b/include/value.h @@ -113,7 +113,6 @@ public: /** * @brief In case of a tag_compound, accesses a tag by key - * @throw std::bad_cast if the tag type is not tag_compound * @sa tag_compound::operator[] */ value& operator[](const std::string& key); -- cgit 0.0.5-2-1-g0f52 From 66a99e80a7bcb85c5cb1f743522d53b150780ee6 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 5 Jul 2015 11:50:22 +0200 Subject: Remove redundant includes --- include/value.h | 1 - include/value_initializer.h | 2 -- test/nbttest.cpp | 1 - 3 files changed, 4 deletions(-) diff --git a/include/value.h b/include/value.h index b2a7a21b1f..5076bb4ed5 100644 --- a/include/value.h +++ b/include/value.h @@ -21,7 +21,6 @@ #define TAG_REF_PROXY_H_INCLUDED #include "tag.h" -#include #include namespace nbt diff --git a/include/value_initializer.h b/include/value_initializer.h index 7ae2cb13f4..57e78ec518 100644 --- a/include/value_initializer.h +++ b/include/value_initializer.h @@ -21,8 +21,6 @@ #define VALUE_INITIALIZER_H_INCLUDED #include "value.h" -#include -#include namespace nbt { diff --git a/test/nbttest.cpp b/test/nbttest.cpp index d8f0f68d15..fe6490eb7b 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -20,7 +20,6 @@ #include "microtest.h" #include "libnbt.h" #include -#include using namespace nbt; -- cgit 0.0.5-2-1-g0f52 From 044752a36b076a59ee4cf5f6fef9d782acc0c5f0 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 5 Jul 2015 11:56:21 +0200 Subject: Add a few logging outputs for the tests --- test/nbttest.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index fe6490eb7b..cbe2202e8f 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -36,6 +36,7 @@ void test_get_type() //ASSERT(tag_list().get_type() == tag_type::List); ASSERT(tag_compound().get_type() == tag_type::Compound); //ASSERT(tag_int_array().get_type() == tag_type::Int_Array); + std::clog << "test_get_type passed" << std::endl; } void test_tag_primitive() @@ -52,6 +53,7 @@ void test_tag_primitive() ASSERT(7 == static_cast(tag)); ASSERT(tag_double() == 0.0); + std::clog << "test_tag_primitive passed" << std::endl; } void test_tag_string() @@ -72,6 +74,7 @@ void test_tag_string() ASSERT(tag_string(str).get() == "foo"); ASSERT(tag_string().get() == ""); + std::clog << "test_tag_string passed" << std::endl; } void test_tag_compound() @@ -144,6 +147,8 @@ void test_tag_compound() ASSERT(comp.emplace("def", "ghi") == true); ASSERT(comp.emplace("def", 4) == false); ASSERT((comp == tag_compound{{"abc", tag_long(-28)}, {"def", tag_byte(4)}})); + + std::clog << "test_tag_compound passed" << std::endl; } int main() -- cgit 0.0.5-2-1-g0f52 From 9771baae369a72e196e0fbbdadbce3b1dc30ac37 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 5 Jul 2015 17:18:32 +0200 Subject: Fix conflicting overload of operator[] --- include/value.h | 1 + src/value.cpp | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/include/value.h b/include/value.h index 5076bb4ed5..c029e1ecad 100644 --- a/include/value.h +++ b/include/value.h @@ -115,6 +115,7 @@ public: * @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[] std::unique_ptr& get_ptr(); const std::unique_ptr& get_ptr() const; diff --git a/src/value.cpp b/src/value.cpp index c1a7a91e2b..bf75b8931b 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -351,6 +351,11 @@ value& value::operator[](const std::string& key) return dynamic_cast(*tag_)[key]; } +value& value::operator[](const char* key) +{ + return (*this)[std::string(key)]; +} + std::unique_ptr& value::get_ptr() { return tag_; -- cgit 0.0.5-2-1-g0f52 From b539b098290f15d321087d48cd93b7bf837f00a4 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 5 Jul 2015 17:26:59 +0200 Subject: Add some tests for value --- test/nbttest.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index cbe2202e8f..b8a548aa05 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -110,6 +110,7 @@ void test_tag_compound() comp["quux"] = tag_compound{{"Hello", "World"}, {"zero", 0}}; ASSERT(comp.at("quux").get_type() == tag_type::Compound); ASSERT(std::string(comp["quux"].at("Hello")) == "World"); + ASSERT(std::string(comp["quux"]["Hello"]) == "World"); EXPECT_EXCEPTION(comp.at("nothing"), std::out_of_range); @@ -151,10 +152,54 @@ void test_tag_compound() std::clog << "test_tag_compound passed" << std::endl; } +void test_value() +{ + value val1; + value val2(std::unique_ptr(new tag_int(42))); + value val3(tag_int(42)); + + ASSERT(!val1 && val2 && val3); + ASSERT(val1 == val1); + ASSERT(val1 != val2); + ASSERT(val2 == val3); + ASSERT(val3 == val3); + + val1 = int64_t(42); + ASSERT(val2 != val1); + EXPECT_EXCEPTION(val2 = int64_t(12), std::bad_cast); + ASSERT(int64_t(val2) == 42); + val2 = 52; + ASSERT(int32_t(val2) == 52); + + EXPECT_EXCEPTION(val1["foo"], std::bad_cast); + EXPECT_EXCEPTION(val1.at("foo"), std::bad_cast); + + val3 = 52; + ASSERT(val2 == val3); + ASSERT(val2.get_ptr() != val3.get_ptr()); + + val3 = std::move(val2); + ASSERT(val3 == tag_int(52)); + ASSERT(!val2); + + tag_int& tag = dynamic_cast(val3.get()); + ASSERT(tag == tag_int(52)); + tag = 21; + ASSERT(int32_t(val3) == 21); + val1.set_ptr(std::move(val3.get_ptr())); + ASSERT(tag_int(val1) == 21); + + ASSERT(val1.get_type() == tag_type::Int); + ASSERT(val2.get_type() == tag_type::Null); + ASSERT(val3.get_type() == tag_type::Null); + std::clog << "test_value passed" << std::endl; +} + int main() { test_get_type(); test_tag_primitive(); test_tag_string(); test_tag_compound(); + test_value(); } -- cgit 0.0.5-2-1-g0f52 From 4ea7019e8594254b77b72ea66e7700b2d8e6d2ef Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 7 Jul 2015 16:55:04 +0200 Subject: Specify undefined behavior on null values --- include/value.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/value.h b/include/value.h index c029e1ecad..7d3dfc0e92 100644 --- a/include/value.h +++ b/include/value.h @@ -54,6 +54,11 @@ public: void set(tag&& t); //Conversion to tag + /** + * @brief Returns the contained tag + * + * If the value is uninitialized, the behavior is undefined. + */ operator tag&(); operator const tag&() const; tag& get(); @@ -94,6 +99,8 @@ public: /** * @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 */ operator const std::string&() const; @@ -103,6 +110,8 @@ public: /** * @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 @@ -112,6 +121,8 @@ public: /** * @brief In case of a tag_compound, accesses a tag by key + * + * If the value is uninitialized, the behavior is undefined. * @sa tag_compound::operator[] */ value& operator[](const std::string& key); -- cgit 0.0.5-2-1-g0f52 From 49bcfa77e1252fb9e4e55d6a5f97a420f6de8fc4 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 7 Jul 2015 17:04:39 +0200 Subject: Create copy and clone functions for value and tag --- include/crtp_tag.h | 10 ++++++++++ include/tag.h | 1 + include/value.h | 5 +++++ src/value.cpp | 5 +++++ 4 files changed, 21 insertions(+) diff --git a/include/crtp_tag.h b/include/crtp_tag.h index abd8b3b1ce..928759d6b8 100644 --- a/include/crtp_tag.h +++ b/include/crtp_tag.h @@ -37,6 +37,7 @@ namespace detail tag_type get_type() const noexcept override final; + std::unique_ptr clone() const& override final; std::unique_ptr move_clone() && override final; private: @@ -53,6 +54,15 @@ namespace detail return Sub::type; } + //TODO: Add copy constructors for tags that are missing it before this becomes useable + /*template + std::unique_ptr crtp_tag::clone() const& + { + return std::unique_ptr( + new Sub(static_cast(*this)) + ); + }*/ + template std::unique_ptr crtp_tag::move_clone() && { diff --git a/include/tag.h b/include/tag.h index 6b037b9a32..da1d5098ad 100644 --- a/include/tag.h +++ b/include/tag.h @@ -54,6 +54,7 @@ public: ///Returns the type of the tag virtual tag_type get_type() const noexcept = 0; + virtual std::unique_ptr clone() const& = 0; virtual std::unique_ptr move_clone() && = 0; friend bool operator==(const tag& lhs, const tag& rhs); diff --git a/include/value.h b/include/value.h index 7d3dfc0e92..64e79efe9c 100644 --- a/include/value.h +++ b/include/value.h @@ -128,6 +128,11 @@ public: value& operator[](const std::string& key); value& operator[](const char* key); //need this overload because of conflict with built-in operator[] + /** + * @brief Creates a copy of the value + */ + value copy() const; + std::unique_ptr& get_ptr(); const std::unique_ptr& get_ptr() const; void set_ptr(std::unique_ptr&& t); diff --git a/src/value.cpp b/src/value.cpp index bf75b8931b..d6c276471a 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -356,6 +356,11 @@ value& value::operator[](const char* key) return (*this)[std::string(key)]; } +value value::copy() const +{ + return value(tag_->clone()); +} + std::unique_ptr& value::get_ptr() { return tag_; -- cgit 0.0.5-2-1-g0f52 From fd4942b6f56227e9d90ad046ae011bc9db05eae0 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 7 Jul 2015 17:16:25 +0200 Subject: Make value copyable (after all) --- include/crtp_tag.h | 4 ++-- include/value.h | 13 +++++-------- src/value.cpp | 18 +++++++++++++----- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/include/crtp_tag.h b/include/crtp_tag.h index 928759d6b8..a871866a2c 100644 --- a/include/crtp_tag.h +++ b/include/crtp_tag.h @@ -55,13 +55,13 @@ namespace detail } //TODO: Add copy constructors for tags that are missing it before this becomes useable - /*template + template std::unique_ptr crtp_tag::clone() const& { return std::unique_ptr( new Sub(static_cast(*this)) ); - }*/ + } template std::unique_ptr crtp_tag::move_clone() && diff --git a/include/value.h b/include/value.h index 64e79efe9c..02937c8f5e 100644 --- a/include/value.h +++ b/include/value.h @@ -39,12 +39,14 @@ public: explicit value(std::unique_ptr&& t); explicit value(tag&& t); - //Movable but not (implicitly) copyable - value(const value&) = delete; + //Moving value(value&&) = default; - value& operator=(const value&) = delete; value& operator=(value&&) = 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 @@ -128,11 +130,6 @@ public: value& operator[](const std::string& key); value& operator[](const char* key); //need this overload because of conflict with built-in operator[] - /** - * @brief Creates a copy of the value - */ - value copy() const; - std::unique_ptr& get_ptr(); const std::unique_ptr& get_ptr() const; void set_ptr(std::unique_ptr&& t); diff --git a/src/value.cpp b/src/value.cpp index d6c276471a..de173a94c2 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -32,6 +32,19 @@ value::value(tag&& t): tag_(std::move(t).move_clone()) {} +value::value(const value& rhs): + tag_(rhs.tag_ ? rhs.tag_->clone() : nullptr) +{} + +value& value::operator=(const value& rhs) +{ + if(this != &rhs) + { + tag_ = rhs.tag_ ? rhs.tag_->clone() : nullptr; + } + return *this; +} + value& value::operator=(tag&& t) { set(std::move(t)); @@ -356,11 +369,6 @@ value& value::operator[](const char* key) return (*this)[std::string(key)]; } -value value::copy() const -{ - return value(tag_->clone()); -} - std::unique_ptr& value::get_ptr() { return tag_; -- cgit 0.0.5-2-1-g0f52 From 9ed28be29dafc97a68098dbf94e29c0599376738 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 7 Jul 2015 17:21:38 +0200 Subject: Add tests for value copying --- test/nbttest.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index b8a548aa05..19a9ddc1b9 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -192,6 +192,18 @@ void test_value() ASSERT(val1.get_type() == tag_type::Int); ASSERT(val2.get_type() == tag_type::Null); ASSERT(val3.get_type() == tag_type::Null); + + val2 = val1; + val1 = val3; + ASSERT(!val1 && val2 && !val3); + ASSERT(val1.get_ptr() == nullptr); + ASSERT(val2.get() == tag_int(21)); + ASSERT(value(val1) == val1); + ASSERT(value(val2) == val2); + val1 = val1; + val2 = val2; + ASSERT(!val1); + ASSERT(val2 == tag_int(21)); std::clog << "test_value passed" << std::endl; } -- cgit 0.0.5-2-1-g0f52 From 780a63c33d80a961002859c13ce79d1ec1db9216 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 7 Jul 2015 17:24:52 +0200 Subject: Correctly implement value_initializer --- include/value_initializer.h | 8 +++++--- src/value_initializer.cpp | 5 ++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/include/value_initializer.h b/include/value_initializer.h index 57e78ec518..8f0ebaf60e 100644 --- a/include/value_initializer.h +++ b/include/value_initializer.h @@ -45,11 +45,13 @@ public: value_initializer(std::string&& str); value_initializer(const char* str); - value& get() const; + value& get(); + const value& get() const; + operator value&(); + operator const value&() const; private: - mutable value value_; //FIXME: std::initializer_list objects are constant. - //This solution will probably fail + value value_; }; } diff --git a/src/value_initializer.cpp b/src/value_initializer.cpp index 8d57933e09..3de74da717 100644 --- a/src/value_initializer.cpp +++ b/src/value_initializer.cpp @@ -37,6 +37,9 @@ value_initializer::value_initializer(const std::string& str): value_(tag_string( value_initializer::value_initializer(std::string&& str): value_(tag_string(std::move(str))) {} value_initializer::value_initializer(const char* str): value_(tag_string(str)) {} -value& value_initializer::get() const { return value_; } +value& value_initializer::get() { return value_; } +const value& value_initializer::get() const { return value_; } +value_initializer::operator value&() { return value_; } +value_initializer::operator const value&() const { return value_; } } -- cgit 0.0.5-2-1-g0f52 From 00b3c2bc9cc638fe29d8e6f18454e9a48d0db714 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 7 Jul 2015 17:52:33 +0200 Subject: Change return type of put and emplace to match std::map --- include/tag_compound.h | 12 +++++++----- src/tag_compound.cpp | 7 +++---- test/nbttest.cpp | 8 ++++---- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/include/tag_compound.h b/include/tag_compound.h index acde3a1e2b..a0adf07282 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -68,19 +68,21 @@ public: * * If the given key already exists, assigns the tag to it. * Otherwise, it is inserted under the given key. - * @return true if the key did not exist + * @return a pair of the iterator to the value and a bool indicating + * whether the key did not exist */ - bool put(const std::string& key, std::unique_ptr&& t); + std::pair put(const std::string& key, std::unique_ptr&& t); /** * @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. - * @return true if the key did not exist + * @return a pair of the iterator to the value and a bool indicating + * whether the key did not exist */ template - bool emplace(const std::string& key, Args&&... args); + std::pair emplace(const std::string& key, Args&&... args); /** * @brief Erases a tag from the compound @@ -113,7 +115,7 @@ private: }; template -bool tag_compound::emplace(const std::string& key, Args&&... args) +std::pair tag_compound::emplace(const std::string& key, Args&&... args) { return put(key, std::unique_ptr(new T(std::forward(args)...))); } diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index a2b91826b6..b0a36c30f4 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -43,18 +43,17 @@ value& tag_compound::operator[](const std::string& key) return tags[key]; } -bool tag_compound::put(const std::string& key, std::unique_ptr&& t) +std::pair tag_compound::put(const std::string& key, std::unique_ptr&& t) { auto it = tags.find(key); if(it != tags.end()) { it->second.set_ptr(std::move(t)); - return false; + return {it, false}; } else { - tags.emplace(key, value(std::move(t))); - return true; + return tags.emplace(key, value(std::move(t))); } } diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 19a9ddc1b9..64db9efe91 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -143,10 +143,10 @@ void test_tag_compound() comp.clear(); ASSERT(comp == tag_compound{}); - ASSERT(comp.put("abc", std::unique_ptr(new tag_double(6.0))) == true); - ASSERT(comp.put("abc", std::unique_ptr(new tag_long(-28))) == false); - ASSERT(comp.emplace("def", "ghi") == true); - ASSERT(comp.emplace("def", 4) == false); + ASSERT(comp.put("abc", std::unique_ptr(new tag_double(6.0))).second == true); + ASSERT(comp.put("abc", std::unique_ptr(new tag_long(-28))).second == false); + ASSERT(comp.emplace("def", "ghi").second == true); + ASSERT(comp.emplace("def", 4).second == false); ASSERT((comp == tag_compound{{"abc", tag_long(-28)}, {"def", tag_byte(4)}})); std::clog << "test_tag_compound passed" << std::endl; -- cgit 0.0.5-2-1-g0f52 From e5d34354de1fa0953b8ed71ed2e8e21d19e63a4f Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 7 Jul 2015 18:13:10 +0200 Subject: Remove redundand includes --- include/tag_compound.h | 1 - include/tag_primitive.h | 1 - test/nbttest.cpp | 1 - 3 files changed, 3 deletions(-) diff --git a/include/tag_compound.h b/include/tag_compound.h index a0adf07282..2b02ec5ba3 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -21,7 +21,6 @@ #define TAG_COMPOUND_H_INCLUDED #include "crtp_tag.h" -#include "value.h" #include "value_initializer.h" #include #include diff --git a/include/tag_primitive.h b/include/tag_primitive.h index e839be7da0..650fc1ad9c 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -22,7 +22,6 @@ #include "crtp_tag.h" #include "primitive_detail.h" -#include namespace nbt { diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 64db9efe91..5470b047ab 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -19,7 +19,6 @@ */ #include "microtest.h" #include "libnbt.h" -#include using namespace nbt; -- cgit 0.0.5-2-1-g0f52 From d9f87a8f9bbd8b296bd876cbb5fb3e410c6a76e3 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 7 Jul 2015 18:15:01 +0200 Subject: Remove unneeded TODO comment --- include/crtp_tag.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/crtp_tag.h b/include/crtp_tag.h index a871866a2c..df1272a3d3 100644 --- a/include/crtp_tag.h +++ b/include/crtp_tag.h @@ -54,7 +54,6 @@ namespace detail return Sub::type; } - //TODO: Add copy constructors for tags that are missing it before this becomes useable template std::unique_ptr crtp_tag::clone() const& { -- cgit 0.0.5-2-1-g0f52 From 0b0b7c343fd1d40a10d7c756237b8e86207d8b97 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 8 Jul 2015 18:20:02 +0200 Subject: Overload clone for rvalue refs --- include/tag.h | 1 + src/tag.cpp | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/include/tag.h b/include/tag.h index da1d5098ad..c38c9a5117 100644 --- a/include/tag.h +++ b/include/tag.h @@ -56,6 +56,7 @@ public: virtual std::unique_ptr clone() const& = 0; virtual std::unique_ptr move_clone() && = 0; + std::unique_ptr clone() &&; friend bool operator==(const tag& lhs, const tag& rhs); friend bool operator!=(const tag& lhs, const tag& rhs); diff --git a/src/tag.cpp b/src/tag.cpp index 40565226cf..da5c3fa68e 100644 --- a/src/tag.cpp +++ b/src/tag.cpp @@ -23,6 +23,11 @@ namespace nbt { +std::unique_ptr tag::clone() && +{ + return std::move(*this).move_clone(); +} + bool operator==(const tag& lhs, const tag& rhs) { if(typeid(lhs) != typeid(rhs)) -- cgit 0.0.5-2-1-g0f52 From 4efbf602230c39b311655a726903478977f53b79 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 8 Jul 2015 18:24:02 +0200 Subject: Make assign method public --- include/crtp_tag.h | 11 ++++++----- include/tag.h | 14 ++++++-------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/include/crtp_tag.h b/include/crtp_tag.h index df1272a3d3..8611038ffa 100644 --- a/include/crtp_tag.h +++ b/include/crtp_tag.h @@ -40,9 +40,10 @@ namespace detail std::unique_ptr clone() const& override final; std::unique_ptr move_clone() && override final; + tag& assign(tag&& rhs) override final; + private: bool equals(const tag& rhs) const override final; - tag& assign(tag&& rhs) override final; }; template @@ -71,15 +72,15 @@ namespace detail } template - bool crtp_tag::equals(const tag& rhs) const + tag& crtp_tag::assign(tag&& rhs) { - return static_cast(*this) == static_cast(rhs); + return *this = dynamic_cast(rhs); } template - tag& crtp_tag::assign(tag&& rhs) + bool crtp_tag::equals(const tag& rhs) const { - return *this = dynamic_cast(rhs); + return static_cast(*this) == static_cast(rhs); } } diff --git a/include/tag.h b/include/tag.h index c38c9a5117..b972a16da6 100644 --- a/include/tag.h +++ b/include/tag.h @@ -58,6 +58,12 @@ public: virtual std::unique_ptr move_clone() && = 0; std::unique_ptr clone() &&; + /** + * @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; + friend bool operator==(const tag& lhs, const tag& rhs); friend bool operator!=(const tag& lhs, const tag& rhs); @@ -67,14 +73,6 @@ private: * @param rhs an instance of the same class as @c *this */ virtual bool equals(const tag& rhs) const = 0; - - /** - * @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; - - friend class value; }; } -- cgit 0.0.5-2-1-g0f52 From ea71042f79738bcdc08fe3bfa0a2f9cd1c0c03ac Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 9 Jul 2015 18:00:42 +0200 Subject: Change signature of put to take value --- include/tag_compound.h | 4 ++-- src/tag_compound.cpp | 6 +++--- test/nbttest.cpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/tag_compound.h b/include/tag_compound.h index 2b02ec5ba3..80be2f609e 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -70,7 +70,7 @@ public: * @return a pair of the iterator to the value and a bool indicating * whether the key did not exist */ - std::pair put(const std::string& key, std::unique_ptr&& t); + std::pair put(const std::string& key, value&& val); /** * @brief Constructs and assigns or inserts a tag into the compound @@ -116,7 +116,7 @@ private: template std::pair tag_compound::emplace(const std::string& key, Args&&... args) { - return put(key, std::unique_ptr(new T(std::forward(args)...))); + return put(key, value(T(std::forward(args)...))); } } diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index b0a36c30f4..4c6c03264e 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -43,17 +43,17 @@ value& tag_compound::operator[](const std::string& key) return tags[key]; } -std::pair tag_compound::put(const std::string& key, std::unique_ptr&& t) +std::pair tag_compound::put(const std::string& key, value&& val) { auto it = tags.find(key); if(it != tags.end()) { - it->second.set_ptr(std::move(t)); + it->second = std::move(val); return {it, false}; } else { - return tags.emplace(key, value(std::move(t))); + return tags.emplace(key, std::move(val)); } } diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 5470b047ab..42f8bd25f4 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -142,8 +142,8 @@ void test_tag_compound() comp.clear(); ASSERT(comp == tag_compound{}); - ASSERT(comp.put("abc", std::unique_ptr(new tag_double(6.0))).second == true); - ASSERT(comp.put("abc", std::unique_ptr(new tag_long(-28))).second == false); + ASSERT(comp.put("abc", value(tag_double(6.0))).second == true); + ASSERT(comp.put("abc", value(tag_long(-28))).second == false); ASSERT(comp.emplace("def", "ghi").second == true); ASSERT(comp.emplace("def", 4).second == false); ASSERT((comp == tag_compound{{"abc", tag_long(-28)}, {"def", tag_byte(4)}})); -- cgit 0.0.5-2-1-g0f52 From 4945e533c37d261e82d34ea129f441be06385cce Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 9 Jul 2015 18:09:06 +0200 Subject: Create tag_compound::insert method --- include/tag_compound.h | 8 ++++++++ src/tag_compound.cpp | 5 +++++ test/nbttest.cpp | 7 ++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/include/tag_compound.h b/include/tag_compound.h index 80be2f609e..0789bed295 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -72,11 +72,19 @@ public: */ std::pair put(const std::string& key, value&& 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 insert(const std::string& key, value&& 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 */ diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index 4c6c03264e..4ee38c4412 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -57,6 +57,11 @@ std::pair tag_compound::put(const std::string& key } } +std::pair tag_compound::insert(const std::string& key, value&& val) +{ + return tags.emplace(key, std::move(val)); +} + bool tag_compound::erase(const std::string& key) { return tags.erase(key) != 0; diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 42f8bd25f4..f4d0405273 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -144,9 +144,14 @@ void test_tag_compound() ASSERT(comp.put("abc", value(tag_double(6.0))).second == true); ASSERT(comp.put("abc", value(tag_long(-28))).second == false); + ASSERT(comp.insert("ghi", value(tag_string("world"))).second == true); + ASSERT(comp.insert("abc", value(tag_string("hello"))).second == false); ASSERT(comp.emplace("def", "ghi").second == true); ASSERT(comp.emplace("def", 4).second == false); - ASSERT((comp == tag_compound{{"abc", tag_long(-28)}, {"def", tag_byte(4)}})); + ASSERT((comp == tag_compound{ + {"abc", tag_long(-28)}, + {"def", tag_byte(4)}, + {"ghi", tag_string("world")}})); std::clog << "test_tag_compound passed" << std::endl; } -- cgit 0.0.5-2-1-g0f52 From 125c0c3acbac2b1aabdcc6d2b7f1a84c16d4b38e Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 9 Jul 2015 18:25:23 +0200 Subject: Make value_initializer inherit from value --- include/value_initializer.h | 12 +++--------- src/tag_compound.cpp | 2 +- src/value_initializer.cpp | 29 ++++++++++++----------------- 3 files changed, 16 insertions(+), 27 deletions(-) diff --git a/include/value_initializer.h b/include/value_initializer.h index 8f0ebaf60e..f50527546d 100644 --- a/include/value_initializer.h +++ b/include/value_initializer.h @@ -27,8 +27,10 @@ namespace nbt /** * @brief Helper class for implicitly constructing value objects + * @note Instances of this class can unproblematically be "sliced" (converted) + * into @ref value objects. */ -class value_initializer +class value_initializer : public value { public: value_initializer(std::unique_ptr&& t); @@ -44,14 +46,6 @@ public: value_initializer(const std::string& str); value_initializer(std::string&& str); value_initializer(const char* str); - - value& get(); - const value& get() const; - operator value&(); - operator const value&() const; - -private: - value value_; }; } diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index 4ee38c4412..78a5c46fc6 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -25,7 +25,7 @@ namespace nbt tag_compound::tag_compound(std::initializer_list> init) { for(const auto& pair: init) - tags.emplace(std::move(pair.first), std::move(pair.second.get())); + tags.emplace(std::move(pair.first), std::move(pair.second)); } value& tag_compound::at(const std::string& key) diff --git a/src/value_initializer.cpp b/src/value_initializer.cpp index 3de74da717..4b039e441a 100644 --- a/src/value_initializer.cpp +++ b/src/value_initializer.cpp @@ -23,23 +23,18 @@ namespace nbt { -value_initializer::value_initializer(std::unique_ptr&& t): value_(std::move(t)) {} -value_initializer::value_initializer(value&& val): value_(std::move(val)) {} -value_initializer::value_initializer(tag&& t): value_(std::move(t)) {} +value_initializer::value_initializer(std::unique_ptr&& t): value(std::move(t)) {} +value_initializer::value_initializer(value&& val): value(std::move(val)) {} +value_initializer::value_initializer(tag&& t): value(std::move(t)) {} -value_initializer::value_initializer(int8_t val): value_(tag_byte(val)) {} -value_initializer::value_initializer(int16_t val): value_(tag_short(val)) {} -value_initializer::value_initializer(int32_t val): value_(tag_int(val)) {} -value_initializer::value_initializer(int64_t val): value_(tag_long(val)) {} -value_initializer::value_initializer(float val): value_(tag_float(val)) {} -value_initializer::value_initializer(double val): value_(tag_double(val)) {} -value_initializer::value_initializer(const std::string& str): value_(tag_string(str)) {} -value_initializer::value_initializer(std::string&& str): value_(tag_string(std::move(str))) {} -value_initializer::value_initializer(const char* str): value_(tag_string(str)) {} - -value& value_initializer::get() { return value_; } -const value& value_initializer::get() const { return value_; } -value_initializer::operator value&() { return value_; } -value_initializer::operator const value&() const { return value_; } +value_initializer::value_initializer(int8_t val): value(tag_byte(val)) {} +value_initializer::value_initializer(int16_t val): value(tag_short(val)) {} +value_initializer::value_initializer(int32_t val): value(tag_int(val)) {} +value_initializer::value_initializer(int64_t val): value(tag_long(val)) {} +value_initializer::value_initializer(float val): value(tag_float(val)) {} +value_initializer::value_initializer(double val): value(tag_double(val)) {} +value_initializer::value_initializer(const std::string& str): value(tag_string(str)) {} +value_initializer::value_initializer(std::string&& str): value(tag_string(std::move(str))) {} +value_initializer::value_initializer(const char* str): value(tag_string(str)) {} } -- cgit 0.0.5-2-1-g0f52 From bd8ac94ccb071ffb4cc16b3fb514e789ca1f866d Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 12 Jul 2015 12:29:25 +0200 Subject: Create tag_list --- include/libnbt.h | 1 + include/tag_list.h | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++ test/nbttest.cpp | 2 +- 3 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 include/tag_list.h diff --git a/include/libnbt.h b/include/libnbt.h index 9d873254f6..965fa309ca 100644 --- a/include/libnbt.h +++ b/include/libnbt.h @@ -23,5 +23,6 @@ #include "tag_primitive.h" #include "tag_string.h" #include "tag_compound.h" +#include "tag_list.h" #endif // LIBNBT_H_INCLUDED diff --git a/include/tag_list.h b/include/tag_list.h new file mode 100644 index 0000000000..afc04c0616 --- /dev/null +++ b/include/tag_list.h @@ -0,0 +1,103 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#ifndef TAG_LIST_H_INCLUDED +#define TAG_LIST_H_INCLUDED + +#include "crtp_tag.h" +#include + +namespace nbt +{ + +///Tag that contains multiple unnamed tags of the same type +class tag_list : public detail::crtp_tag +{ +public: + //Iterator types + typedef std::vector::iterator iterator; + typedef std::vector::const_iterator const_iterator; + + ///The type of the tag + static constexpr tag_type type = tag_type::List; + + ///Constructs an empty list + tag_list() {} + + /** + * @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 given key does not exist + */ + 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); + const value& operator[](size_t i) const; + + /** + * @brief Appends the tag to the end of the list + * @throw std::bad_cast if the type of the tag does not match the list's + * content type + */ + void push_back(value&& val); + + /** + * @brief Constructs and appends a tag to the end of the list + * @throw std::bad_cast if the type of the tag does not match the list's + * content type + */ + template + void emplace_back(Args&&... args); + + ///Removes the last element of the list + void pop_back(); + + ///Returns the number of tags in the list + size_t size() const; + + ///Erases all tags from the list + void clear(); + + //Iterators + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + const_iterator cbegin() const; + const_iterator cend() const; + + friend bool operator==(const tag_list& lhs, const tag_list& rhs); + friend bool operator!=(const tag_list& lhs, const tag_list& rhs); + +private: + std::vector tags; +}; + +} + +#endif // TAG_LIST_H_INCLUDED diff --git a/test/nbttest.cpp b/test/nbttest.cpp index f4d0405273..f24b25f5f6 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -32,7 +32,7 @@ void test_get_type() ASSERT(tag_double().get_type() == tag_type::Double); //ASSERT(tag_byte_array().get_type() == tag_type::Byte_Array); ASSERT(tag_string().get_type() == tag_type::String); - //ASSERT(tag_list().get_type() == tag_type::List); + ASSERT(tag_list().get_type() == tag_type::List); ASSERT(tag_compound().get_type() == tag_type::Compound); //ASSERT(tag_int_array().get_type() == tag_type::Int_Array); std::clog << "test_get_type passed" << std::endl; -- cgit 0.0.5-2-1-g0f52 From 3c1177618ad70760bfb315c930065054f8aefedd Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 12 Jul 2015 12:48:08 +0200 Subject: Add functions for the list's content type --- include/tag_list.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/include/tag_list.h b/include/tag_list.h index afc04c0616..b13da3c5a9 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -37,8 +37,15 @@ public: ///The type of the tag static constexpr tag_type type = tag_type::List; - ///Constructs an empty list - tag_list() {} + /** + * @brief Constructs an empty list + * + * The content type is determined when the first tag is added. + */ + tag_list(); + + ///Constructs an empty list with the given content type + tag_list(tag_type type); /** * @brief Accesses a tag by index with bounds checking @@ -77,6 +84,9 @@ public: ///Removes the last element of the list void pop_back(); + ///Returns the content type of the list, or tag_type::Null if undetermined + tag_type el_type() const; + ///Returns the number of tags in the list size_t size() const; @@ -96,6 +106,7 @@ public: private: std::vector tags; + tag_type el_type; }; } -- cgit 0.0.5-2-1-g0f52 From 63460f29957ed2e28ee35ee8a92432fe446e0936 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 12 Jul 2015 12:54:09 +0200 Subject: Create tag_list::set method --- include/tag_list.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/include/tag_list.h b/include/tag_list.h index b13da3c5a9..d0b80ebb01 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -52,7 +52,7 @@ public: * * 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 given key does not exist + * @throw std::out_of_range if the index is out of range */ value& at(size_t i); const value& at(size_t i) const; @@ -66,6 +66,14 @@ public: value& operator[](size_t i); const value& operator[](size_t i) const; + /** + * @brief Assigns a tag at the given index + * @throw std::bad_cast if the type of the tag 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::bad_cast if the type of the tag does not match the list's @@ -106,7 +114,7 @@ public: private: std::vector tags; - tag_type el_type; + tag_type el_type_; }; } -- cgit 0.0.5-2-1-g0f52 From bd1ae77694bf3257f02f2d16ce58481115fe14db Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 12 Jul 2015 17:04:26 +0200 Subject: Add list accessors to value --- include/value.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/value.h b/include/value.h index 02937c8f5e..a03395c2d1 100644 --- a/include/value.h +++ b/include/value.h @@ -125,11 +125,34 @@ public: * @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; + std::unique_ptr& get_ptr(); const std::unique_ptr& get_ptr() const; void set_ptr(std::unique_ptr&& t); -- cgit 0.0.5-2-1-g0f52 From a6e071d35535c6097458d12ca268bd73c582dcfc Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 12 Jul 2015 18:11:32 +0200 Subject: Add tests for tag_list --- test/nbttest.cpp | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index f24b25f5f6..da4ff89876 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -151,7 +151,8 @@ void test_tag_compound() ASSERT((comp == tag_compound{ {"abc", tag_long(-28)}, {"def", tag_byte(4)}, - {"ghi", tag_string("world")}})); + {"ghi", tag_string("world")} + })); std::clog << "test_tag_compound passed" << std::endl; } @@ -211,6 +212,49 @@ void test_value() std::clog << "test_value passed" << std::endl; } +void test_tag_list() +{ + tag_list list; + ASSERT(list.el_type() == tag_type::Null); + EXPECT_EXCEPTION(list.push_back(value()), std::bad_cast); + + list.emplace_back("foo"); + ASSERT(list.el_type() == tag_type::String); + list.push_back(value(tag_string("bar"))); + EXPECT_EXCEPTION(list.push_back(value(tag_int(42))), std::bad_cast); + EXPECT_EXCEPTION(list.emplace_back(), std::bad_cast); + + //ASSERT(list == tag_list{"foo", "bar"}); + ASSERT(list[0] == tag_string("foo")); + ASSERT(std::string(list.at(1)) == "bar"); + + ASSERT(list.size() == 2); + EXPECT_EXCEPTION(list.at(2), std::out_of_range); + EXPECT_EXCEPTION(list.at(-1), std::out_of_range); + + list.set(1, value(tag_string("baz"))); + ASSERT(std::string(list[1]) == "baz"); + + const char* values[] = {"foo", "baz"}; + unsigned i = 0; + for(const value& val: list) + { + ASSERT(i < list.size()); + ASSERT(std::string(val) == values[i]); + ++i; + } + + list.pop_back(); + //ASSERT(list == tag_list{"foo"}); + + list.clear(); + ASSERT(list.size() == 0); + EXPECT_EXCEPTION(list.push_back(value(tag_short(25))), std::bad_cast); + EXPECT_EXCEPTION(list.push_back(value()), std::bad_cast); + + std::clog << "test_tag_list passed" << std::endl; +} + int main() { test_get_type(); @@ -218,4 +262,5 @@ int main() test_tag_string(); test_tag_compound(); test_value(); + test_tag_list(); } -- cgit 0.0.5-2-1-g0f52 From 1ce4f17facba4dfe62cf52c83b4d59e51b659c31 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 12 Jul 2015 18:19:38 +0200 Subject: Implement tag_list --- include/tag_list.h | 14 +++++++- src/tag_list.cpp | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 src/tag_list.cpp diff --git a/include/tag_list.h b/include/tag_list.h index d0b80ebb01..fe5a6818fa 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -21,6 +21,8 @@ #define TAG_LIST_H_INCLUDED #include "crtp_tag.h" +#include "value.h" +#include #include namespace nbt @@ -98,7 +100,7 @@ public: ///Returns the number of tags in the list size_t size() const; - ///Erases all tags from the list + ///Erases all tags from the list. Preserves the content type. void clear(); //Iterators @@ -117,6 +119,16 @@ private: tag_type el_type_; }; +template +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::bad_cast(); + tags.emplace_back(T(std::forward(args)...)); +} + } #endif // TAG_LIST_H_INCLUDED diff --git a/src/tag_list.cpp b/src/tag_list.cpp new file mode 100644 index 0000000000..2b66889e56 --- /dev/null +++ b/src/tag_list.cpp @@ -0,0 +1,99 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include "tag_list.h" + +namespace nbt +{ + +tag_list::tag_list(): + tag_list(tag_type::Null) +{} + +tag_list::tag_list(tag_type type): + el_type_(type) +{} + +value& tag_list::at(size_t i) +{ + return tags.at(i); +} + +const value& tag_list::at(size_t i) const +{ + return tags.at(i); +} + +value& tag_list::operator[](size_t i) +{ + return tags[i]; +} + +const value& tag_list::operator[](size_t i) const +{ + return tags[i]; +} + +void tag_list::set(size_t i, value&& val) +{ + if(val.get_type() != el_type_) + throw std::bad_cast(); + tags.at(i) = std::move(val); +} + +void tag_list::push_back(value&& val) +{ + if(!val) //don't allow null values + throw std::bad_cast(); + if(el_type_ == tag_type::Null) //set content type if undetermined + el_type_ = val.get_type(); + else if(el_type_ != val.get_type()) + throw std::bad_cast(); + tags.push_back(std::move(val)); +} + +void tag_list::pop_back() +{ + tags.pop_back(); +} + +tag_type tag_list::el_type() const +{ + return el_type_; +} + +size_t tag_list::size() const +{ + return tags.size(); +} + +void tag_list::clear() +{ + tags.clear(); +} + +auto tag_list::begin() -> iterator { return tags.begin(); } +auto tag_list::end() -> iterator { return tags.end(); } +auto tag_list::begin() const -> const_iterator { return tags.begin(); } +auto tag_list::end() const -> const_iterator { return tags.end(); } +auto tag_list::cbegin() const -> const_iterator { return tags.cbegin(); } +auto tag_list::cend() const -> const_iterator { return tags.cend(); } + +} + -- cgit 0.0.5-2-1-g0f52 From 65750c0c2941c7c71640ad1b9bc8ff43b45889a9 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 12 Jul 2015 18:19:59 +0200 Subject: Change the notion of list equality --- include/tag_list.h | 6 ++++++ src/tag_list.cpp | 11 +++++++++++ test/nbttest.cpp | 2 ++ 3 files changed, 19 insertions(+) diff --git a/include/tag_list.h b/include/tag_list.h index fe5a6818fa..2603e4a5ca 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -111,6 +111,12 @@ public: const_iterator cbegin() const; const_iterator cend() const; + /** + * @brief Equality comparison for lists + * + * Lists are considered equal if they contain equal tags. Empty lists + * are always considered equal. + */ friend bool operator==(const tag_list& lhs, const tag_list& rhs); friend bool operator!=(const tag_list& lhs, const tag_list& rhs); diff --git a/src/tag_list.cpp b/src/tag_list.cpp index 2b66889e56..d0ae233e80 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -95,5 +95,16 @@ auto tag_list::end() const -> const_iterator { return tags.end(); } auto tag_list::cbegin() const -> const_iterator { return tags.cbegin(); } auto tag_list::cend() const -> const_iterator { return tags.cend(); } +bool operator==(const tag_list& lhs, const tag_list& rhs) +{ + if(lhs.size() == 0 && rhs.size() == 0) + return true; + return lhs.el_type_ == rhs.el_type_ && lhs.tags == rhs.tags; } +bool operator!=(const tag_list& lhs, const tag_list& rhs) +{ + return !(lhs == rhs); +} + +} diff --git a/test/nbttest.cpp b/test/nbttest.cpp index da4ff89876..4c2fa548c5 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -252,6 +252,8 @@ void test_tag_list() EXPECT_EXCEPTION(list.push_back(value(tag_short(25))), std::bad_cast); EXPECT_EXCEPTION(list.push_back(value()), std::bad_cast); + ASSERT(tag_list() == tag_list(tag_type::Int)); + ASSERT(tag_list(tag_type::Short) == tag_list(tag_type::Int)); std::clog << "test_tag_list passed" << std::endl; } -- cgit 0.0.5-2-1-g0f52 From 857bdd55e92b5910852d1215bf5cef43e78b5ecb Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 12 Jul 2015 18:44:52 +0200 Subject: Add initializer list constructors for tag_list --- include/tag_list.h | 25 +++++++++++++++++++++++++ test/nbttest.cpp | 13 +++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/include/tag_list.h b/include/tag_list.h index 2603e4a5ca..594d630547 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -28,6 +28,12 @@ namespace nbt { +//Forward declarations +class tag_list; +class tag_compound; +class tag_byte_array; +class tag_int_array; + ///Tag that contains multiple unnamed tags of the same type class tag_list : public detail::crtp_tag { @@ -49,6 +55,25 @@ public: ///Constructs an empty list with the given content type tag_list(tag_type type); + ///Constructs a list with the given contents + tag_list(std::initializer_list init); + tag_list(std::initializer_list init); + tag_list(std::initializer_list init); + tag_list(std::initializer_list init); + tag_list(std::initializer_list init); + tag_list(std::initializer_list init); + tag_list(std::initializer_list init); + tag_list(std::initializer_list init); + tag_list(std::initializer_list init); + tag_list(std::initializer_list init); + tag_list(std::initializer_list init); + + /** + * @brief Constructs a list with the given contents + * @throw std::bad_cast if the tags are not all of the same type + */ + tag_list(std::initializer_list init); + /** * @brief Accesses a tag by index with bounds checking * diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 4c2fa548c5..932bdd2301 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -224,7 +224,7 @@ void test_tag_list() EXPECT_EXCEPTION(list.push_back(value(tag_int(42))), std::bad_cast); EXPECT_EXCEPTION(list.emplace_back(), std::bad_cast); - //ASSERT(list == tag_list{"foo", "bar"}); + ASSERT((list == tag_list{"foo", "bar"})); ASSERT(list[0] == tag_string("foo")); ASSERT(std::string(list.at(1)) == "bar"); @@ -245,7 +245,9 @@ void test_tag_list() } list.pop_back(); - //ASSERT(list == tag_list{"foo"}); + ASSERT(list == tag_list{"foo"}); + ASSERT(list == tag_list{std::string("foo")}); + ASSERT((list != tag_list{2, 3, 5, 7})); list.clear(); ASSERT(list.size() == 0); @@ -254,6 +256,13 @@ void test_tag_list() ASSERT(tag_list() == tag_list(tag_type::Int)); ASSERT(tag_list(tag_type::Short) == tag_list(tag_type::Int)); + + tag_list short_list{int16_t(25), int16_t(36)}; + ASSERT(short_list.el_type() == tag_type::Short); + ASSERT((short_list != tag_list{25, 36})); + ASSERT((short_list == tag_list{value(tag_short(25)), value(tag_short(36))})); + + EXPECT_EXCEPTION((tag_list{value(tag_byte(4)), value(tag_int(5))}), std::bad_cast); std::clog << "test_tag_list passed" << std::endl; } -- cgit 0.0.5-2-1-g0f52 From a9c5da13649466c13a081fa35eed7261340158c1 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 12 Jul 2015 19:40:57 +0200 Subject: Implement initializer_list constructors for tag_list --- include/tag_list.h | 9 +++++++++ src/tag_list.cpp | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/include/tag_list.h b/include/tag_list.h index 594d630547..12aa86d965 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -148,6 +148,15 @@ public: private: std::vector tags; tag_type el_type_; + + /** + * Internally used constructor that initializes the list with tags of + * type T, with the constructor arguments of each T given by init. + * @param dummy ignored, only used for inducing the template parameter T + * @param init list of values that are, one by one, given to a constructor of T + */ + template + tag_list(T dummy, std::initializer_list init); }; template diff --git a/src/tag_list.cpp b/src/tag_list.cpp index d0ae233e80..da35d19cac 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -18,6 +18,7 @@ * along with libnbt++. If not, see . */ #include "tag_list.h" +#include "libnbt.h" namespace nbt { @@ -30,6 +31,43 @@ tag_list::tag_list(tag_type type): el_type_(type) {} +template +tag_list::tag_list(T dummy, std::initializer_list init): + el_type_(T::type) +{ + tags.reserve(init.size()); + for(const Arg& arg: init) + tags.emplace_back(T(arg)); +} + +tag_list::tag_list(std::initializer_list init): tag_list(tag_byte() , init) {} +tag_list::tag_list(std::initializer_list init): tag_list(tag_short() , init) {} +tag_list::tag_list(std::initializer_list init): tag_list(tag_int() , init) {} +tag_list::tag_list(std::initializer_list init): tag_list(tag_long() , init) {} +tag_list::tag_list(std::initializer_list init): tag_list(tag_float() , init) {} +tag_list::tag_list(std::initializer_list init): tag_list(tag_double() , init) {} +tag_list::tag_list(std::initializer_list init): tag_list(tag_string() , init) {} +//tag_list::tag_list(std::initializer_list init): tag_list(tag_byte_array(), init) {} +tag_list::tag_list(std::initializer_list init): tag_list(tag_list() , init) {} +tag_list::tag_list(std::initializer_list init): tag_list(tag_compound() , init) {} +//tag_list::tag_list(std::initializer_list init): tag_list(tag_int_array() , init) {} + +tag_list::tag_list(std::initializer_list init) +{ + if(init.size() == 0) + el_type_ = tag_type::Null; + else + { + el_type_ = init.begin()->get_type(); + for(const value& val: init) + { + if(!val || val.get_type() != el_type_) + throw std::bad_cast(); + } + tags.assign(init.begin(), init.end()); + } +} + value& tag_list::at(size_t i) { return tags.at(i); -- cgit 0.0.5-2-1-g0f52 From 94df1cdedd83beecec1bc0b1fa90445cc7e369db Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 12 Jul 2015 19:52:37 +0200 Subject: Add more tests for tag_list --- test/nbttest.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 932bdd2301..8fdffd0467 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -216,7 +216,7 @@ void test_tag_list() { tag_list list; ASSERT(list.el_type() == tag_type::Null); - EXPECT_EXCEPTION(list.push_back(value()), std::bad_cast); + EXPECT_EXCEPTION(list.push_back(value(nullptr)), std::bad_cast); list.emplace_back("foo"); ASSERT(list.el_type() == tag_type::String); @@ -252,7 +252,7 @@ void test_tag_list() list.clear(); ASSERT(list.size() == 0); EXPECT_EXCEPTION(list.push_back(value(tag_short(25))), std::bad_cast); - EXPECT_EXCEPTION(list.push_back(value()), std::bad_cast); + EXPECT_EXCEPTION(list.push_back(value(nullptr)), std::bad_cast); ASSERT(tag_list() == tag_list(tag_type::Int)); ASSERT(tag_list(tag_type::Short) == tag_list(tag_type::Int)); @@ -263,6 +263,9 @@ void test_tag_list() ASSERT((short_list == tag_list{value(tag_short(25)), value(tag_short(36))})); EXPECT_EXCEPTION((tag_list{value(tag_byte(4)), value(tag_int(5))}), std::bad_cast); + EXPECT_EXCEPTION((tag_list{value(nullptr), value(tag_int(6))}), std::bad_cast); + EXPECT_EXCEPTION((tag_list{value(tag_int(7)), value(tag_int(8)), value(nullptr)}), std::bad_cast); + ASSERT((tag_list(std::initializer_list{})).el_type() == tag_type::Null); std::clog << "test_tag_list passed" << std::endl; } -- cgit 0.0.5-2-1-g0f52 From 37717c56332a524026272c4d91b7bd01b3c699fc Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 12 Jul 2015 20:14:15 +0200 Subject: Implement and test list accessors for value --- src/value.cpp | 20 ++++++++++++++++++++ test/nbttest.cpp | 11 +++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/value.cpp b/src/value.cpp index de173a94c2..fa6018dd5d 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -369,6 +369,26 @@ value& value::operator[](const char* key) return (*this)[std::string(key)]; } +value& value::at(size_t i) +{ + return dynamic_cast(*tag_).at(i); +} + +const value& value::at(size_t i) const +{ + return dynamic_cast(*tag_).at(i); +} + +value& value::operator[](size_t i) +{ + return dynamic_cast(*tag_)[i]; +} + +const value& value::operator[](size_t i) const +{ + return dynamic_cast(*tag_)[i]; +} + std::unique_ptr& value::get_ptr() { return tag_; diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 8fdffd0467..8553284543 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -81,7 +81,8 @@ void test_tag_compound() tag_compound comp{ {"foo", int16_t(12)}, {"bar", "baz"}, - {"baz", -2.0} + {"baz", -2.0}, + {"list", tag_list{int8_t(16), int8_t(17)}} }; ASSERT(comp["foo"].get_type() == tag_type::Short); @@ -110,6 +111,7 @@ void test_tag_compound() ASSERT(comp.at("quux").get_type() == tag_type::Compound); ASSERT(std::string(comp["quux"].at("Hello")) == "World"); ASSERT(std::string(comp["quux"]["Hello"]) == "World"); + ASSERT(comp["list"][1] == tag_byte(17)); EXPECT_EXCEPTION(comp.at("nothing"), std::out_of_range); @@ -117,14 +119,15 @@ void test_tag_compound() {"foo", int16_t(32)}, {"bar", "barbaz"}, {"baz", -2.0}, - {"quux", tag_compound{{"Hello", "World"}, {"zero", 0}}} + {"quux", tag_compound{{"Hello", "World"}, {"zero", 0}}}, + {"list", tag_list{int8_t(16), int8_t(17)}} }; ASSERT(comp == comp2); ASSERT(comp != (const tag_compound&)comp2["quux"]); ASSERT(comp != comp2["quux"]); - ASSERT(comp2.size() == 4); - const char* keys[] = {"bar", "baz", "foo", "quux"}; //alphabetic order + ASSERT(comp2.size() == 5); + const char* keys[] = {"bar", "baz", "foo", "list", "quux"}; //alphabetic order unsigned i = 0; for(const std::pair& val: comp2) { -- cgit 0.0.5-2-1-g0f52 From 125a60596531cb0717869dd8c54147f79335f5c2 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 12 Jul 2015 20:35:25 +0200 Subject: Create tag_list::of function --- include/tag_compound.h | 1 + include/tag_list.h | 22 ++++++++++++++++++++++ src/tag_list.cpp | 9 --------- test/nbttest.cpp | 9 +++++---- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/include/tag_compound.h b/include/tag_compound.h index 0789bed295..abf1df239a 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -42,6 +42,7 @@ public: ///Constructs an empty compound tag_compound() {} + ///Constructs a compound with the given key-value pairs tag_compound(std::initializer_list> init); /** diff --git a/include/tag_list.h b/include/tag_list.h index 12aa86d965..b69eeba2fa 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -45,6 +45,13 @@ public: ///The type of the tag static constexpr tag_type type = tag_type::List; + /** + * @brief Constructs a list with the given contents of type T + * @param init list of values that are, one by one, given to a constructor of T + */ + template + static tag_list of(std::initializer_list init); + /** * @brief Constructs an empty list * @@ -159,6 +166,12 @@ private: tag_list(T dummy, std::initializer_list init); }; +template +tag_list tag_list::of(std::initializer_list init) +{ + return tag_list(T(), std::move(init)); +} + template void tag_list::emplace_back(Args&&... args) { @@ -169,6 +182,15 @@ void tag_list::emplace_back(Args&&... args) tags.emplace_back(T(std::forward(args)...)); } +template +tag_list::tag_list(T dummy, std::initializer_list init): + el_type_(T::type) +{ + tags.reserve(init.size()); + for(const Arg& arg: init) + tags.emplace_back(T(arg)); +} + } #endif // TAG_LIST_H_INCLUDED diff --git a/src/tag_list.cpp b/src/tag_list.cpp index da35d19cac..df21d2dace 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -31,15 +31,6 @@ tag_list::tag_list(tag_type type): el_type_(type) {} -template -tag_list::tag_list(T dummy, std::initializer_list init): - el_type_(T::type) -{ - tags.reserve(init.size()); - for(const Arg& arg: init) - tags.emplace_back(T(arg)); -} - tag_list::tag_list(std::initializer_list init): tag_list(tag_byte() , init) {} tag_list::tag_list(std::initializer_list init): tag_list(tag_short() , init) {} tag_list::tag_list(std::initializer_list init): tag_list(tag_int() , init) {} diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 8553284543..abba16a8c6 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -82,7 +82,7 @@ void test_tag_compound() {"foo", int16_t(12)}, {"bar", "baz"}, {"baz", -2.0}, - {"list", tag_list{int8_t(16), int8_t(17)}} + {"list", tag_list::of({16, 17})} }; ASSERT(comp["foo"].get_type() == tag_type::Short); @@ -120,7 +120,7 @@ void test_tag_compound() {"bar", "barbaz"}, {"baz", -2.0}, {"quux", tag_compound{{"Hello", "World"}, {"zero", 0}}}, - {"list", tag_list{int8_t(16), int8_t(17)}} + {"list", tag_list::of({16, 17})} }; ASSERT(comp == comp2); ASSERT(comp != (const tag_compound&)comp2["quux"]); @@ -249,7 +249,7 @@ void test_tag_list() list.pop_back(); ASSERT(list == tag_list{"foo"}); - ASSERT(list == tag_list{std::string("foo")}); + ASSERT(list == tag_list::of({"foo"})); ASSERT((list != tag_list{2, 3, 5, 7})); list.clear(); @@ -260,8 +260,9 @@ void test_tag_list() ASSERT(tag_list() == tag_list(tag_type::Int)); ASSERT(tag_list(tag_type::Short) == tag_list(tag_type::Int)); - tag_list short_list{int16_t(25), int16_t(36)}; + tag_list short_list = tag_list::of({25, 36}); ASSERT(short_list.el_type() == tag_type::Short); + ASSERT((short_list == tag_list{int16_t(25), int16_t(36)})); ASSERT((short_list != tag_list{25, 36})); ASSERT((short_list == tag_list{value(tag_short(25)), value(tag_short(36))})); -- cgit 0.0.5-2-1-g0f52 From d2b7c2442da9363b13f884bd0c47fb757cb0f005 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 14 Jul 2015 13:50:48 +0200 Subject: Replace constructor and dummy argument with init method --- include/tag_list.h | 19 ++++++++++--------- src/tag_list.cpp | 22 +++++++++++----------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/include/tag_list.h b/include/tag_list.h index b69eeba2fa..1a2f20e353 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -157,19 +157,20 @@ private: tag_type el_type_; /** - * Internally used constructor that initializes the list with tags of - * type T, with the constructor arguments of each T given by init. - * @param dummy ignored, only used for inducing the template parameter T - * @param init list of values that are, one by one, given to a constructor of T + * 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 - tag_list(T dummy, std::initializer_list init); + void init(std::initializer_list il); }; template -tag_list tag_list::of(std::initializer_list init) +tag_list tag_list::of(std::initializer_list il) { - return tag_list(T(), std::move(init)); + tag_list result; + result.init(il); + return result; } template @@ -183,9 +184,9 @@ void tag_list::emplace_back(Args&&... args) } template -tag_list::tag_list(T dummy, std::initializer_list init): - el_type_(T::type) +void tag_list::init(std::initializer_list init) { + el_type_ = T::type; tags.reserve(init.size()); for(const Arg& arg: init) tags.emplace_back(T(arg)); diff --git a/src/tag_list.cpp b/src/tag_list.cpp index df21d2dace..488f29ebaa 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -31,17 +31,17 @@ tag_list::tag_list(tag_type type): el_type_(type) {} -tag_list::tag_list(std::initializer_list init): tag_list(tag_byte() , init) {} -tag_list::tag_list(std::initializer_list init): tag_list(tag_short() , init) {} -tag_list::tag_list(std::initializer_list init): tag_list(tag_int() , init) {} -tag_list::tag_list(std::initializer_list init): tag_list(tag_long() , init) {} -tag_list::tag_list(std::initializer_list init): tag_list(tag_float() , init) {} -tag_list::tag_list(std::initializer_list init): tag_list(tag_double() , init) {} -tag_list::tag_list(std::initializer_list init): tag_list(tag_string() , init) {} -//tag_list::tag_list(std::initializer_list init): tag_list(tag_byte_array(), init) {} -tag_list::tag_list(std::initializer_list init): tag_list(tag_list() , init) {} -tag_list::tag_list(std::initializer_list init): tag_list(tag_compound() , init) {} -//tag_list::tag_list(std::initializer_list init): tag_list(tag_int_array() , init) {} +tag_list::tag_list(std::initializer_list il) { init(il); } +tag_list::tag_list(std::initializer_list il) { init(il); } +tag_list::tag_list(std::initializer_list il) { init(il); } +tag_list::tag_list(std::initializer_list il) { init(il); } +tag_list::tag_list(std::initializer_list il) { init(il); } +tag_list::tag_list(std::initializer_list il) { init(il); } +tag_list::tag_list(std::initializer_list il) { init(il); } +//tag_list::tag_list(std::initializer_list il) { init(il); } +tag_list::tag_list(std::initializer_list il) { init(il); } +tag_list::tag_list(std::initializer_list il) { init(il); } +//tag_list::tag_list(std::initializer_list il) { init(il); } tag_list::tag_list(std::initializer_list init) { -- cgit 0.0.5-2-1-g0f52 From e56e6df99cd89b0aa6e0bf8fda723f39ede41806 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 14 Jul 2015 13:56:35 +0200 Subject: Add C string constructor to tag_string --- include/tag_string.h | 2 ++ src/tag_string.cpp | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/include/tag_string.h b/include/tag_string.h index f4476993e3..846650e507 100644 --- a/include/tag_string.h +++ b/include/tag_string.h @@ -35,6 +35,7 @@ public: tag_string(const std::string& str); tag_string(std::string&& str = ""); + tag_string(const char* str); operator std::string&(); operator const std::string&() const; @@ -42,6 +43,7 @@ public: tag_string& operator=(const std::string& str); tag_string& operator=(std::string&& str); + tag_string& operator=(const char* str); void set(const std::string& str); void set(std::string&& str); diff --git a/src/tag_string.cpp b/src/tag_string.cpp index 6bd1465e84..e4f714d0b3 100644 --- a/src/tag_string.cpp +++ b/src/tag_string.cpp @@ -30,6 +30,10 @@ tag_string::tag_string(std::string&& str): value(std::move(str)) {} +tag_string::tag_string(const char* str): + value(std::string(str)) +{} + tag_string::operator std::string&() { return value; @@ -57,6 +61,12 @@ tag_string& tag_string::operator=(std::string&& str) return *this; } +tag_string& tag_string::operator=(const char* str) +{ + value = std::string(str); + return *this; +} + void tag_string::set(const std::string& str) { value = str; -- cgit 0.0.5-2-1-g0f52 From 1f3911395a7f952c817e0de1755aa90e23998761 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 14 Jul 2015 13:58:48 +0200 Subject: Remove Arg parameter from tag_list::of --- include/tag_list.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/tag_list.h b/include/tag_list.h index 1a2f20e353..94403119dc 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -49,8 +49,8 @@ public: * @brief Constructs a list with the given contents of type T * @param init list of values that are, one by one, given to a constructor of T */ - template - static tag_list of(std::initializer_list init); + template + static tag_list of(std::initializer_list init); /** * @brief Constructs an empty list @@ -165,11 +165,11 @@ private: void init(std::initializer_list il); }; -template -tag_list tag_list::of(std::initializer_list il) +template +tag_list tag_list::of(std::initializer_list il) { tag_list result; - result.init(il); + result.init(il); return result; } -- cgit 0.0.5-2-1-g0f52 From ac24220bedd41cd0f811f70f9b0f65fa14c04589 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 14 Jul 2015 14:07:26 +0200 Subject: Add one more test for list element type --- test/nbttest.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index abba16a8c6..f48cec24b7 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -270,6 +270,7 @@ void test_tag_list() EXPECT_EXCEPTION((tag_list{value(nullptr), value(tag_int(6))}), std::bad_cast); EXPECT_EXCEPTION((tag_list{value(tag_int(7)), value(tag_int(8)), value(nullptr)}), std::bad_cast); ASSERT((tag_list(std::initializer_list{})).el_type() == tag_type::Null); + ASSERT((tag_list{2, 3, 5, 7}).el_type() == tag_type::Int); std::clog << "test_tag_list passed" << std::endl; } -- cgit 0.0.5-2-1-g0f52 From 6ec54ee1766a1819d62771620e91328a188341d2 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 14 Jul 2015 14:39:18 +0200 Subject: Turn tag_list::of into a subtype of tag_list So we can get rid of the double braces :3 --- include/tag_list.h | 35 +++++++++++++++++++---------------- test/nbttest.cpp | 9 +++++---- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/include/tag_list.h b/include/tag_list.h index 94403119dc..c97aaed66d 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -42,16 +42,12 @@ public: typedef std::vector::iterator iterator; typedef std::vector::const_iterator const_iterator; + template + class of; + ///The type of the tag static constexpr tag_type type = tag_type::List; - /** - * @brief Constructs a list with the given contents of type T - * @param init list of values that are, one by one, given to a constructor of T - */ - template - static tag_list of(std::initializer_list init); - /** * @brief Constructs an empty list * @@ -152,10 +148,7 @@ public: friend bool operator==(const tag_list& lhs, const tag_list& rhs); friend bool operator!=(const tag_list& lhs, const tag_list& rhs); -private: - std::vector tags; - tag_type el_type_; - +protected: /** * Internally used initialization function that initializes the list with * tags of type T, with the constructor arguments of each T given by il. @@ -163,15 +156,25 @@ private: */ template void init(std::initializer_list il); + +private: + std::vector tags; + tag_type el_type_; }; +/** + * @brief Subclass of tag_list with compile-time-known element type + * @todo Statically override some of the superclass methods to forgo dynamic type checking + */ template -tag_list tag_list::of(std::initializer_list il) +class tag_list::of : public tag_list { - tag_list result; - result.init(il); - return result; -} +public: + of(std::initializer_list il) + { + init(il); + } +}; template void tag_list::emplace_back(Args&&... args) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index f48cec24b7..12e36a5db7 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -82,7 +82,7 @@ void test_tag_compound() {"foo", int16_t(12)}, {"bar", "baz"}, {"baz", -2.0}, - {"list", tag_list::of({16, 17})} + {"list", tag_list::of{16, 17}} }; ASSERT(comp["foo"].get_type() == tag_type::Short); @@ -120,7 +120,7 @@ void test_tag_compound() {"bar", "barbaz"}, {"baz", -2.0}, {"quux", tag_compound{{"Hello", "World"}, {"zero", 0}}}, - {"list", tag_list::of({16, 17})} + {"list", tag_list::of{16, 17}} }; ASSERT(comp == comp2); ASSERT(comp != (const tag_compound&)comp2["quux"]); @@ -249,7 +249,8 @@ void test_tag_list() list.pop_back(); ASSERT(list == tag_list{"foo"}); - ASSERT(list == tag_list::of({"foo"})); + ASSERT(list == tag_list::of{"foo"}); + ASSERT(tag_list::of{"foo"} == tag_list{"foo"}); ASSERT((list != tag_list{2, 3, 5, 7})); list.clear(); @@ -260,7 +261,7 @@ void test_tag_list() ASSERT(tag_list() == tag_list(tag_type::Int)); ASSERT(tag_list(tag_type::Short) == tag_list(tag_type::Int)); - tag_list short_list = tag_list::of({25, 36}); + tag_list::of short_list{25, 36}; ASSERT(short_list.el_type() == tag_type::Short); ASSERT((short_list == tag_list{int16_t(25), int16_t(36)})); ASSERT((short_list != tag_list{25, 36})); -- cgit 0.0.5-2-1-g0f52 From 0060f816c7798148556ebeb445510a3cc78161f2 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 15 Jul 2015 15:42:23 +0200 Subject: Revert "Turn tag_list::of into a subtype of tag_list" This reverts commit 6ec54ee1766a1819d62771620e91328a188341d2. --- include/tag_list.h | 35 ++++++++++++++++------------------- test/nbttest.cpp | 10 +++++----- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/include/tag_list.h b/include/tag_list.h index c97aaed66d..94403119dc 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -42,12 +42,16 @@ public: typedef std::vector::iterator iterator; typedef std::vector::const_iterator const_iterator; - template - class of; - ///The type of the tag static constexpr tag_type type = tag_type::List; + /** + * @brief Constructs a list with the given contents of type T + * @param init list of values that are, one by one, given to a constructor of T + */ + template + static tag_list of(std::initializer_list init); + /** * @brief Constructs an empty list * @@ -148,7 +152,10 @@ public: friend bool operator==(const tag_list& lhs, const tag_list& rhs); friend bool operator!=(const tag_list& lhs, const tag_list& rhs); -protected: +private: + std::vector 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. @@ -156,25 +163,15 @@ protected: */ template void init(std::initializer_list il); - -private: - std::vector tags; - tag_type el_type_; }; -/** - * @brief Subclass of tag_list with compile-time-known element type - * @todo Statically override some of the superclass methods to forgo dynamic type checking - */ template -class tag_list::of : public tag_list +tag_list tag_list::of(std::initializer_list il) { -public: - of(std::initializer_list il) - { - init(il); - } -}; + tag_list result; + result.init(il); + return result; +} template void tag_list::emplace_back(Args&&... args) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 12e36a5db7..3e043f750b 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -82,7 +82,7 @@ void test_tag_compound() {"foo", int16_t(12)}, {"bar", "baz"}, {"baz", -2.0}, - {"list", tag_list::of{16, 17}} + {"list", tag_list::of({16, 17})} }; ASSERT(comp["foo"].get_type() == tag_type::Short); @@ -120,7 +120,7 @@ void test_tag_compound() {"bar", "barbaz"}, {"baz", -2.0}, {"quux", tag_compound{{"Hello", "World"}, {"zero", 0}}}, - {"list", tag_list::of{16, 17}} + {"list", tag_list::of({16, 17})} }; ASSERT(comp == comp2); ASSERT(comp != (const tag_compound&)comp2["quux"]); @@ -249,8 +249,8 @@ void test_tag_list() list.pop_back(); ASSERT(list == tag_list{"foo"}); - ASSERT(list == tag_list::of{"foo"}); - ASSERT(tag_list::of{"foo"} == tag_list{"foo"}); + ASSERT(list == tag_list::of({"foo"})); + ASSERT(tag_list::of({"foo"}) == tag_list{"foo"}); ASSERT((list != tag_list{2, 3, 5, 7})); list.clear(); @@ -261,7 +261,7 @@ void test_tag_list() ASSERT(tag_list() == tag_list(tag_type::Int)); ASSERT(tag_list(tag_type::Short) == tag_list(tag_type::Int)); - tag_list::of short_list{25, 36}; + tag_list short_list = tag_list::of({25, 36}); ASSERT(short_list.el_type() == tag_type::Short); ASSERT((short_list == tag_list{int16_t(25), int16_t(36)})); ASSERT((short_list != tag_list{25, 36})); -- cgit 0.0.5-2-1-g0f52 From 9eb00e57addf80fd0c8aca7d573b3075d26602cd Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 15 Jul 2015 16:44:59 +0200 Subject: Create make_unique function --- include/crtp_tag.h | 9 +++------ include/make_unique.h | 37 +++++++++++++++++++++++++++++++++++++ test/nbttest.cpp | 2 +- 3 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 include/make_unique.h diff --git a/include/crtp_tag.h b/include/crtp_tag.h index 8611038ffa..f227fec9cf 100644 --- a/include/crtp_tag.h +++ b/include/crtp_tag.h @@ -21,6 +21,7 @@ #define CRTP_TAG_H_INCLUDED #include "tag.h" +#include "make_unique.h" namespace nbt { @@ -58,17 +59,13 @@ namespace detail template std::unique_ptr crtp_tag::clone() const& { - return std::unique_ptr( - new Sub(static_cast(*this)) - ); + return make_unique(static_cast(*this)); } template std::unique_ptr crtp_tag::move_clone() && { - return std::unique_ptr( - new Sub(static_cast(*this)) - ); + return make_unique(static_cast(*this)); } template diff --git a/include/make_unique.h b/include/make_unique.h new file mode 100644 index 0000000000..9a929543cc --- /dev/null +++ b/include/make_unique.h @@ -0,0 +1,37 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#ifndef MAKE_UNIQUE_H_INCLUDED +#define MAKE_UNIQUE_H_INCLUDED + +#include + +namespace nbt +{ + +///Creates a new object of type T and returns a std::unique_ptr to it +template +std::unique_ptr make_unique(Args&&... args) +{ + return std::unique_ptr(new T(std::forward(args)...)); +} + +} + +#endif // MAKE_UNIQUE_H_INCLUDED diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 3e043f750b..3b72c102e5 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -163,7 +163,7 @@ void test_tag_compound() void test_value() { value val1; - value val2(std::unique_ptr(new tag_int(42))); + value val2(make_unique(42)); value val3(tag_int(42)); ASSERT(!val1 && val2 && val3); -- cgit 0.0.5-2-1-g0f52 From a38d666be5330a1391eddf490574aa07f7dae9c7 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 15 Jul 2015 17:49:45 +0200 Subject: Make tag_list class final --- include/tag_list.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/tag_list.h b/include/tag_list.h index 94403119dc..a2170c4f0f 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -35,7 +35,7 @@ class tag_byte_array; class tag_int_array; ///Tag that contains multiple unnamed tags of the same type -class tag_list : public detail::crtp_tag +class tag_list final : public detail::crtp_tag { public: //Iterator types -- cgit 0.0.5-2-1-g0f52 From 367f7ef26704011929ea8e2bef9d9e73052897c6 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 15 Jul 2015 17:55:04 +0200 Subject: Add tag_array class stub --- include/libnbt.h | 1 + include/tag_array.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 include/tag_array.h diff --git a/include/libnbt.h b/include/libnbt.h index 965fa309ca..9a2198778c 100644 --- a/include/libnbt.h +++ b/include/libnbt.h @@ -24,5 +24,6 @@ #include "tag_string.h" #include "tag_compound.h" #include "tag_list.h" +#include "tag_array.h" #endif // LIBNBT_H_INCLUDED diff --git a/include/tag_array.h b/include/tag_array.h new file mode 100644 index 0000000000..ee36cdacc8 --- /dev/null +++ b/include/tag_array.h @@ -0,0 +1,45 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#ifndef TAG_ARRAY_H_INCLUDED +#define TAG_ARRAY_H_INCLUDED + +#include "crtp_tag.h" + +namespace nbt +{ + +/** + * @brief Tag that contains an array of byte or int values + * + * Common class for tag_byte_array and tag_int_array. + */ +template +class tag_array final : public detail::crtp_tag> +{ + +}; + +//Typedefs that should be used instead of the template tag_array. +typedef tag_array tag_byte_array; +typedef tag_array tag_int_array; + +} + +#endif // TAG_ARRAY_H_INCLUDED -- cgit 0.0.5-2-1-g0f52 From f198ab5fbf31c107e82e05969ea72abb8872fc11 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 15 Jul 2015 17:55:19 +0200 Subject: Fix forward declarations for tag_array --- include/tag_list.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/tag_list.h b/include/tag_list.h index a2170c4f0f..d34ca83b20 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -31,8 +31,9 @@ namespace nbt //Forward declarations class tag_list; class tag_compound; -class tag_byte_array; -class tag_int_array; +template class tag_array; +typedef tag_array tag_byte_array; +typedef tag_array tag_int_array; ///Tag that contains multiple unnamed tags of the same type class tag_list final : public detail::crtp_tag -- cgit 0.0.5-2-1-g0f52 From 6fb5d9850e0fb08c3cd7bdb7dd4f60a03dd0a948 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 15 Jul 2015 18:04:46 +0200 Subject: Put tag forward declarations in separate header --- include/tag_list.h | 8 +------- include/tagfwd.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 include/tagfwd.h diff --git a/include/tag_list.h b/include/tag_list.h index d34ca83b20..a84e010f04 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -21,6 +21,7 @@ #define TAG_LIST_H_INCLUDED #include "crtp_tag.h" +#include "tagfwd.h" #include "value.h" #include #include @@ -28,13 +29,6 @@ namespace nbt { -//Forward declarations -class tag_list; -class tag_compound; -template class tag_array; -typedef tag_array tag_byte_array; -typedef tag_array tag_int_array; - ///Tag that contains multiple unnamed tags of the same type class tag_list final : public detail::crtp_tag { diff --git a/include/tagfwd.h b/include/tagfwd.h new file mode 100644 index 0000000000..98d75b8328 --- /dev/null +++ b/include/tagfwd.h @@ -0,0 +1,48 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +/** @file + * @brief Provides forward declarations for all tag classes + */ +#ifndef TAGFWD_H_INCLUDED +#define TAGFWD_H_INCLUDED + +namespace nbt +{ + +class tag; + +template class tag_primitive; +typedef tag_primitive tag_byte; +typedef tag_primitive tag_short; +typedef tag_primitive tag_int; +typedef tag_primitive tag_long; +typedef tag_primitive tag_float; +typedef tag_primitive tag_double; + +template class tag_array; +typedef tag_array tag_byte_array; +typedef tag_array tag_int_array; + +class tag_list; +class tag_compound; + +} + +#endif // TAGFWD_H_INCLUDED -- cgit 0.0.5-2-1-g0f52 From 1434698cc01d9e5b2c73ef7b9fc83de034a96241 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 15 Jul 2015 18:27:51 +0200 Subject: Add tests for primitive numeric limits --- include/primitive_detail.h | 4 ++++ test/nbttest.cpp | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/include/primitive_detail.h b/include/primitive_detail.h index dea7ff225f..aaf33ff563 100644 --- a/include/primitive_detail.h +++ b/include/primitive_detail.h @@ -20,8 +20,12 @@ #ifndef PRIMITIVE_DETAIL_H_INCLUDED #define PRIMITIVE_DETAIL_H_INCLUDED +#include #include +static_assert(std::numeric_limits::is_iec559 && std::numeric_limits::is_iec559, + "The floating point values for NBT must conform to IEC 559/IEEE 754"); + ///@cond namespace nbt { diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 3b72c102e5..516202c57c 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -52,6 +52,15 @@ void test_tag_primitive() ASSERT(7 == static_cast(tag)); ASSERT(tag_double() == 0.0); + + ASSERT(tag_byte(INT8_MAX).get() == INT8_MAX); + ASSERT(tag_byte(INT8_MIN).get() == INT8_MIN); + ASSERT(tag_short(INT16_MAX).get() == INT16_MAX); + ASSERT(tag_short(INT16_MIN).get() == INT16_MIN); + ASSERT(tag_int(INT32_MAX).get() == INT32_MAX); + ASSERT(tag_int(INT32_MIN).get() == INT32_MIN); + ASSERT(tag_long(INT64_MAX).get() == INT64_MAX); + ASSERT(tag_long(INT64_MIN).get() == INT64_MIN); std::clog << "test_tag_primitive passed" << std::endl; } -- cgit 0.0.5-2-1-g0f52 From 593c3f241f3f6775226341a4343a34dc00358ac8 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 15 Jul 2015 18:42:58 +0200 Subject: Declare tag_array methods --- include/tag_array.h | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tag_list.cpp | 4 ++-- test/nbttest.cpp | 4 ++-- 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/include/tag_array.h b/include/tag_array.h index ee36cdacc8..4283ea4fac 100644 --- a/include/tag_array.h +++ b/include/tag_array.h @@ -21,6 +21,7 @@ #define TAG_ARRAY_H_INCLUDED #include "crtp_tag.h" +#include namespace nbt { @@ -33,7 +34,60 @@ namespace nbt template class tag_array final : public detail::crtp_tag> { +public: + //Iterator types + typedef typename std::vector::iterator iterator; + typedef typename std::vector::const_iterator const_iterator; + ///The type of the contained values + typedef T value_type; + + ///The type of the tag + static constexpr tag_type type = tag_type::Byte_Array; + + ///Constructs an empty array + tag_array(); + + ///Constructs an array with the given values + tag_array(std::initializer_list init); + + /** + * @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); + T at(size_t i) const; + + /** + * @brief Accesses a value by index + * + * No bounds checking is performed. + */ + T& operator[](size_t i); + T operator[](size_t i) const; + + ///Appends a value at the end of the array + void push_back(T val); + + ///Removes the last element from the array + void pop_back(); + + ///Returns the number of values in the array + size_t size() const; + + ///Erases all values from the array. + void clear(); + + //Iterators + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + const_iterator cbegin() const; + const_iterator cend() const; + +private: + std::vector values; }; //Typedefs that should be used instead of the template tag_array. diff --git a/src/tag_list.cpp b/src/tag_list.cpp index 488f29ebaa..b483819405 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -38,10 +38,10 @@ tag_list::tag_list(std::initializer_list il) { init(il tag_list::tag_list(std::initializer_list il) { init(il); } tag_list::tag_list(std::initializer_list il) { init(il); } tag_list::tag_list(std::initializer_list il) { init(il); } -//tag_list::tag_list(std::initializer_list il) { init(il); } +tag_list::tag_list(std::initializer_list il) { init(il); } tag_list::tag_list(std::initializer_list il) { init(il); } tag_list::tag_list(std::initializer_list il) { init(il); } -//tag_list::tag_list(std::initializer_list il) { init(il); } +tag_list::tag_list(std::initializer_list il) { init(il); } tag_list::tag_list(std::initializer_list init) { diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 516202c57c..7193fe0141 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -30,11 +30,11 @@ void test_get_type() ASSERT(tag_long().get_type() == tag_type::Long); ASSERT(tag_float().get_type() == tag_type::Float); ASSERT(tag_double().get_type() == tag_type::Double); - //ASSERT(tag_byte_array().get_type() == tag_type::Byte_Array); + ASSERT(tag_byte_array().get_type() == tag_type::Byte_Array); ASSERT(tag_string().get_type() == tag_type::String); ASSERT(tag_list().get_type() == tag_type::List); ASSERT(tag_compound().get_type() == tag_type::Compound); - //ASSERT(tag_int_array().get_type() == tag_type::Int_Array); + ASSERT(tag_int_array().get_type() == tag_type::Int_Array); std::clog << "test_get_type passed" << std::endl; } -- cgit 0.0.5-2-1-g0f52 From 3d1965752496afd8f6cdff38a23cc0814c782725 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 15 Jul 2015 19:03:12 +0200 Subject: Use std::equal for tag_list iterator testing --- test/nbttest.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 7193fe0141..9599eb1799 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -19,6 +19,7 @@ */ #include "microtest.h" #include "libnbt.h" +#include using namespace nbt; @@ -247,14 +248,10 @@ void test_tag_list() list.set(1, value(tag_string("baz"))); ASSERT(std::string(list[1]) == "baz"); - const char* values[] = {"foo", "baz"}; - unsigned i = 0; - for(const value& val: list) - { - ASSERT(i < list.size()); - ASSERT(std::string(val) == values[i]); - ++i; - } + ASSERT(list.size() == 2); + tag_string values[] = {"foo", "baz"}; + ASSERT(list.end() - list.begin() == int(list.size())); + ASSERT(std::equal(list.begin(), list.end(), values)); list.pop_back(); ASSERT(list == tag_list{"foo"}); -- cgit 0.0.5-2-1-g0f52 From dea5b824c5c2a36e2b7bbbcb24f7863630315986 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 15 Jul 2015 19:35:38 +0200 Subject: Create tests for tag_array --- test/nbttest.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 9599eb1799..0ba3a1db6c 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -281,6 +281,59 @@ void test_tag_list() std::clog << "test_tag_list passed" << std::endl; } +void test_tag_byte_array() +{ + tag_byte_array arr{1, 2, 127, -128}; + ASSERT(arr.size() == 4); + ASSERT(arr.at(0) == 1 && arr[1] == 2 && arr[2] == 127 && arr.at(3) == -128); + EXPECT_EXCEPTION(arr.at(-1), std::out_of_range); + EXPECT_EXCEPTION(arr.at(4), std::out_of_range); + + arr.push_back(42); + + ASSERT(arr.size() == 5); + int8_t values[] = {1, 2, 127, -128, 42}; + ASSERT(arr.end() - arr.begin() == int(arr.size())); + ASSERT(std::equal(arr.begin(), arr.end(), values)); + + arr.pop_back(); + arr.pop_back(); + ASSERT(arr.size() == 3); + ASSERT((arr == tag_byte_array{1, 2, 127})); + ASSERT((arr != tag_int_array{1, 2, 127})); + ASSERT((arr != tag_byte_array{1, 2, -1})); + + arr.clear(); + ASSERT(arr == tag_byte_array()); + std::clog << "test_tag_byte_array passed" << std::endl; +} + +void test_tag_int_array() +{ + tag_int_array arr{100, 200, INT32_MAX, INT32_MIN}; + ASSERT(arr.size() == 4); + ASSERT(arr.at(0) == 100 && arr[1] == 200 && arr[2] == INT32_MAX && arr.at(3) == INT32_MIN); + EXPECT_EXCEPTION(arr.at(-1), std::out_of_range); + EXPECT_EXCEPTION(arr.at(4), std::out_of_range); + + arr.push_back(42); + + ASSERT(arr.size() == 5); + int32_t values[] = {100, 200, INT32_MAX, INT32_MIN, 42}; + ASSERT(arr.end() - arr.begin() == int(arr.size())); + ASSERT(std::equal(arr.begin(), arr.end(), values)); + + arr.pop_back(); + arr.pop_back(); + ASSERT(arr.size() == 3); + ASSERT((arr == tag_int_array{100, 200, INT32_MAX})); + ASSERT((arr != tag_int_array{100, -56, -1})); + + arr.clear(); + ASSERT(arr == tag_int_array()); + std::clog << "test_tag_int_array passed" << std::endl; +} + int main() { test_get_type(); @@ -289,4 +342,6 @@ int main() test_tag_compound(); test_value(); test_tag_list(); + test_tag_byte_array(); + test_tag_int_array(); } -- cgit 0.0.5-2-1-g0f52 From ea404fc070d15cf53fb6e0099f8e68771da529fd Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 15 Jul 2015 19:35:51 +0200 Subject: Implement tag_array --- include/primitive_detail.h | 4 +- include/tag_array.h | 95 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 92 insertions(+), 7 deletions(-) diff --git a/include/primitive_detail.h b/include/primitive_detail.h index aaf33ff563..d124e738da 100644 --- a/include/primitive_detail.h +++ b/include/primitive_detail.h @@ -34,9 +34,7 @@ namespace detail { ///Meta-struct that holds the tag_type value for a specific primitive type template struct get_primitive_type - { - static_assert(sizeof(T) != sizeof(T), "Can only use primitive types used by NBT as parameter for tag_primitive"); - }; + { static_assert(sizeof(T) != sizeof(T), "Can only use primitive types used by NBT as parameter for tag_primitive"); }; template<> struct get_primitive_type : public std::integral_constant {}; template<> struct get_primitive_type : public std::integral_constant {}; diff --git a/include/tag_array.h b/include/tag_array.h index 4283ea4fac..159e432626 100644 --- a/include/tag_array.h +++ b/include/tag_array.h @@ -21,11 +21,23 @@ #define TAG_ARRAY_H_INCLUDED #include "crtp_tag.h" +#include #include namespace nbt { +namespace detail +{ + + template struct get_array_type + { static_assert(sizeof(T) != sizeof(T), "Can only use byte or int as parameter for tag_array"); }; + + template<> struct get_array_type : public std::integral_constant {}; + template<> struct get_array_type : public std::integral_constant {}; + +} + /** * @brief Tag that contains an array of byte or int values * @@ -43,10 +55,10 @@ public: typedef T value_type; ///The type of the tag - static constexpr tag_type type = tag_type::Byte_Array; + static constexpr tag_type type = detail::get_array_type::value; ///Constructs an empty array - tag_array(); + tag_array() {} ///Constructs an array with the given values tag_array(std::initializer_list init); @@ -86,14 +98,89 @@ public: const_iterator cbegin() const; const_iterator cend() const; -private: - std::vector values; + ///The vector that holds the values + std::vector data; }; +template bool operator==(const tag_array& lhs, const tag_array& rhs); +template bool operator!=(const tag_array& lhs, const tag_array& rhs); + //Typedefs that should be used instead of the template tag_array. typedef tag_array tag_byte_array; typedef tag_array tag_int_array; +template +tag_array::tag_array(std::initializer_list init): + data(init) +{} + +template +T& tag_array::at(size_t i) +{ + return data.at(i); +} + +template +T tag_array::at(size_t i) const +{ + return data.at(i); +} + +template +T& tag_array::operator[](size_t i) +{ + return data[i]; +} + +template +T tag_array::operator[](size_t i) const +{ + return data[i]; +} + +template +void tag_array::push_back(T val) +{ + data.push_back(val); +} + +template +void tag_array::pop_back() +{ + data.pop_back(); +} + +template +size_t tag_array::size() const +{ + return data.size(); +} + +template +void tag_array::clear() +{ + data.clear(); +} + +template auto tag_array::begin() -> iterator { return data.begin(); } +template auto tag_array::end() -> iterator { return data.end(); } +template auto tag_array::begin() const -> const_iterator { return data.begin(); } +template auto tag_array::end() const -> const_iterator { return data.end(); } +template auto tag_array::cbegin() const -> const_iterator { return data.cbegin(); } +template auto tag_array::cend() const -> const_iterator { return data.cend(); } + +template +bool operator==(const tag_array& lhs, const tag_array& rhs) +{ + return lhs.data == rhs.data; +} + +template +bool operator!=(const tag_array& lhs, const tag_array& rhs) +{ + return !(lhs == rhs); +} + } #endif // TAG_ARRAY_H_INCLUDED -- cgit 0.0.5-2-1-g0f52 From 95f02bc15bb1f45c0d016a2c3f16d451f8da8aaa Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 15 Jul 2015 21:21:20 +0200 Subject: Make tag_array::data private and add accessor --- include/tag_array.h | 26 ++++++++++++++++++++++++-- test/nbttest.cpp | 16 ++++++++++++---- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/include/tag_array.h b/include/tag_array.h index 159e432626..41ec1e573e 100644 --- a/include/tag_array.h +++ b/include/tag_array.h @@ -62,6 +62,11 @@ public: ///Constructs an array with the given values tag_array(std::initializer_list init); + tag_array(std::vector&& vec); + + ///Returns a reference to the vector that contains the values + std::vector& get(); + const std::vector& get() const; /** * @brief Accesses a value by index with bounds checking @@ -98,7 +103,7 @@ public: const_iterator cbegin() const; const_iterator cend() const; - ///The vector that holds the values +private: std::vector data; }; @@ -114,6 +119,23 @@ tag_array::tag_array(std::initializer_list init): data(init) {} +template +tag_array::tag_array(std::vector&& vec): + data(std::move(vec)) +{} + +template +std::vector& tag_array::get() +{ + return data; +} + +template +const std::vector& tag_array::get() const +{ + return data; +} + template T& tag_array::at(size_t i) { @@ -172,7 +194,7 @@ template auto tag_array::cend() const -> const_iterator { return d template bool operator==(const tag_array& lhs, const tag_array& rhs) { - return lhs.data == rhs.data; + return lhs.get() == rhs.get(); } template diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 0ba3a1db6c..e6256fe8c5 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -283,18 +283,22 @@ void test_tag_list() void test_tag_byte_array() { + std::vector vec{1, 2, 127, -128}; tag_byte_array arr{1, 2, 127, -128}; ASSERT(arr.size() == 4); ASSERT(arr.at(0) == 1 && arr[1] == 2 && arr[2] == 127 && arr.at(3) == -128); EXPECT_EXCEPTION(arr.at(-1), std::out_of_range); EXPECT_EXCEPTION(arr.at(4), std::out_of_range); + ASSERT(arr.get() == vec); + ASSERT(arr == tag_byte_array(std::vector(vec))); + arr.push_back(42); + vec.push_back(42); ASSERT(arr.size() == 5); - int8_t values[] = {1, 2, 127, -128, 42}; ASSERT(arr.end() - arr.begin() == int(arr.size())); - ASSERT(std::equal(arr.begin(), arr.end(), values)); + ASSERT(std::equal(arr.begin(), arr.end(), vec.begin())); arr.pop_back(); arr.pop_back(); @@ -310,18 +314,22 @@ void test_tag_byte_array() void test_tag_int_array() { + std::vector vec{100, 200, INT32_MAX, INT32_MIN}; tag_int_array arr{100, 200, INT32_MAX, INT32_MIN}; ASSERT(arr.size() == 4); ASSERT(arr.at(0) == 100 && arr[1] == 200 && arr[2] == INT32_MAX && arr.at(3) == INT32_MIN); EXPECT_EXCEPTION(arr.at(-1), std::out_of_range); EXPECT_EXCEPTION(arr.at(4), std::out_of_range); + ASSERT(arr.get() == vec); + ASSERT(arr == tag_int_array(std::vector(vec))); + arr.push_back(42); + vec.push_back(42); ASSERT(arr.size() == 5); - int32_t values[] = {100, 200, INT32_MAX, INT32_MIN, 42}; ASSERT(arr.end() - arr.begin() == int(arr.size())); - ASSERT(std::equal(arr.begin(), arr.end(), values)); + ASSERT(std::equal(arr.begin(), arr.end(), vec.begin())); arr.pop_back(); arr.pop_back(); -- cgit 0.0.5-2-1-g0f52 From 189451e21e0e8603270a9b03adda46e9b9509143 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 15 Jul 2015 21:31:49 +0200 Subject: Move implementation of tag_primitive to .cpp file --- include/primitive_detail.h | 6 +--- include/tag_primitive.h | 48 -------------------------- src/tag_primitive.cpp | 85 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 53 deletions(-) create mode 100644 src/tag_primitive.cpp diff --git a/include/primitive_detail.h b/include/primitive_detail.h index d124e738da..4715ee7ed6 100644 --- a/include/primitive_detail.h +++ b/include/primitive_detail.h @@ -20,12 +20,8 @@ #ifndef PRIMITIVE_DETAIL_H_INCLUDED #define PRIMITIVE_DETAIL_H_INCLUDED -#include #include -static_assert(std::numeric_limits::is_iec559 && std::numeric_limits::is_iec559, - "The floating point values for NBT must conform to IEC 559/IEEE 754"); - ///@cond namespace nbt { @@ -34,7 +30,7 @@ namespace detail { ///Meta-struct that holds the tag_type value for a specific primitive type template struct get_primitive_type - { static_assert(sizeof(T) != sizeof(T), "Can only use primitive types used by NBT as parameter for tag_primitive"); }; + { static_assert(sizeof(T) != sizeof(T), "Invalid type paramter for tag_primitive, can only use types that NBT uses"); }; template<> struct get_primitive_type : public std::integral_constant {}; template<> struct get_primitive_type : public std::integral_constant {}; diff --git a/include/tag_primitive.h b/include/tag_primitive.h index 650fc1ad9c..fd8de3906e 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -65,54 +65,6 @@ typedef tag_primitive tag_long; typedef tag_primitive tag_float; typedef tag_primitive tag_double; -template -tag_primitive::tag_primitive(T val): - value(val) -{} - -template -tag_primitive& tag_primitive::operator=(T val) -{ - value = val; - return *this; -} - -template -void tag_primitive::set(T val) -{ - value = val; -} - -template -tag_primitive::operator T&() -{ - return value; -} - -template -tag_primitive::operator T() const -{ - return value; -} - -template -T tag_primitive::get() const -{ - return value; -} - -template -bool operator==(const tag_primitive& lhs, const tag_primitive& rhs) -{ - return lhs.get() == rhs.get(); -} - -template -bool operator!=(const tag_primitive& lhs, const tag_primitive& rhs) -{ - return !(lhs == rhs); -} - } #endif // TAG_PRIMITIVE_H_INCLUDED diff --git a/src/tag_primitive.cpp b/src/tag_primitive.cpp new file mode 100644 index 0000000000..30a15cc476 --- /dev/null +++ b/src/tag_primitive.cpp @@ -0,0 +1,85 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include "tag_primitive.h" +#include + +namespace nbt +{ + +static_assert(std::numeric_limits::is_iec559 && std::numeric_limits::is_iec559, + "The floating point values for NBT must conform to IEC 559/IEEE 754"); + +template +tag_primitive::tag_primitive(T val): + value(val) +{} + +template +tag_primitive& tag_primitive::operator=(T val) +{ + value = val; + return *this; +} + +template +void tag_primitive::set(T val) +{ + value = val; +} + +template +tag_primitive::operator T&() +{ + return value; +} + +template +tag_primitive::operator T() const +{ + return value; +} + +template +T tag_primitive::get() const +{ + return value; +} + +template +bool operator==(const tag_primitive& lhs, const tag_primitive& rhs) +{ + return lhs.get() == rhs.get(); +} + +template +bool operator!=(const tag_primitive& lhs, const tag_primitive& rhs) +{ + return !(lhs == rhs); +} + +//Enforce instantiations +template class tag_primitive; +template class tag_primitive; +template class tag_primitive; +template class tag_primitive; +template class tag_primitive; +template class tag_primitive; + +} -- cgit 0.0.5-2-1-g0f52 From ce3287483e396697d9c4b50629b6c4d3581bdec4 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 15 Jul 2015 21:49:04 +0200 Subject: Need to enforce operator!= too ...come on, it's worth the shorter compilation time --- src/tag_primitive.cpp | 9 ++++++++- test/nbttest.cpp | 4 ++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/tag_primitive.cpp b/src/tag_primitive.cpp index 30a15cc476..7f71098bd6 100644 --- a/src/tag_primitive.cpp +++ b/src/tag_primitive.cpp @@ -74,12 +74,19 @@ bool operator!=(const tag_primitive& lhs, const tag_primitive& rhs) return !(lhs == rhs); } -//Enforce instantiations +//Enforce template instantiations template class tag_primitive; template class tag_primitive; template class tag_primitive; template class tag_primitive; template class tag_primitive; template class tag_primitive; +//Need to also instantiate operator!=, whereas operator== already gets instantiated in crtp_tag +template bool operator!= (const tag_primitive& , const tag_primitive&); +template bool operator!=(const tag_primitive&, const tag_primitive&); +template bool operator!=(const tag_primitive&, const tag_primitive&); +template bool operator!=(const tag_primitive&, const tag_primitive&); +template bool operator!= (const tag_primitive& , const tag_primitive&); +template bool operator!= (const tag_primitive& , const tag_primitive&); } diff --git a/test/nbttest.cpp b/test/nbttest.cpp index e6256fe8c5..89181a73c3 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -52,6 +52,10 @@ void test_tag_primitive() tag = 7; ASSERT(7 == static_cast(tag)); + ASSERT(tag == tag_int(7)); + ASSERT(tag_float(2.5) != tag_float(-2.5)); + ASSERT(tag_float(2.5) != tag_double(2.5)); + ASSERT(tag_double() == 0.0); ASSERT(tag_byte(INT8_MAX).get() == INT8_MAX); -- cgit 0.0.5-2-1-g0f52 From 578dd6911154a836cda92b5d2cef15efae7c96b9 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 15 Jul 2015 21:50:55 +0200 Subject: Move implementation of tag_array into .cpp file --- include/tag_array.h | 96 ++--------------------------------------- src/tag_array.cpp | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 92 deletions(-) create mode 100644 src/tag_array.cpp diff --git a/include/tag_array.h b/include/tag_array.h index 41ec1e573e..7aca60722b 100644 --- a/include/tag_array.h +++ b/include/tag_array.h @@ -27,16 +27,17 @@ namespace nbt { +///@cond namespace detail { - + ///Meta-struct that holds the tag_type value for a specific array type template struct get_array_type - { static_assert(sizeof(T) != sizeof(T), "Can only use byte or int as parameter for tag_array"); }; + { static_assert(sizeof(T) != sizeof(T), "Invalid type paramter for tag_primitive, can only use byte or int"); }; template<> struct get_array_type : public std::integral_constant {}; template<> struct get_array_type : public std::integral_constant {}; - } +///@cond /** * @brief Tag that contains an array of byte or int values @@ -114,95 +115,6 @@ template bool operator!=(const tag_array& lhs, const tag_array& r typedef tag_array tag_byte_array; typedef tag_array tag_int_array; -template -tag_array::tag_array(std::initializer_list init): - data(init) -{} - -template -tag_array::tag_array(std::vector&& vec): - data(std::move(vec)) -{} - -template -std::vector& tag_array::get() -{ - return data; -} - -template -const std::vector& tag_array::get() const -{ - return data; -} - -template -T& tag_array::at(size_t i) -{ - return data.at(i); -} - -template -T tag_array::at(size_t i) const -{ - return data.at(i); -} - -template -T& tag_array::operator[](size_t i) -{ - return data[i]; -} - -template -T tag_array::operator[](size_t i) const -{ - return data[i]; -} - -template -void tag_array::push_back(T val) -{ - data.push_back(val); -} - -template -void tag_array::pop_back() -{ - data.pop_back(); -} - -template -size_t tag_array::size() const -{ - return data.size(); -} - -template -void tag_array::clear() -{ - data.clear(); -} - -template auto tag_array::begin() -> iterator { return data.begin(); } -template auto tag_array::end() -> iterator { return data.end(); } -template auto tag_array::begin() const -> const_iterator { return data.begin(); } -template auto tag_array::end() const -> const_iterator { return data.end(); } -template auto tag_array::cbegin() const -> const_iterator { return data.cbegin(); } -template auto tag_array::cend() const -> const_iterator { return data.cend(); } - -template -bool operator==(const tag_array& lhs, const tag_array& rhs) -{ - return lhs.get() == rhs.get(); -} - -template -bool operator!=(const tag_array& lhs, const tag_array& rhs) -{ - return !(lhs == rhs); -} - } #endif // TAG_ARRAY_H_INCLUDED diff --git a/src/tag_array.cpp b/src/tag_array.cpp new file mode 100644 index 0000000000..0db749d1f6 --- /dev/null +++ b/src/tag_array.cpp @@ -0,0 +1,120 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include "tag_array.h" + +namespace nbt +{ + +template +tag_array::tag_array(std::initializer_list init): + data(init) +{} + +template +tag_array::tag_array(std::vector&& vec): + data(std::move(vec)) +{} + +template +std::vector& tag_array::get() +{ + return data; +} + +template +const std::vector& tag_array::get() const +{ + return data; +} + +template +T& tag_array::at(size_t i) +{ + return data.at(i); +} + +template +T tag_array::at(size_t i) const +{ + return data.at(i); +} + +template +T& tag_array::operator[](size_t i) +{ + return data[i]; +} + +template +T tag_array::operator[](size_t i) const +{ + return data[i]; +} + +template +void tag_array::push_back(T val) +{ + data.push_back(val); +} + +template +void tag_array::pop_back() +{ + data.pop_back(); +} + +template +size_t tag_array::size() const +{ + return data.size(); +} + +template +void tag_array::clear() +{ + data.clear(); +} + +template auto tag_array::begin() -> iterator { return data.begin(); } +template auto tag_array::end() -> iterator { return data.end(); } +template auto tag_array::begin() const -> const_iterator { return data.begin(); } +template auto tag_array::end() const -> const_iterator { return data.end(); } +template auto tag_array::cbegin() const -> const_iterator { return data.cbegin(); } +template auto tag_array::cend() const -> const_iterator { return data.cend(); } + +template +bool operator==(const tag_array& lhs, const tag_array& rhs) +{ + return lhs.get() == rhs.get(); +} + +template +bool operator!=(const tag_array& lhs, const tag_array& rhs) +{ + return !(lhs == rhs); +} + +//Enforce template instantiations. Just as for tag_primitive we have to instantiate operator!= +template class tag_array; +template class tag_array; +template bool operator!= (const tag_array& , const tag_array&); +template bool operator!=(const tag_array&, const tag_array&); + +} -- cgit 0.0.5-2-1-g0f52 From f5fc52e27b54a47ee86edfe9ba002426a4833d3d Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Fri, 17 Jul 2015 19:49:21 +0200 Subject: Overload has_key with version that takes tag_type --- include/tag_compound.h | 2 ++ src/tag_compound.cpp | 6 ++++++ test/nbttest.cpp | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/include/tag_compound.h b/include/tag_compound.h index abf1df239a..da6b755ad5 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -100,6 +100,8 @@ public: ///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; diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index 78a5c46fc6..48f4f42239 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -72,6 +72,12 @@ bool tag_compound::has_key(const std::string& key) const return tags.find(key) != tags.end(); } +bool tag_compound::has_key(const std::string& key, tag_type type) const +{ + auto it = tags.find(key); + return it != tags.end() && it->second.get_type() == type; +} + size_t tag_compound::size() const { return tags.size(); diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 89181a73c3..14f10975fd 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -153,8 +153,14 @@ void test_tag_compound() ASSERT(comp.erase("nothing") == false); ASSERT(comp.has_key("quux")); + ASSERT(comp.has_key("quux", tag_type::Compound)); + ASSERT(!comp.has_key("quux", tag_type::List)); + ASSERT(!comp.has_key("quux", tag_type::Null)); + ASSERT(comp.erase("quux") == true); ASSERT(!comp.has_key("quux")); + ASSERT(!comp.has_key("quux", tag_type::Compound)); + ASSERT(!comp.has_key("quux", tag_type::Null)); comp.clear(); ASSERT(comp == tag_compound{}); -- cgit 0.0.5-2-1-g0f52 From 078982fed333362e152dd424fc6f120246112acd Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Fri, 17 Jul 2015 21:44:46 +0200 Subject: Add and change some tests --- test/nbttest.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 14f10975fd..66d82be16a 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -96,7 +96,7 @@ void test_tag_compound() {"foo", int16_t(12)}, {"bar", "baz"}, {"baz", -2.0}, - {"list", tag_list::of({16, 17})} + {"list", tag_list{16, 17}} }; ASSERT(comp["foo"].get_type() == tag_type::Short); @@ -125,7 +125,7 @@ void test_tag_compound() ASSERT(comp.at("quux").get_type() == tag_type::Compound); ASSERT(std::string(comp["quux"].at("Hello")) == "World"); ASSERT(std::string(comp["quux"]["Hello"]) == "World"); - ASSERT(comp["list"][1] == tag_byte(17)); + ASSERT(comp["list"][1] == tag_int(17)); EXPECT_EXCEPTION(comp.at("nothing"), std::out_of_range); @@ -134,11 +134,12 @@ void test_tag_compound() {"bar", "barbaz"}, {"baz", -2.0}, {"quux", tag_compound{{"Hello", "World"}, {"zero", 0}}}, - {"list", tag_list::of({16, 17})} + {"list", tag_list{16, 17}} }; ASSERT(comp == comp2); - ASSERT(comp != (const tag_compound&)comp2["quux"]); + ASSERT(comp != dynamic_cast(comp2["quux"].get())); ASSERT(comp != comp2["quux"]); + ASSERT(dynamic_cast(comp["quux"].get()) == comp2["quux"]); ASSERT(comp2.size() == 5); const char* keys[] = {"bar", "baz", "foo", "list", "quux"}; //alphabetic order @@ -192,6 +193,12 @@ void test_value() ASSERT(val2 == val3); ASSERT(val3 == val3); + value valstr(tag_string("foo")); + ASSERT(std::string(valstr) == "foo"); + valstr = "bar"; + EXPECT_EXCEPTION(valstr = 5, std::bad_cast); + ASSERT(std::string(valstr) == "bar"); + val1 = int64_t(42); ASSERT(val2 != val1); EXPECT_EXCEPTION(val2 = int64_t(12), std::bad_cast); -- cgit 0.0.5-2-1-g0f52 From 26b61743aecdaf4ae81c15f8efa26e4ae3726673 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Fri, 17 Jul 2015 23:31:36 +0200 Subject: Add missing source files to CMakeLists --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8474392677..584f20d7eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,10 @@ add_definitions(-std=c++11) include_directories(include) add_library(nbt++ STATIC src/tag.cpp + src/tag_array.cpp src/tag_compound.cpp + src/tag_list.cpp + src/tag_primitive.cpp src/tag_string.cpp src/value.cpp src/value_initializer.cpp) -- cgit 0.0.5-2-1-g0f52 From 3bab56d8f1e59d921ac7857e6747c7ecbab045f7 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sat, 18 Jul 2015 21:20:44 +0200 Subject: Change some parameters to value_initializer --- include/tag_compound.h | 4 ++-- include/tag_list.h | 6 +++--- src/tag_compound.cpp | 4 ++-- src/tag_list.cpp | 4 ++-- test/nbttest.cpp | 17 +++++++++-------- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/include/tag_compound.h b/include/tag_compound.h index da6b755ad5..1f1c5f3e1a 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -71,14 +71,14 @@ public: * @return a pair of the iterator to the value and a bool indicating * whether the key did not exist */ - std::pair put(const std::string& key, value&& val); + std::pair 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 insert(const std::string& key, value&& val); + std::pair insert(const std::string& key, value_initializer&& val); /** * @brief Constructs and assigns or inserts a tag into the compound diff --git a/include/tag_list.h b/include/tag_list.h index a84e010f04..137b53d5d1 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -22,7 +22,7 @@ #include "crtp_tag.h" #include "tagfwd.h" -#include "value.h" +#include "value_initializer.h" #include #include @@ -101,14 +101,14 @@ public: * content type * @throw std::out_of_range if the index is out of range */ - void set(size_t i, value&& val); + void set(size_t i, value_initializer&& val); /** * @brief Appends the tag to the end of the list * @throw std::bad_cast if the type of the tag does not match the list's * content type */ - void push_back(value&& val); + void push_back(value_initializer&& val); /** * @brief Constructs and appends a tag to the end of the list diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index 48f4f42239..d3eaf06440 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -43,7 +43,7 @@ value& tag_compound::operator[](const std::string& key) return tags[key]; } -std::pair tag_compound::put(const std::string& key, value&& val) +std::pair tag_compound::put(const std::string& key, value_initializer&& val) { auto it = tags.find(key); if(it != tags.end()) @@ -57,7 +57,7 @@ std::pair tag_compound::put(const std::string& key } } -std::pair tag_compound::insert(const std::string& key, value&& val) +std::pair tag_compound::insert(const std::string& key, value_initializer&& val) { return tags.emplace(key, std::move(val)); } diff --git a/src/tag_list.cpp b/src/tag_list.cpp index b483819405..1d930b4f92 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -79,14 +79,14 @@ const value& tag_list::operator[](size_t i) const return tags[i]; } -void tag_list::set(size_t i, value&& val) +void tag_list::set(size_t i, value_initializer&& val) { if(val.get_type() != el_type_) throw std::bad_cast(); tags.at(i) = std::move(val); } -void tag_list::push_back(value&& val) +void tag_list::push_back(value_initializer&& val) { if(!val) //don't allow null values throw std::bad_cast(); diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 66d82be16a..3e1f040529 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -102,6 +102,7 @@ void test_tag_compound() ASSERT(comp["foo"].get_type() == tag_type::Short); ASSERT(int32_t(comp["foo"]) == 12); ASSERT(int16_t(comp.at("foo")) == int16_t(12)); + ASSERT(comp["foo"] == tag_short(12)); EXPECT_EXCEPTION(int8_t(comp["foo"]), std::bad_cast); EXPECT_EXCEPTION(std::string(comp["foo"]), std::bad_cast); @@ -166,10 +167,10 @@ void test_tag_compound() comp.clear(); ASSERT(comp == tag_compound{}); - ASSERT(comp.put("abc", value(tag_double(6.0))).second == true); - ASSERT(comp.put("abc", value(tag_long(-28))).second == false); - ASSERT(comp.insert("ghi", value(tag_string("world"))).second == true); - ASSERT(comp.insert("abc", value(tag_string("hello"))).second == false); + ASSERT(comp.put("abc", tag_double(6.0)).second == true); + ASSERT(comp.put("abc", tag_long(-28)).second == false); + ASSERT(comp.insert("ghi", tag_string("world")).second == true); + ASSERT(comp.insert("abc", tag_string("hello")).second == false); ASSERT(comp.emplace("def", "ghi").second == true); ASSERT(comp.emplace("def", 4).second == false); ASSERT((comp == tag_compound{ @@ -250,8 +251,8 @@ void test_tag_list() list.emplace_back("foo"); ASSERT(list.el_type() == tag_type::String); - list.push_back(value(tag_string("bar"))); - EXPECT_EXCEPTION(list.push_back(value(tag_int(42))), std::bad_cast); + list.push_back(tag_string("bar")); + EXPECT_EXCEPTION(list.push_back(tag_int(42)), std::bad_cast); EXPECT_EXCEPTION(list.emplace_back(), std::bad_cast); ASSERT((list == tag_list{"foo", "bar"})); @@ -262,7 +263,7 @@ void test_tag_list() EXPECT_EXCEPTION(list.at(2), std::out_of_range); EXPECT_EXCEPTION(list.at(-1), std::out_of_range); - list.set(1, value(tag_string("baz"))); + list.set(1, tag_string("baz")); ASSERT(std::string(list[1]) == "baz"); ASSERT(list.size() == 2); @@ -278,7 +279,7 @@ void test_tag_list() list.clear(); ASSERT(list.size() == 0); - EXPECT_EXCEPTION(list.push_back(value(tag_short(25))), std::bad_cast); + EXPECT_EXCEPTION(list.push_back(tag_short(25)), std::bad_cast); EXPECT_EXCEPTION(list.push_back(value(nullptr)), std::bad_cast); ASSERT(tag_list() == tag_list(tag_type::Int)); -- cgit 0.0.5-2-1-g0f52 From 41b3ee4c93182d10d717ab1d7e40c33034a53f7a Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sat, 18 Jul 2015 21:27:19 +0200 Subject: Add tests for tag::get_ptr and set_ptr --- test/nbttest.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 3e1f040529..89ecdf8f85 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -202,10 +202,14 @@ void test_value() val1 = int64_t(42); ASSERT(val2 != val1); + EXPECT_EXCEPTION(val2 = int64_t(12), std::bad_cast); ASSERT(int64_t(val2) == 42); + tag_int* ptr = dynamic_cast(val2.get_ptr().get()); + ASSERT(*ptr == 42); val2 = 52; ASSERT(int32_t(val2) == 52); + ASSERT(*ptr == 52); EXPECT_EXCEPTION(val1["foo"], std::bad_cast); EXPECT_EXCEPTION(val1.at("foo"), std::bad_cast); @@ -240,6 +244,9 @@ void test_value() val2 = val2; ASSERT(!val1); ASSERT(val2 == tag_int(21)); + + val2.set_ptr(make_unique("foo")); + ASSERT(val2 == tag_string("foo")); std::clog << "test_value passed" << std::endl; } -- cgit 0.0.5-2-1-g0f52 From c28d3777bf5ee435e313e700bde2c0c84562b369 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sat, 18 Jul 2015 21:50:41 +0200 Subject: Add value::as method for conversion Make conversion operators explicit --- include/value.h | 35 ++++++++++++++++++++++++++++------- test/nbttest.cpp | 5 ++++- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/include/value.h b/include/value.h index a03395c2d1..c82aeb9097 100644 --- a/include/value.h +++ b/include/value.h @@ -66,6 +66,15 @@ public: tag& get(); const tag& get() const; + /** + * @brief Returns the contained tag as an instance of T + * @throw std::bad_cast if the tag is not of type T + */ + template + T& as(); + template + const T& as() const; + //Assignment of primitives and string /** * @brief Assigns the given value to the tag if the type is compatible @@ -92,12 +101,12 @@ public: * @throw std::bad_cast if the tag type is not convertible to the desired * type via a widening conversion */ - operator int8_t() const; - operator int16_t() const; - operator int32_t() const; - operator int64_t() const; - operator float() const; - operator double() const; + 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 @@ -105,7 +114,7 @@ public: * If the value is uninitialized, the behavior is undefined. * @throw std::bad_cast if the tag type is not tag_string */ - operator const std::string&() const; + explicit operator const std::string&() const; ///Returns true if the value is not uninitialized explicit operator bool() const; @@ -167,6 +176,18 @@ private: std::unique_ptr tag_; }; +template +T& value::as() +{ + return dynamic_cast(get()); +} + +template +const T& value::as() const +{ + return dynamic_cast(get()); +} + } #endif // TAG_REF_PROXY_H_INCLUDED diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 89ecdf8f85..ac9e5d5a30 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -199,6 +199,9 @@ void test_value() valstr = "bar"; EXPECT_EXCEPTION(valstr = 5, std::bad_cast); ASSERT(std::string(valstr) == "bar"); + ASSERT(valstr.as() == "bar"); + ASSERT(&valstr.as() == &valstr.get()); + EXPECT_EXCEPTION(valstr.as(), std::bad_cast); val1 = int64_t(42); ASSERT(val2 != val1); @@ -227,7 +230,7 @@ void test_value() tag = 21; ASSERT(int32_t(val3) == 21); val1.set_ptr(std::move(val3.get_ptr())); - ASSERT(tag_int(val1) == 21); + ASSERT(val1.as() == 21); ASSERT(val1.get_type() == tag_type::Int); ASSERT(val2.get_type() == tag_type::Null); -- cgit 0.0.5-2-1-g0f52 From 61d29e8ac2efc53ca502645a9199f9377832404d Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sat, 18 Jul 2015 21:57:58 +0200 Subject: Add static_assert to ensure correct types for as --- include/value.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/value.h b/include/value.h index c82aeb9097..2dc4e10207 100644 --- a/include/value.h +++ b/include/value.h @@ -22,6 +22,7 @@ #include "tag.h" #include +#include namespace nbt { @@ -179,12 +180,14 @@ private: template T& value::as() { + static_assert(std::is_base_of::value, "T must be a subclass of tag"); return dynamic_cast(get()); } template const T& value::as() const { + static_assert(std::is_base_of::value, "T must be a subclass of tag"); return dynamic_cast(get()); } -- cgit 0.0.5-2-1-g0f52 From d003eceb72357a9c7ba0acb9fbb227de1f56b125 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sat, 18 Jul 2015 23:50:08 +0200 Subject: Elaborate on use and rationale of value and value_initializer --- include/value.h | 30 ++++++++++++++++++++++++++++-- include/value_initializer.h | 15 +++++++++++++-- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/include/value.h b/include/value.h index 2dc4e10207..670fc3fe8e 100644 --- a/include/value.h +++ b/include/value.h @@ -30,8 +30,34 @@ namespace nbt /** * @brief Contains an NBT value of fixed type * - * A convenience wrapper around @c std::unique_ptr, contains a tag of - * fixed type. + * This class is a convenience wrapper for @c std::unique_ptr. + * 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. + * Once the value contains a tag, the type of the contained tag will not change + * unless set_ptr is used. + * + * 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 + * 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 + * compound["foo"] = 42. 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 value { diff --git a/include/value_initializer.h b/include/value_initializer.h index f50527546d..4ba07cb8c1 100644 --- a/include/value_initializer.h +++ b/include/value_initializer.h @@ -27,8 +27,19 @@ namespace nbt /** * @brief Helper class for implicitly constructing value objects - * @note Instances of this class can unproblematically be "sliced" (converted) - * into @ref 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 value_initializer : public value { -- cgit 0.0.5-2-1-g0f52 From 35041b1d15c0068779e090f6b094a98d92f232d2 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 21 Jul 2015 15:01:59 +0200 Subject: Replace function-style casts with static_cast --- test/nbttest.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index ac9e5d5a30..fdc6d5e3f0 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -100,27 +100,27 @@ void test_tag_compound() }; ASSERT(comp["foo"].get_type() == tag_type::Short); - ASSERT(int32_t(comp["foo"]) == 12); - ASSERT(int16_t(comp.at("foo")) == int16_t(12)); + ASSERT(static_cast(comp["foo"]) == 12); + ASSERT(static_cast(comp.at("foo")) == int16_t(12)); ASSERT(comp["foo"] == tag_short(12)); - EXPECT_EXCEPTION(int8_t(comp["foo"]), std::bad_cast); - EXPECT_EXCEPTION(std::string(comp["foo"]), std::bad_cast); + EXPECT_EXCEPTION(static_cast(comp["foo"]), std::bad_cast); + EXPECT_EXCEPTION(static_cast(comp["foo"]), std::bad_cast); EXPECT_EXCEPTION(comp["foo"] = 32, std::bad_cast); comp["foo"] = int8_t(32); - ASSERT(int16_t(comp["foo"]) == 32); + ASSERT(static_cast(comp["foo"]) == 32); ASSERT(comp["bar"].get_type() == tag_type::String); - ASSERT(std::string(comp["bar"]) == "baz"); - EXPECT_EXCEPTION(int(comp["bar"]), std::bad_cast); + ASSERT(static_cast(comp["bar"]) == "baz"); + EXPECT_EXCEPTION(static_cast(comp["bar"]), std::bad_cast); EXPECT_EXCEPTION(comp["bar"] = -128, std::bad_cast); comp["bar"] = "barbaz"; - ASSERT(std::string(comp["bar"]) == "barbaz"); + ASSERT(static_cast(comp["bar"]) == "barbaz"); ASSERT(comp["baz"].get_type() == tag_type::Double); - ASSERT(double(comp["baz"]) == -2.0); - EXPECT_EXCEPTION(float(comp["baz"]), std::bad_cast); + ASSERT(static_cast(comp["baz"]) == -2.0); + EXPECT_EXCEPTION(static_cast(comp["baz"]), std::bad_cast); comp["quux"] = tag_compound{{"Hello", "World"}, {"zero", 0}}; ASSERT(comp.at("quux").get_type() == tag_type::Compound); @@ -195,10 +195,10 @@ void test_value() ASSERT(val3 == val3); value valstr(tag_string("foo")); - ASSERT(std::string(valstr) == "foo"); + ASSERT(static_cast(valstr) == "foo"); valstr = "bar"; EXPECT_EXCEPTION(valstr = 5, std::bad_cast); - ASSERT(std::string(valstr) == "bar"); + ASSERT(static_cast(valstr) == "bar"); ASSERT(valstr.as() == "bar"); ASSERT(&valstr.as() == &valstr.get()); EXPECT_EXCEPTION(valstr.as(), std::bad_cast); @@ -207,11 +207,11 @@ void test_value() ASSERT(val2 != val1); EXPECT_EXCEPTION(val2 = int64_t(12), std::bad_cast); - ASSERT(int64_t(val2) == 42); + ASSERT(static_cast(val2) == 42); tag_int* ptr = dynamic_cast(val2.get_ptr().get()); ASSERT(*ptr == 42); val2 = 52; - ASSERT(int32_t(val2) == 52); + ASSERT(static_cast(val2) == 52); ASSERT(*ptr == 52); EXPECT_EXCEPTION(val1["foo"], std::bad_cast); @@ -228,7 +228,7 @@ void test_value() tag_int& tag = dynamic_cast(val3.get()); ASSERT(tag == tag_int(52)); tag = 21; - ASSERT(int32_t(val3) == 21); + ASSERT(static_cast(val3) == 21); val1.set_ptr(std::move(val3.get_ptr())); ASSERT(val1.as() == 21); @@ -267,14 +267,14 @@ void test_tag_list() ASSERT((list == tag_list{"foo", "bar"})); ASSERT(list[0] == tag_string("foo")); - ASSERT(std::string(list.at(1)) == "bar"); + ASSERT(static_cast(list.at(1)) == "bar"); ASSERT(list.size() == 2); EXPECT_EXCEPTION(list.at(2), std::out_of_range); EXPECT_EXCEPTION(list.at(-1), std::out_of_range); list.set(1, tag_string("baz")); - ASSERT(std::string(list[1]) == "baz"); + ASSERT(static_cast(list[1]) == "baz"); ASSERT(list.size() == 2); tag_string values[] = {"foo", "baz"}; -- cgit 0.0.5-2-1-g0f52 From 473f094be33ccd92c003ec25c9a9f940629ecdc9 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 22 Jul 2015 15:20:32 +0200 Subject: Add operator<< for tag_type --- include/tag.h | 5 ++++- src/tag.cpp | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/include/tag.h b/include/tag.h index b972a16da6..63125f1dc5 100644 --- a/include/tag.h +++ b/include/tag.h @@ -21,6 +21,7 @@ #define TAG_H_INCLUDED #include +#include #include namespace nbt @@ -41,7 +42,7 @@ enum class tag_type : int8_t List = 9, Compound = 10, Int_Array = 11, - Null = -1 + Null = -1 ///< Used to denote empty @ref value s }; ///Base class for all NBT tag classes @@ -75,6 +76,8 @@ private: virtual bool equals(const tag& rhs) const = 0; }; +std::ostream& operator<<(std::ostream& os, tag_type tt); + } #endif // TAG_H_INCLUDED diff --git a/src/tag.cpp b/src/tag.cpp index da5c3fa68e..76c850f3c8 100644 --- a/src/tag.cpp +++ b/src/tag.cpp @@ -18,6 +18,7 @@ * along with libnbt++. If not, see . */ #include "tag.h" +#include #include namespace nbt @@ -40,4 +41,25 @@ bool operator!=(const tag& lhs, const tag& rhs) return !(lhs == rhs); } +std::ostream& operator<<(std::ostream& os, tag_type tt) +{ + switch(tt) + { + case tag_type::End: return os << "end"; + case tag_type::Byte: return os << "byte"; + case tag_type::Short: return os << "short"; + case tag_type::Int: return os << "int"; + case tag_type::Long: return os << "long"; + case tag_type::Float: return os << "float"; + case tag_type::Double: return os << "double"; + case tag_type::Byte_Array: return os << "byte_array"; + case tag_type::List: return os << "list"; + case tag_type::Compound: return os << "compound"; + case tag_type::Int_Array: return os << "int_array"; + case tag_type::Null: return os << "null"; + + default: return os << "invalid"; + } +} + } -- cgit 0.0.5-2-1-g0f52 From 3a99fb629cf059061d2aba9aab3a18d9ebe5703c Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 22 Jul 2015 16:55:58 +0200 Subject: Add a few comments --- include/tag_primitive.h | 3 +++ include/tag_string.h | 3 +++ include/value.h | 5 +++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/tag_primitive.h b/include/tag_primitive.h index fd8de3906e..93c7936d99 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -41,12 +41,15 @@ public: ///The type of the tag static constexpr tag_type type = detail::get_primitive_type::value; + //Constructor tag_primitive(T value = 0); + //Getters operator T&(); operator T() const; T get() const; + //Setters tag_primitive& operator=(T value); void set(T value); diff --git a/include/tag_string.h b/include/tag_string.h index 846650e507..e28d18cd88 100644 --- a/include/tag_string.h +++ b/include/tag_string.h @@ -33,14 +33,17 @@ public: ///The type of the tag static constexpr tag_type type = tag_type::String; + //Constructors tag_string(const std::string& str); tag_string(std::string&& str = ""); tag_string(const char* str); + //Getters operator std::string&(); operator const std::string&() const; const std::string& get() const; + //Setters tag_string& operator=(const std::string& str); tag_string& operator=(std::string&& str); tag_string& operator=(const char* str); diff --git a/include/value.h b/include/value.h index 670fc3fe8e..7c9511d74d 100644 --- a/include/value.h +++ b/include/value.h @@ -34,8 +34,6 @@ namespace nbt * 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. - * Once the value contains a tag, the type of the contained tag will not change - * unless set_ptr is used. * * 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 @@ -62,6 +60,7 @@ namespace nbt class value { public: + //Constructors value() {} explicit value(std::unique_ptr&& t); explicit value(tag&& t); @@ -189,8 +188,10 @@ public: value& operator[](size_t i); const value& operator[](size_t i) const; + ///Returns a reference to the underlying std::unique_ptr std::unique_ptr& get_ptr(); const std::unique_ptr& get_ptr() const; + ///Resets the underlying std::unique_ptr to a different value void set_ptr(std::unique_ptr&& t); ///@sa tag::get_type -- cgit 0.0.5-2-1-g0f52 From db169073f195efcb9b35df5c314c65ba39705cef Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 22 Jul 2015 17:21:54 +0200 Subject: Change how emplacing is being done to avoid unnecessary move_clones on temporaries --- include/tag_compound.h | 2 +- include/tag_list.h | 4 ++-- test/nbttest.cpp | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/tag_compound.h b/include/tag_compound.h index 1f1c5f3e1a..8e09ff4834 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -127,7 +127,7 @@ private: template std::pair tag_compound::emplace(const std::string& key, Args&&... args) { - return put(key, value(T(std::forward(args)...))); + return put(key, value(make_unique(std::forward(args)...))); } } diff --git a/include/tag_list.h b/include/tag_list.h index 137b53d5d1..f1f5e6dec1 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -175,7 +175,7 @@ void tag_list::emplace_back(Args&&... args) el_type_ = T::type; else if(el_type_ != T::type) throw std::bad_cast(); - tags.emplace_back(T(std::forward(args)...)); + tags.emplace_back(make_unique(std::forward(args)...)); } template @@ -184,7 +184,7 @@ void tag_list::init(std::initializer_list init) el_type_ = T::type; tags.reserve(init.size()); for(const Arg& arg: init) - tags.emplace_back(T(arg)); + tags.emplace_back(make_unique(arg)); } } diff --git a/test/nbttest.cpp b/test/nbttest.cpp index fdc6d5e3f0..7e3a78b341 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -274,6 +274,8 @@ void test_tag_list() EXPECT_EXCEPTION(list.at(-1), std::out_of_range); list.set(1, tag_string("baz")); + EXPECT_EXCEPTION(list.set(1, value(nullptr)), std::bad_cast); + EXPECT_EXCEPTION(list.set(1, -42), std::bad_cast); ASSERT(static_cast(list[1]) == "baz"); ASSERT(list.size() == 2); -- cgit 0.0.5-2-1-g0f52 From 2054a73eeff2a5a931d466b6722215dcf9666b2a Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 22 Jul 2015 20:57:58 +0200 Subject: Add some missing tests for value --- test/nbttest.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 7e3a78b341..ee4b77e321 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -248,6 +248,10 @@ void test_value() ASSERT(!val1); ASSERT(val2 == tag_int(21)); + val3 = tag_short(2); + EXPECT_EXCEPTION(val3 = tag_string("foo"), std::bad_cast); + ASSERT(val3.get() == tag_short(2)); + val2.set_ptr(make_unique("foo")); ASSERT(val2 == tag_string("foo")); std::clog << "test_value passed" << std::endl; -- cgit 0.0.5-2-1-g0f52 From a8991a4b5e8854ae4ffa48b40ccf66358f462704 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 22 Jul 2015 21:21:45 +0200 Subject: Add missing switch case --- src/tag.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tag.cpp b/src/tag.cpp index 76c850f3c8..fbed9fb6e1 100644 --- a/src/tag.cpp +++ b/src/tag.cpp @@ -53,6 +53,7 @@ std::ostream& operator<<(std::ostream& os, tag_type tt) case tag_type::Float: return os << "float"; case tag_type::Double: return os << "double"; case tag_type::Byte_Array: return os << "byte_array"; + case tag_type::String: return os << "string"; case tag_type::List: return os << "list"; case tag_type::Compound: return os << "compound"; case tag_type::Int_Array: return os << "int_array"; -- cgit 0.0.5-2-1-g0f52 From e969e62e5c724efc995ea55d0c9238631ab4377b Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 22 Jul 2015 21:52:14 +0200 Subject: Fix missing cast --- include/crtp_tag.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/crtp_tag.h b/include/crtp_tag.h index f227fec9cf..94730887d5 100644 --- a/include/crtp_tag.h +++ b/include/crtp_tag.h @@ -71,7 +71,7 @@ namespace detail template tag& crtp_tag::assign(tag&& rhs) { - return *this = dynamic_cast(rhs); + return static_cast(*this) = dynamic_cast(rhs); } template -- cgit 0.0.5-2-1-g0f52 From a4c22fa02f779c28109e4617ba64ffbd760abcea Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 22 Jul 2015 22:10:54 +0200 Subject: Change argument of list::set to value --- include/tag_list.h | 6 +++--- src/tag_list.cpp | 2 +- test/nbttest.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/tag_list.h b/include/tag_list.h index f1f5e6dec1..f44f4ba66e 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -96,12 +96,12 @@ public: const value& operator[](size_t i) const; /** - * @brief Assigns a tag at the given index - * @throw std::bad_cast if the type of the tag does not match the list's + * @brief Assigns a value at the given index + * @throw std::bad_cast 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_initializer&& val); + void set(size_t i, value&& val); /** * @brief Appends the tag to the end of the list diff --git a/src/tag_list.cpp b/src/tag_list.cpp index 1d930b4f92..e23f162eb2 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -79,7 +79,7 @@ const value& tag_list::operator[](size_t i) const return tags[i]; } -void tag_list::set(size_t i, value_initializer&& val) +void tag_list::set(size_t i, value&& val) { if(val.get_type() != el_type_) throw std::bad_cast(); diff --git a/test/nbttest.cpp b/test/nbttest.cpp index ee4b77e321..dc29d1a6ed 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -277,9 +277,9 @@ void test_tag_list() EXPECT_EXCEPTION(list.at(2), std::out_of_range); EXPECT_EXCEPTION(list.at(-1), std::out_of_range); - list.set(1, tag_string("baz")); + list.set(1, value(tag_string("baz"))); EXPECT_EXCEPTION(list.set(1, value(nullptr)), std::bad_cast); - EXPECT_EXCEPTION(list.set(1, -42), std::bad_cast); + EXPECT_EXCEPTION(list.set(1, value(tag_int(-42))), std::bad_cast); ASSERT(static_cast(list[1]) == "baz"); ASSERT(list.size() == 2); -- cgit 0.0.5-2-1-g0f52 From 3a8f3ec2a07a518c2ea8422eceefba2544397500 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 22 Jul 2015 23:55:51 +0200 Subject: Specify undefined behavior if list is broken --- include/tag_list.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/tag_list.h b/include/tag_list.h index f44f4ba66e..87b2742e92 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -29,7 +29,12 @@ namespace nbt { -///Tag that contains multiple unnamed tags of the same type +/** + * @brief Tag that contains multiple unnamed tags of the same type + * + * The list's behavior is undefined if the contained values are changed in a + * way that their type differs from the list's content type. + */ class tag_list final : public detail::crtp_tag { public: -- cgit 0.0.5-2-1-g0f52 From bfea511ea876c5186d98e335c942d0e8389b7eb9 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 23 Jul 2015 11:06:42 +0200 Subject: Add noexcept at some important places --- include/crtp_tag.h | 4 ++-- include/tag_array.h | 2 +- include/tag_primitive.h | 2 +- include/value.h | 8 ++++---- include/value_initializer.h | 4 ++-- src/tag_array.cpp | 2 +- src/tag_primitive.cpp | 2 +- src/value.cpp | 2 +- src/value_initializer.cpp | 22 +++++++++++----------- 9 files changed, 24 insertions(+), 24 deletions(-) diff --git a/include/crtp_tag.h b/include/crtp_tag.h index 94730887d5..25f87bf6e8 100644 --- a/include/crtp_tag.h +++ b/include/crtp_tag.h @@ -34,7 +34,7 @@ namespace detail { public: //Pure virtual destructor to make the class abstract - virtual ~crtp_tag() = 0; + virtual ~crtp_tag() noexcept = 0; tag_type get_type() const noexcept override final; @@ -48,7 +48,7 @@ namespace detail }; template - crtp_tag::~crtp_tag() {} + crtp_tag::~crtp_tag() noexcept {} template tag_type crtp_tag::get_type() const noexcept diff --git a/include/tag_array.h b/include/tag_array.h index 7aca60722b..9a79489c56 100644 --- a/include/tag_array.h +++ b/include/tag_array.h @@ -63,7 +63,7 @@ public: ///Constructs an array with the given values tag_array(std::initializer_list init); - tag_array(std::vector&& vec); + tag_array(std::vector&& vec) noexcept; ///Returns a reference to the vector that contains the values std::vector& get(); diff --git a/include/tag_primitive.h b/include/tag_primitive.h index 93c7936d99..ac41568920 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -42,7 +42,7 @@ public: static constexpr tag_type type = detail::get_primitive_type::value; //Constructor - tag_primitive(T value = 0); + tag_primitive(T value = 0) noexcept; //Getters operator T&(); diff --git a/include/value.h b/include/value.h index 7c9511d74d..c017bcbd27 100644 --- a/include/value.h +++ b/include/value.h @@ -61,13 +61,13 @@ class value { public: //Constructors - value() {} - explicit value(std::unique_ptr&& t); + value() noexcept {} + explicit value(std::unique_ptr&& t) noexcept; explicit value(tag&& t); //Moving - value(value&&) = default; - value& operator=(value&&) = default; + value(value&&) noexcept = default; + value& operator=(value&&) noexcept = default; //Copying explicit value(const value& rhs); diff --git a/include/value_initializer.h b/include/value_initializer.h index 4ba07cb8c1..42cf77da77 100644 --- a/include/value_initializer.h +++ b/include/value_initializer.h @@ -44,8 +44,8 @@ namespace nbt class value_initializer : public value { public: - value_initializer(std::unique_ptr&& t); - value_initializer(value&& val); + value_initializer(std::unique_ptr&& t) noexcept; + value_initializer(value&& val) noexcept; value_initializer(tag&& t); value_initializer(int8_t val); diff --git a/src/tag_array.cpp b/src/tag_array.cpp index 0db749d1f6..942d1f5c78 100644 --- a/src/tag_array.cpp +++ b/src/tag_array.cpp @@ -28,7 +28,7 @@ tag_array::tag_array(std::initializer_list init): {} template -tag_array::tag_array(std::vector&& vec): +tag_array::tag_array(std::vector&& vec) noexcept: data(std::move(vec)) {} diff --git a/src/tag_primitive.cpp b/src/tag_primitive.cpp index 7f71098bd6..27aabcfed5 100644 --- a/src/tag_primitive.cpp +++ b/src/tag_primitive.cpp @@ -27,7 +27,7 @@ static_assert(std::numeric_limits::is_iec559 && std::numeric_limits -tag_primitive::tag_primitive(T val): +tag_primitive::tag_primitive(T val) noexcept: value(val) {} diff --git a/src/value.cpp b/src/value.cpp index fa6018dd5d..bba76b0c8b 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -24,7 +24,7 @@ namespace nbt { -value::value(std::unique_ptr&& t): +value::value(std::unique_ptr&& t) noexcept: tag_(std::move(t)) {} diff --git a/src/value_initializer.cpp b/src/value_initializer.cpp index 4b039e441a..468b46e99c 100644 --- a/src/value_initializer.cpp +++ b/src/value_initializer.cpp @@ -23,18 +23,18 @@ namespace nbt { -value_initializer::value_initializer(std::unique_ptr&& t): value(std::move(t)) {} -value_initializer::value_initializer(value&& val): value(std::move(val)) {} -value_initializer::value_initializer(tag&& t): value(std::move(t)) {} +value_initializer::value_initializer(std::unique_ptr&& t) noexcept: value(std::move(t)) {} +value_initializer::value_initializer(value&& val) noexcept : value(std::move(val)) {} +value_initializer::value_initializer(tag&& t) : value(std::move(t)) {} -value_initializer::value_initializer(int8_t val): value(tag_byte(val)) {} -value_initializer::value_initializer(int16_t val): value(tag_short(val)) {} -value_initializer::value_initializer(int32_t val): value(tag_int(val)) {} -value_initializer::value_initializer(int64_t val): value(tag_long(val)) {} -value_initializer::value_initializer(float val): value(tag_float(val)) {} -value_initializer::value_initializer(double val): value(tag_double(val)) {} +value_initializer::value_initializer(int8_t val) : value(tag_byte(val)) {} +value_initializer::value_initializer(int16_t val) : value(tag_short(val)) {} +value_initializer::value_initializer(int32_t val) : value(tag_int(val)) {} +value_initializer::value_initializer(int64_t val) : value(tag_long(val)) {} +value_initializer::value_initializer(float val) : value(tag_float(val)) {} +value_initializer::value_initializer(double val) : value(tag_double(val)) {} value_initializer::value_initializer(const std::string& str): value(tag_string(str)) {} -value_initializer::value_initializer(std::string&& str): value(tag_string(std::move(str))) {} -value_initializer::value_initializer(const char* str): value(tag_string(str)) {} +value_initializer::value_initializer(std::string&& str) : value(tag_string(std::move(str))) {} +value_initializer::value_initializer(const char* str) : value(tag_string(str)) {} } -- cgit 0.0.5-2-1-g0f52 From a4af97e198bda6e2a73976124b32d6fe2b812d3a Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 23 Jul 2015 14:04:01 +0200 Subject: Change constructors of tag_string --- include/tag_string.h | 3 ++- src/tag_string.cpp | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/tag_string.h b/include/tag_string.h index e28d18cd88..0cb057125f 100644 --- a/include/tag_string.h +++ b/include/tag_string.h @@ -34,8 +34,9 @@ public: static constexpr tag_type type = tag_type::String; //Constructors + tag_string() {} tag_string(const std::string& str); - tag_string(std::string&& str = ""); + tag_string(std::string&& str) noexcept; tag_string(const char* str); //Getters diff --git a/src/tag_string.cpp b/src/tag_string.cpp index e4f714d0b3..58635d5e0a 100644 --- a/src/tag_string.cpp +++ b/src/tag_string.cpp @@ -26,12 +26,12 @@ tag_string::tag_string(const std::string& str): value(str) {} -tag_string::tag_string(std::string&& str): +tag_string::tag_string(std::string&& str) noexcept: value(std::move(str)) {} tag_string::tag_string(const char* str): - value(std::string(str)) + value(str) {} tag_string::operator std::string&() -- cgit 0.0.5-2-1-g0f52 From aeab493a5619b578bd0f2ea8e1e968a30bceb35f Mon Sep 17 00:00:00 2001 From: ljfa Date: Mon, 27 Jul 2015 12:17:08 +0200 Subject: Rename Requirements.md to PRD.md --- PRD.md | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Requirements.md | 60 --------------------------------------------------------- 2 files changed, 60 insertions(+), 60 deletions(-) create mode 100644 PRD.md delete mode 100644 Requirements.md diff --git a/PRD.md b/PRD.md new file mode 100644 index 0000000000..f7b46f1a8a --- /dev/null +++ b/PRD.md @@ -0,0 +1,60 @@ +# libnbt++2 Product Requirements Document + +### Purpose +Provide a C++ interface for working with NBT data, particularly originating +from Minecraft. + +### Scope +External Minecraft utilities that read or manipulate parts of savefiles, +such as: +- (Graphical) NBT editors +- Inventory editors +- Tools for reading or changing world metadata +- Map editors and visualizers + +### Definitions, Acronyms and Abbreviations +- **libnbt++1:** The predecessor of libnbt++2. +- **Minecraft:** A sandbox voxel world game written in Java, developed by + Mojang. +- **Mojang's implementation:** The NBT library written in Java that Mojang + uses in Minecraft. +- **NBT:** Named Binary Tag. A binary serialization format used by Minecraft. +- **Tag:** A data unit in NBT. Can be a number, string, array, list or + compound. + +### Product Functions +- /RF10/ Reading and writing NBT data files/streams with or without + compression. +- /RF20/ Representing NBT data in memory and allowing programs to read or + manipulate it in all the ways that Mojang's implementation and libnbt++1 + provide. +- /RF30/ A shorter syntax than in libnbt++1 and preferrably also Mojang's + implementation. +- /RF35/ Typesafe operations (no possibly unwanted implicit casts), in case + of incompatible types exceptions should be thrown. +- /RF40/ The need for insecure operations and manual memory management should + be minimized; references and `std::unique_ptr` should be preferred before + raw pointers. +- /RF55/ A wrapper for tags that provides syntactic sugar is preferred + before raw `std::unique_ptr` values. +- /RF50/ Move semantics are preferred before copy semantics. +- /RF55/ Copying tags should be possible, but only in an explicit manner. +- /RF60/ Checked conversions are preferred, unchecked conversions may be + possible but discouraged. + +### Product Performance +- /RP10/ All operations on (not too large) NBT data should not be slower + than their counterparts in Mojang's implementation. +- /RP20/ The library must be able to handle all possible NBT data that + Mojang's implementation can create and handle. +- /RP30/ Often used operations on large Lists, Compounds and Arrays must + be of at most O(log n) time complexity if reasonable. Other operations + should be at most O(n). + +### Quality Requirements +- Functionality: good +- Reliability: normal +- Usability: very good +- Efficiency: good +- Changeability: normal +- Transferability: normal diff --git a/Requirements.md b/Requirements.md deleted file mode 100644 index f7b46f1a8a..0000000000 --- a/Requirements.md +++ /dev/null @@ -1,60 +0,0 @@ -# libnbt++2 Product Requirements Document - -### Purpose -Provide a C++ interface for working with NBT data, particularly originating -from Minecraft. - -### Scope -External Minecraft utilities that read or manipulate parts of savefiles, -such as: -- (Graphical) NBT editors -- Inventory editors -- Tools for reading or changing world metadata -- Map editors and visualizers - -### Definitions, Acronyms and Abbreviations -- **libnbt++1:** The predecessor of libnbt++2. -- **Minecraft:** A sandbox voxel world game written in Java, developed by - Mojang. -- **Mojang's implementation:** The NBT library written in Java that Mojang - uses in Minecraft. -- **NBT:** Named Binary Tag. A binary serialization format used by Minecraft. -- **Tag:** A data unit in NBT. Can be a number, string, array, list or - compound. - -### Product Functions -- /RF10/ Reading and writing NBT data files/streams with or without - compression. -- /RF20/ Representing NBT data in memory and allowing programs to read or - manipulate it in all the ways that Mojang's implementation and libnbt++1 - provide. -- /RF30/ A shorter syntax than in libnbt++1 and preferrably also Mojang's - implementation. -- /RF35/ Typesafe operations (no possibly unwanted implicit casts), in case - of incompatible types exceptions should be thrown. -- /RF40/ The need for insecure operations and manual memory management should - be minimized; references and `std::unique_ptr` should be preferred before - raw pointers. -- /RF55/ A wrapper for tags that provides syntactic sugar is preferred - before raw `std::unique_ptr` values. -- /RF50/ Move semantics are preferred before copy semantics. -- /RF55/ Copying tags should be possible, but only in an explicit manner. -- /RF60/ Checked conversions are preferred, unchecked conversions may be - possible but discouraged. - -### Product Performance -- /RP10/ All operations on (not too large) NBT data should not be slower - than their counterparts in Mojang's implementation. -- /RP20/ The library must be able to handle all possible NBT data that - Mojang's implementation can create and handle. -- /RP30/ Often used operations on large Lists, Compounds and Arrays must - be of at most O(log n) time complexity if reasonable. Other operations - should be at most O(n). - -### Quality Requirements -- Functionality: good -- Reliability: normal -- Usability: very good -- Efficiency: good -- Changeability: normal -- Transferability: normal -- cgit 0.0.5-2-1-g0f52 From a5bf6ccebd957365a4eeaa761688d44bfedb0593 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 28 Jul 2015 17:22:09 +0200 Subject: Create endian_str.h and test --- include/endian_str.h | 83 ++++++++++++++++++++++++++++++++++++++++++++++++ test/CMakeLists.txt | 4 +++ test/endian_str_test.cpp | 80 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+) create mode 100644 include/endian_str.h create mode 100644 test/endian_str_test.cpp diff --git a/include/endian_str.h b/include/endian_str.h new file mode 100644 index 0000000000..f636773c12 --- /dev/null +++ b/include/endian_str.h @@ -0,0 +1,83 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#ifndef ENDIAN_STR_H_INCLUDED +#define ENDIAN_STR_H_INCLUDED + +#include +#include + +/** + * @brief Reading and writing numbers from and to streams + * in binary format with different byte orders. + */ +namespace endian +{ + +///Reads number from stream in little endian +void read_little(std::istream& str, uint8_t& x); +void read_little(std::istream& str, uint16_t& x); +void read_little(std::istream& str, uint32_t& x); +void read_little(std::istream& str, uint64_t& x); +void read_little(std::istream& str, int8_t& x); +void read_little(std::istream& str, int16_t& x); +void read_little(std::istream& str, int32_t& x); +void read_little(std::istream& str, int64_t& x); +void read_little(std::istream& str, float& x); +void read_little(std::istream& str, double& x); + +///Writes number to stream in little endian +void write_little(std::ostream& str, uint8_t x); +void write_little(std::ostream& str, uint16_t x); +void write_little(std::ostream& str, uint32_t x); +void write_little(std::ostream& str, uint64_t x); +void write_little(std::ostream& str, int8_t x); +void write_little(std::ostream& str, int16_t x); +void write_little(std::ostream& str, int32_t x); +void write_little(std::ostream& str, int64_t x); +void write_little(std::ostream& str, float x); +void write_little(std::ostream& str, double x); + +///Reads number from stream in big endian +void read_big(std::istream& str, uint8_t& x); +void read_big(std::istream& str, uint16_t& x); +void read_big(std::istream& str, uint32_t& x); +void read_big(std::istream& str, uint64_t& x); +void read_big(std::istream& str, int8_t& x); +void read_big(std::istream& str, int16_t& x); +void read_big(std::istream& str, int32_t& x); +void read_big(std::istream& str, int64_t& x); +void read_big(std::istream& str, float& x); +void read_big(std::istream& str, double& x); + +///Writes number to stream in big endian +void write_big(std::ostream& str, uint8_t x); +void write_big(std::ostream& str, uint16_t x); +void write_big(std::ostream& str, uint32_t x); +void write_big(std::ostream& str, uint64_t x); +void write_big(std::ostream& str, int8_t x); +void write_big(std::ostream& str, int16_t x); +void write_big(std::ostream& str, int32_t x); +void write_big(std::ostream& str, int64_t x); +void write_big(std::ostream& str, float x); +void write_big(std::ostream& str, double x); + +} + +#endif // ENDIAN_STR_H_INCLUDED diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 04672d2666..735ca2bb4c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -4,3 +4,7 @@ include_directories(${libnbt++_SOURCE_DIR}/include) add_executable(nbttest nbttest.cpp) target_link_libraries(nbttest nbt++) add_test(nbttest nbttest) + +add_executable(endian_str_test endian_str_test.cpp) +target_link_libraries(endian_str_test nbt++) +add_test(endian_str_test endian_str_test) diff --git a/test/endian_str_test.cpp b/test/endian_str_test.cpp new file mode 100644 index 0000000000..d9d01d15c5 --- /dev/null +++ b/test/endian_str_test.cpp @@ -0,0 +1,80 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include "microtest.h" +#include "endian_str.h" +#include + +using namespace endian; + +void test_uint() +{ + std::stringstream str(std::ios::in | std::ios::out | std::ios::binary); + + write_little(str, uint8_t (0x01)); + write_little(str, uint16_t(0x0102)); + write_little(str, uint32_t(0x01020304UL)); + write_little(str, uint64_t(0x0102030405060708ULL)); + + write_big (str, uint8_t (0x09)); + write_big (str, uint16_t(0x090A)); + write_big (str, uint32_t(0x090A0B0CUL)); + write_big (str, uint64_t(0x090A0B0C0D0E0F10ULL)); + + const char expected[] = { + 1, + 2, 1, + 4, 3, 2, 1, + 8, 7, 6, 5, 4, 3, 2, 1, + + 9, + 9, 10, + 9, 10, 11, 12, + 9, 10, 11, 12, 13, 14, 15, 16, + 0}; //Null terminator + ASSERT(str.str() == expected); + + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + + read_little(str, u8); + ASSERT(u8 == 0x01); + read_little(str, u16); + ASSERT(u16 == 0x0102); + read_little(str, u32); + ASSERT(u32 == 0x01020304UL); + read_little(str, u64); + ASSERT(u64 == 0x0102030405060708ULL); + + read_big(str, u8); + ASSERT(u8 == 0x09); + read_big(str, u16); + ASSERT(u16 == 0x090A); + read_big(str, u32); + ASSERT(u32 == 0x090A0B0CUL); + read_big(str, u64); + ASSERT(u64 == 0x090A0B0C0D0E0F10ULL); +} + +int main() +{ + test_uint(); +} -- cgit 0.0.5-2-1-g0f52 From 91cce39f2d04875b17b530a0994fe17a91f303ad Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 28 Jul 2015 17:48:03 +0200 Subject: Implement endian stream i/o for integers Reorder declarations in header --- CMakeLists.txt | 1 + include/endian_str.h | 84 ++++++++++----------- src/endian_str.cpp | 203 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 246 insertions(+), 42 deletions(-) create mode 100644 src/endian_str.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 584f20d7eb..d30f8a820c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ set(libnbt++_VERSION_MINOR 0) add_definitions(-std=c++11) include_directories(include) add_library(nbt++ STATIC + src/endian_str.cpp src/tag.cpp src/tag_array.cpp src/tag_compound.cpp diff --git a/include/endian_str.h b/include/endian_str.h index f636773c12..3ccd9817c7 100644 --- a/include/endian_str.h +++ b/include/endian_str.h @@ -31,52 +31,52 @@ namespace endian { ///Reads number from stream in little endian -void read_little(std::istream& str, uint8_t& x); -void read_little(std::istream& str, uint16_t& x); -void read_little(std::istream& str, uint32_t& x); -void read_little(std::istream& str, uint64_t& x); -void read_little(std::istream& str, int8_t& x); -void read_little(std::istream& str, int16_t& x); -void read_little(std::istream& str, int32_t& x); -void read_little(std::istream& str, int64_t& x); -void read_little(std::istream& str, float& x); -void read_little(std::istream& str, double& x); - -///Writes number to stream in little endian -void write_little(std::ostream& str, uint8_t x); -void write_little(std::ostream& str, uint16_t x); -void write_little(std::ostream& str, uint32_t x); -void write_little(std::ostream& str, uint64_t x); -void write_little(std::ostream& str, int8_t x); -void write_little(std::ostream& str, int16_t x); -void write_little(std::ostream& str, int32_t x); -void write_little(std::ostream& str, int64_t x); -void write_little(std::ostream& str, float x); -void write_little(std::ostream& str, double x); +void read_little(std::istream& is, uint8_t& x); +void read_little(std::istream& is, uint16_t& x); +void read_little(std::istream& is, uint32_t& x); +void read_little(std::istream& is, uint64_t& x); +void read_little(std::istream& is, int8_t& x); +void read_little(std::istream& is, int16_t& x); +void read_little(std::istream& is, int32_t& x); +void read_little(std::istream& is, int64_t& x); +void read_little(std::istream& is, float& x); +void read_little(std::istream& is, double& x); ///Reads number from stream in big endian -void read_big(std::istream& str, uint8_t& x); -void read_big(std::istream& str, uint16_t& x); -void read_big(std::istream& str, uint32_t& x); -void read_big(std::istream& str, uint64_t& x); -void read_big(std::istream& str, int8_t& x); -void read_big(std::istream& str, int16_t& x); -void read_big(std::istream& str, int32_t& x); -void read_big(std::istream& str, int64_t& x); -void read_big(std::istream& str, float& x); -void read_big(std::istream& str, double& x); +void read_big(std::istream& is, uint8_t& x); +void read_big(std::istream& is, uint16_t& x); +void read_big(std::istream& is, uint32_t& x); +void read_big(std::istream& is, uint64_t& x); +void read_big(std::istream& is, int8_t& x); +void read_big(std::istream& is, int16_t& x); +void read_big(std::istream& is, int32_t& x); +void read_big(std::istream& is, int64_t& x); +void read_big(std::istream& is, float& x); +void read_big(std::istream& is, double& x); + +///Writes number to stream in little endian +void write_little(std::ostream& os, uint8_t x); +void write_little(std::ostream& os, uint16_t x); +void write_little(std::ostream& os, uint32_t x); +void write_little(std::ostream& os, uint64_t x); +void write_little(std::ostream& os, int8_t x); +void write_little(std::ostream& os, int16_t x); +void write_little(std::ostream& os, int32_t x); +void write_little(std::ostream& os, int64_t x); +void write_little(std::ostream& os, float x); +void write_little(std::ostream& os, double x); ///Writes number to stream in big endian -void write_big(std::ostream& str, uint8_t x); -void write_big(std::ostream& str, uint16_t x); -void write_big(std::ostream& str, uint32_t x); -void write_big(std::ostream& str, uint64_t x); -void write_big(std::ostream& str, int8_t x); -void write_big(std::ostream& str, int16_t x); -void write_big(std::ostream& str, int32_t x); -void write_big(std::ostream& str, int64_t x); -void write_big(std::ostream& str, float x); -void write_big(std::ostream& str, double x); +void write_big(std::ostream& os, uint8_t x); +void write_big(std::ostream& os, uint16_t x); +void write_big(std::ostream& os, uint32_t x); +void write_big(std::ostream& os, uint64_t x); +void write_big(std::ostream& os, int8_t x); +void write_big(std::ostream& os, int16_t x); +void write_big(std::ostream& os, int32_t x); +void write_big(std::ostream& os, int64_t x); +void write_big(std::ostream& os, float x); +void write_big(std::ostream& os, double x); } diff --git a/src/endian_str.cpp b/src/endian_str.cpp new file mode 100644 index 0000000000..104a11312b --- /dev/null +++ b/src/endian_str.cpp @@ -0,0 +1,203 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include "endian_str.h" +#include +#include + +static_assert(CHAR_BIT == 8, "Assuming that a byte has 8 bits"); + +namespace endian +{ + +void read_little(std::istream& is, uint8_t& x) +{ + is.get(reinterpret_cast(x)); +} + +void read_little(std::istream& is, uint16_t& x) +{ + uint8_t tmp[2]; + is.read(reinterpret_cast(tmp), 2); + x = uint16_t(tmp[0]) + | (uint16_t(tmp[1]) << 8); +} + +void read_little(std::istream& is, uint32_t& x) +{ + uint8_t tmp[4]; + is.read(reinterpret_cast(tmp), 4); + x = uint32_t(tmp[0]) + | (uint32_t(tmp[1]) << 8) + | (uint32_t(tmp[2]) << 16) + | (uint32_t(tmp[3]) << 24); +} + +void read_little(std::istream& is, uint64_t& x) +{ + uint8_t tmp[8]; + is.read(reinterpret_cast(tmp), 8); + x = uint64_t(tmp[0]) + | (uint64_t(tmp[1]) << 8) + | (uint64_t(tmp[2]) << 16) + | (uint64_t(tmp[3]) << 24) + | (uint64_t(tmp[4]) << 32) + | (uint64_t(tmp[5]) << 40) + | (uint64_t(tmp[6]) << 48) + | (uint64_t(tmp[7]) << 56); +} + +void read_little(std::istream& is, int8_t & x) { read_little(is, reinterpret_cast(x)); } +void read_little(std::istream& is, int16_t& x) { read_little(is, reinterpret_cast(x)); } +void read_little(std::istream& is, int32_t& x) { read_little(is, reinterpret_cast(x)); } +void read_little(std::istream& is, int64_t& x) { read_little(is, reinterpret_cast(x)); } + +//------------------------------------------------------------------------------ + +void read_big(std::istream& is, uint8_t& x) +{ + is.read(reinterpret_cast(&x), 1); +} + +void read_big(std::istream& is, uint16_t& x) +{ + uint8_t tmp[2]; + is.read(reinterpret_cast(tmp), 2); + x = uint16_t(tmp[1]) + | (uint16_t(tmp[0]) << 8); +} + +void read_big(std::istream& is, uint32_t& x) +{ + uint8_t tmp[4]; + is.read(reinterpret_cast(tmp), 4); + x = uint32_t(tmp[3]) + | (uint32_t(tmp[2]) << 8) + | (uint32_t(tmp[1]) << 16) + | (uint32_t(tmp[0]) << 24); +} + +void read_big(std::istream& is, uint64_t& x) +{ + uint8_t tmp[8]; + is.read(reinterpret_cast(tmp), 8); + x = uint64_t(tmp[7]) + | (uint64_t(tmp[6]) << 8) + | (uint64_t(tmp[5]) << 16) + | (uint64_t(tmp[4]) << 24) + | (uint64_t(tmp[3]) << 32) + | (uint64_t(tmp[2]) << 40) + | (uint64_t(tmp[1]) << 48) + | (uint64_t(tmp[0]) << 56); +} + +void read_big(std::istream& is, int8_t & x) { read_big(is, reinterpret_cast(x)); } +void read_big(std::istream& is, int16_t& x) { read_big(is, reinterpret_cast(x)); } +void read_big(std::istream& is, int32_t& x) { read_big(is, reinterpret_cast(x)); } +void read_big(std::istream& is, int64_t& x) { read_big(is, reinterpret_cast(x)); } + +//------------------------------------------------------------------------------ + +void write_little(std::ostream& os, uint8_t x) +{ + os.put(x); +} + +void write_little(std::ostream& os, uint16_t x) +{ + uint8_t tmp[2] { + x & 0x00FF, + (x & 0xFF00) >> 8}; + os.write(reinterpret_cast(tmp), 2); +} + +void write_little(std::ostream& os, uint32_t x) +{ + uint8_t tmp[4] { + x & 0x000000FFUL, + (x & 0x0000FF00UL) >> 8, + (x & 0x00FF0000UL) >> 16, + (x & 0xFF000000UL) >> 24}; + os.write(reinterpret_cast(tmp), 4); +} + +void write_little(std::ostream& os, uint64_t x) +{ + uint8_t tmp[8] { + x & 0x00000000000000FFULL, + (x & 0x000000000000FF00ULL) >> 8, + (x & 0x0000000000FF0000ULL) >> 16, + (x & 0x00000000FF000000ULL) >> 24, + (x & 0x000000FF00000000ULL) >> 32, + (x & 0x0000FF0000000000ULL) >> 40, + (x & 0x00FF000000000000ULL) >> 48, + (x & 0xFF00000000000000ULL) >> 56}; + os.write(reinterpret_cast(tmp), 8); +} + +void write_little(std::ostream& os, int8_t x) { write_little(os, static_cast(x)); } +void write_little(std::ostream& os, int16_t x) { write_little(os, static_cast(x)); } +void write_little(std::ostream& os, int32_t x) { write_little(os, static_cast(x)); } +void write_little(std::ostream& os, int64_t x) { write_little(os, static_cast(x)); } + +//------------------------------------------------------------------------------ + +void write_big(std::ostream& os, uint8_t x) +{ + os.put(x); +} + +void write_big(std::ostream& os, uint16_t x) +{ + uint8_t tmp[2] { + (x & 0xFF00) >> 8, + x & 0x00FF}; + os.write(reinterpret_cast(tmp), 2); +} + +void write_big(std::ostream& os, uint32_t x) +{ + uint8_t tmp[4] { + (x & 0xFF000000UL) >> 24, + (x & 0x00FF0000UL) >> 16, + (x & 0x0000FF00UL) >> 8, + x & 0x000000FFUL}; + os.write(reinterpret_cast(tmp), 4); +} + +void write_big(std::ostream& os, uint64_t x) +{ + uint8_t tmp[8] { + (x & 0xFF00000000000000ULL) >> 56, + (x & 0x00FF000000000000ULL) >> 48, + (x & 0x0000FF0000000000ULL) >> 40, + (x & 0x000000FF00000000ULL) >> 32, + (x & 0x00000000FF000000ULL) >> 24, + (x & 0x0000000000FF0000ULL) >> 16, + (x & 0x000000000000FF00ULL) >> 8, + x & 0x00000000000000FFULL}; + os.write(reinterpret_cast(tmp), 8); +} + +void write_big(std::ostream& os, int8_t x) { write_big(os, static_cast(x)); } +void write_big(std::ostream& os, int16_t x) { write_big(os, static_cast(x)); } +void write_big(std::ostream& os, int32_t x) { write_big(os, static_cast(x)); } +void write_big(std::ostream& os, int64_t x) { write_big(os, static_cast(x)); } + +} -- cgit 0.0.5-2-1-g0f52 From b7974ffbefd62c92a8f8988b08c19224b6ba7908 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 28 Jul 2015 18:10:44 +0200 Subject: Add tests for signed integers --- test/endian_str_test.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/test/endian_str_test.cpp b/test/endian_str_test.cpp index d9d01d15c5..82bb429d50 100644 --- a/test/endian_str_test.cpp +++ b/test/endian_str_test.cpp @@ -74,7 +74,59 @@ void test_uint() ASSERT(u64 == 0x090A0B0C0D0E0F10ULL); } +void test_sint() +{ + std::stringstream str(std::ios::in | std::ios::out | std::ios::binary); + + write_little(str, int8_t (-0x01)); + write_little(str, int16_t(-0x0102)); + write_little(str, int32_t(-0x01020304L)); + write_little(str, int64_t(-0x0102030405060708LL)); + + write_big (str, int8_t (-0x09)); + write_big (str, int16_t(-0x090A)); + write_big (str, int32_t(-0x090A0B0CL)); + write_big (str, int64_t(-0x090A0B0C0D0E0F10LL)); + + const char expected[] = { //meh, stupid narrowing conversions + '\xFF', + '\xFE', '\xFE', + '\xFC', '\xFC', '\xFD', '\xFE', + '\xF8', '\xF8', '\xF9', '\xFA', '\xFB', '\xFC', '\xFD', '\xFE', + + '\xF7', + '\xF6', '\xF6', + '\xF6', '\xF5', '\xF4', '\xF4', + '\xF6', '\xF5', '\xF4', '\xF3', '\xF2', '\xF1', '\xF0', '\xF0', + 0}; //Null terminator + ASSERT(str.str() == expected); + + int8_t i8; + int16_t i16; + int32_t i32; + int64_t i64; + + read_little(str, i8); + ASSERT(i8 == -0x01); + read_little(str, i16); + ASSERT(i16 == -0x0102); + read_little(str, i32); + ASSERT(i32 == -0x01020304UL); + read_little(str, i64); + ASSERT(i64 == -0x0102030405060708ULL); + + read_big(str, i8); + ASSERT(i8 == -0x09); + read_big(str, i16); + ASSERT(i16 == -0x090A); + read_big(str, i32); + ASSERT(i32 == -0x090A0B0CUL); + read_big(str, i64); + ASSERT(i64 == -0x090A0B0C0D0E0F10ULL); +} + int main() { test_uint(); + test_sint(); } -- cgit 0.0.5-2-1-g0f52 From 3aacff8d19a1dd0bae6226b73c01f050143784a9 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 28 Jul 2015 18:16:55 +0200 Subject: Add check if stream has failed --- test/endian_str_test.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/endian_str_test.cpp b/test/endian_str_test.cpp index 82bb429d50..5f1df35de4 100644 --- a/test/endian_str_test.cpp +++ b/test/endian_str_test.cpp @@ -72,6 +72,8 @@ void test_uint() ASSERT(u32 == 0x090A0B0CUL); read_big(str, u64); ASSERT(u64 == 0x090A0B0C0D0E0F10ULL); + + ASSERT(str); //Check if stream has failed } void test_sint() @@ -123,6 +125,8 @@ void test_sint() ASSERT(i32 == -0x090A0B0CUL); read_big(str, i64); ASSERT(i64 == -0x090A0B0C0D0E0F10ULL); + + ASSERT(str); //Check if stream has failed } int main() -- cgit 0.0.5-2-1-g0f52 From d5270ff95e88e9f2e4d2b8fed1a981c44b54eb0d Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 28 Jul 2015 18:21:09 +0200 Subject: Cast values in init-list to uint8_t so the compiler shuts up about narrowing conversions --- src/endian_str.cpp | 56 +++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/endian_str.cpp b/src/endian_str.cpp index 104a11312b..f03ebf2743 100644 --- a/src/endian_str.cpp +++ b/src/endian_str.cpp @@ -122,32 +122,32 @@ void write_little(std::ostream& os, uint8_t x) void write_little(std::ostream& os, uint16_t x) { uint8_t tmp[2] { - x & 0x00FF, - (x & 0xFF00) >> 8}; + uint8_t( x & 0x00FF), + uint8_t((x & 0xFF00) >> 8)}; os.write(reinterpret_cast(tmp), 2); } void write_little(std::ostream& os, uint32_t x) { uint8_t tmp[4] { - x & 0x000000FFUL, - (x & 0x0000FF00UL) >> 8, - (x & 0x00FF0000UL) >> 16, - (x & 0xFF000000UL) >> 24}; + uint8_t( x & 0x000000FFUL), + uint8_t((x & 0x0000FF00UL) >> 8), + uint8_t((x & 0x00FF0000UL) >> 16), + uint8_t((x & 0xFF000000UL) >> 24)}; os.write(reinterpret_cast(tmp), 4); } void write_little(std::ostream& os, uint64_t x) { uint8_t tmp[8] { - x & 0x00000000000000FFULL, - (x & 0x000000000000FF00ULL) >> 8, - (x & 0x0000000000FF0000ULL) >> 16, - (x & 0x00000000FF000000ULL) >> 24, - (x & 0x000000FF00000000ULL) >> 32, - (x & 0x0000FF0000000000ULL) >> 40, - (x & 0x00FF000000000000ULL) >> 48, - (x & 0xFF00000000000000ULL) >> 56}; + uint8_t( x & 0x00000000000000FFULL), + uint8_t((x & 0x000000000000FF00ULL) >> 8), + uint8_t((x & 0x0000000000FF0000ULL) >> 16), + uint8_t((x & 0x00000000FF000000ULL) >> 24), + uint8_t((x & 0x000000FF00000000ULL) >> 32), + uint8_t((x & 0x0000FF0000000000ULL) >> 40), + uint8_t((x & 0x00FF000000000000ULL) >> 48), + uint8_t((x & 0xFF00000000000000ULL) >> 56)}; os.write(reinterpret_cast(tmp), 8); } @@ -166,32 +166,32 @@ void write_big(std::ostream& os, uint8_t x) void write_big(std::ostream& os, uint16_t x) { uint8_t tmp[2] { - (x & 0xFF00) >> 8, - x & 0x00FF}; + uint8_t((x & 0xFF00) >> 8), + uint8_t( x & 0x00FF)}; os.write(reinterpret_cast(tmp), 2); } void write_big(std::ostream& os, uint32_t x) { uint8_t tmp[4] { - (x & 0xFF000000UL) >> 24, - (x & 0x00FF0000UL) >> 16, - (x & 0x0000FF00UL) >> 8, - x & 0x000000FFUL}; + uint8_t((x & 0xFF000000UL) >> 24), + uint8_t((x & 0x00FF0000UL) >> 16), + uint8_t((x & 0x0000FF00UL) >> 8), + uint8_t( x & 0x000000FFUL)}; os.write(reinterpret_cast(tmp), 4); } void write_big(std::ostream& os, uint64_t x) { uint8_t tmp[8] { - (x & 0xFF00000000000000ULL) >> 56, - (x & 0x00FF000000000000ULL) >> 48, - (x & 0x0000FF0000000000ULL) >> 40, - (x & 0x000000FF00000000ULL) >> 32, - (x & 0x00000000FF000000ULL) >> 24, - (x & 0x0000000000FF0000ULL) >> 16, - (x & 0x000000000000FF00ULL) >> 8, - x & 0x00000000000000FFULL}; + uint8_t((x & 0xFF00000000000000ULL) >> 56), + uint8_t((x & 0x00FF000000000000ULL) >> 48), + uint8_t((x & 0x0000FF0000000000ULL) >> 40), + uint8_t((x & 0x000000FF00000000ULL) >> 32), + uint8_t((x & 0x00000000FF000000ULL) >> 24), + uint8_t((x & 0x0000000000FF0000ULL) >> 16), + uint8_t((x & 0x000000000000FF00ULL) >> 8), + uint8_t( x & 0x00000000000000FFULL)}; os.write(reinterpret_cast(tmp), 8); } -- cgit 0.0.5-2-1-g0f52 From 4fbfca378926bdcff3fc18c2d8dbaccdae3afccf Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 28 Jul 2015 18:39:19 +0200 Subject: Fix derp with unsigned ints --- test/endian_str_test.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/endian_str_test.cpp b/test/endian_str_test.cpp index 5f1df35de4..213d3030b8 100644 --- a/test/endian_str_test.cpp +++ b/test/endian_str_test.cpp @@ -113,18 +113,18 @@ void test_sint() read_little(str, i16); ASSERT(i16 == -0x0102); read_little(str, i32); - ASSERT(i32 == -0x01020304UL); + ASSERT(i32 == -0x01020304L); read_little(str, i64); - ASSERT(i64 == -0x0102030405060708ULL); + ASSERT(i64 == -0x0102030405060708LL); read_big(str, i8); ASSERT(i8 == -0x09); read_big(str, i16); ASSERT(i16 == -0x090A); read_big(str, i32); - ASSERT(i32 == -0x090A0B0CUL); + ASSERT(i32 == -0x090A0B0CL); read_big(str, i64); - ASSERT(i64 == -0x090A0B0C0D0E0F10ULL); + ASSERT(i64 == -0x090A0B0C0D0E0F10LL); ASSERT(str); //Check if stream has failed } -- cgit 0.0.5-2-1-g0f52 From 1d51c9a541361693e5d7113f8bef47c63a246436 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 28 Jul 2015 19:56:08 +0200 Subject: Implement endian stream i/o for float --- src/endian_str.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ test/endian_str_test.cpp | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/endian_str.cpp b/src/endian_str.cpp index f03ebf2743..15aaa36a3f 100644 --- a/src/endian_str.cpp +++ b/src/endian_str.cpp @@ -19,13 +19,32 @@ */ #include "endian_str.h" #include +#include #include static_assert(CHAR_BIT == 8, "Assuming that a byte has 8 bits"); +static_assert(sizeof(float) == 4, "Assuming that a float is 4 byte long"); +static_assert(sizeof(double) == 8, "Assuming that a double is 8 byte long"); namespace endian { +namespace //anonymous +{ + void pun_int_to_float(float& f, uint32_t i) + { + //Yes we need to do it this way to avoid undefined behavior + memcpy(&f, &i, 4); + } + + uint32_t pun_float_to_int(float f) + { + uint32_t ret; + memcpy(&ret, &f, 4); + return ret; + } +} + void read_little(std::istream& is, uint8_t& x) { is.get(reinterpret_cast(x)); @@ -68,6 +87,13 @@ void read_little(std::istream& is, int16_t& x) { read_little(is, reinterpret_cas void read_little(std::istream& is, int32_t& x) { read_little(is, reinterpret_cast(x)); } void read_little(std::istream& is, int64_t& x) { read_little(is, reinterpret_cast(x)); } +void read_little(std::istream& is, float& x) +{ + uint32_t tmp; + read_little(is, tmp); + pun_int_to_float(x, tmp); +} + //------------------------------------------------------------------------------ void read_big(std::istream& is, uint8_t& x) @@ -112,6 +138,13 @@ void read_big(std::istream& is, int16_t& x) { read_big(is, reinterpret_cast(x)); } void read_big(std::istream& is, int64_t& x) { read_big(is, reinterpret_cast(x)); } +void read_big(std::istream& is, float& x) +{ + uint32_t tmp; + read_big(is, tmp); + pun_int_to_float(x, tmp); +} + //------------------------------------------------------------------------------ void write_little(std::ostream& os, uint8_t x) @@ -156,6 +189,11 @@ void write_little(std::ostream& os, int16_t x) { write_little(os, static_cast(x)); } void write_little(std::ostream& os, int64_t x) { write_little(os, static_cast(x)); } +void write_little(std::ostream& os, float x) +{ + write_little(os, pun_float_to_int(x)); +} + //------------------------------------------------------------------------------ void write_big(std::ostream& os, uint8_t x) @@ -200,4 +238,9 @@ void write_big(std::ostream& os, int16_t x) { write_big(os, static_cast(x)); } void write_big(std::ostream& os, int64_t x) { write_big(os, static_cast(x)); } +void write_big(std::ostream& os, float x) +{ + write_big(os, pun_float_to_int(x)); +} + } diff --git a/test/endian_str_test.cpp b/test/endian_str_test.cpp index 213d3030b8..37cee685a5 100644 --- a/test/endian_str_test.cpp +++ b/test/endian_str_test.cpp @@ -19,6 +19,7 @@ */ #include "microtest.h" #include "endian_str.h" +#include #include using namespace endian; @@ -129,8 +130,40 @@ void test_sint() ASSERT(str); //Check if stream has failed } +void test_float() +{ + std::stringstream str(std::ios::in | std::ios::out | std::ios::binary); + + //C99 has hexadecimal floating point literals, C++ doesn't... + const float fconst = strtof("-0xCDEF01p-63", nullptr); //-1.46325e-012 + const double dconst = strtod("-0x1DEF0102030405p-375", nullptr); //-1.09484e-097 + //We will be assuming IEEE 754 here + + write_little(str, fconst); + write_big (str, fconst); + + const char expected[] = { + '\x01', '\xEF', '\xCD', '\xAB', + + '\xAB', '\xCD', '\xEF', '\x01', + 0}; //Null terminator + ASSERT(str.str() == expected); + + float f; + double d; + + read_little(str, f); + ASSERT(f == fconst); + + read_big(str, f); + ASSERT(f == fconst); + + ASSERT(str); //Check if stream has failed +} + int main() { test_uint(); test_sint(); + test_float(); } -- cgit 0.0.5-2-1-g0f52 From 9d4d2455a4a562f0883c73ec8e87c469d38069e5 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 28 Jul 2015 20:03:40 +0200 Subject: Implement endian stream i/o for double --- src/endian_str.cpp | 38 ++++++++++++++++++++++++++++++++++++++ test/endian_str_test.cpp | 8 ++++++++ 2 files changed, 46 insertions(+) diff --git a/src/endian_str.cpp b/src/endian_str.cpp index 15aaa36a3f..bc2f4b149c 100644 --- a/src/endian_str.cpp +++ b/src/endian_str.cpp @@ -43,8 +43,22 @@ namespace //anonymous memcpy(&ret, &f, 4); return ret; } + + void pun_int_to_double(double& d, uint64_t i) + { + memcpy(&d, &i, 8); + } + + uint64_t pun_double_to_int(double f) + { + uint64_t ret; + memcpy(&ret, &f, 8); + return ret; + } } +//------------------------------------------------------------------------------ + void read_little(std::istream& is, uint8_t& x) { is.get(reinterpret_cast(x)); @@ -94,6 +108,13 @@ void read_little(std::istream& is, float& x) pun_int_to_float(x, tmp); } +void read_little(std::istream& is, double& x) +{ + uint64_t tmp; + read_little(is, tmp); + pun_int_to_double(x, tmp); +} + //------------------------------------------------------------------------------ void read_big(std::istream& is, uint8_t& x) @@ -145,6 +166,13 @@ void read_big(std::istream& is, float& x) pun_int_to_float(x, tmp); } +void read_big(std::istream& is, double& x) +{ + uint64_t tmp; + read_big(is, tmp); + pun_int_to_double(x, tmp); +} + //------------------------------------------------------------------------------ void write_little(std::ostream& os, uint8_t x) @@ -194,6 +222,11 @@ void write_little(std::ostream& os, float x) write_little(os, pun_float_to_int(x)); } +void write_little(std::ostream& os, double x) +{ + write_little(os, pun_double_to_int(x)); +} + //------------------------------------------------------------------------------ void write_big(std::ostream& os, uint8_t x) @@ -243,4 +276,9 @@ void write_big(std::ostream& os, float x) write_big(os, pun_float_to_int(x)); } +void write_big(std::ostream& os, double x) +{ + write_big(os, pun_double_to_int(x)); +} + } diff --git a/test/endian_str_test.cpp b/test/endian_str_test.cpp index 37cee685a5..873cf0276d 100644 --- a/test/endian_str_test.cpp +++ b/test/endian_str_test.cpp @@ -140,12 +140,16 @@ void test_float() //We will be assuming IEEE 754 here write_little(str, fconst); + write_little(str, dconst); write_big (str, fconst); + write_big (str, dconst); const char expected[] = { '\x01', '\xEF', '\xCD', '\xAB', + '\x05', '\x04', '\x03', '\x02', '\x01', '\xEF', '\xCD', '\xAB', '\xAB', '\xCD', '\xEF', '\x01', + '\xAB', '\xCD', '\xEF', '\x01', '\x02', '\x03', '\x04', '\x05', 0}; //Null terminator ASSERT(str.str() == expected); @@ -154,9 +158,13 @@ void test_float() read_little(str, f); ASSERT(f == fconst); + read_little(str, d); + ASSERT(d == dconst); read_big(str, f); ASSERT(f == fconst); + read_big(str, d); + ASSERT(d == dconst); ASSERT(str); //Check if stream has failed } -- cgit 0.0.5-2-1-g0f52 From 8d506948615de1de5d8a1510a1d1b82de3738cfa Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 28 Jul 2015 20:11:55 +0200 Subject: Meh, we do need to instantiate operator== after all compiler optimizations... --- src/tag_array.cpp | 4 +++- src/tag_primitive.cpp | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/tag_array.cpp b/src/tag_array.cpp index 942d1f5c78..a944899402 100644 --- a/src/tag_array.cpp +++ b/src/tag_array.cpp @@ -111,9 +111,11 @@ bool operator!=(const tag_array& lhs, const tag_array& rhs) return !(lhs == rhs); } -//Enforce template instantiations. Just as for tag_primitive we have to instantiate operator!= +//Enforce template instantiations template class tag_array; template class tag_array; +template bool operator== (const tag_array& , const tag_array&); +template bool operator==(const tag_array&, const tag_array&); template bool operator!= (const tag_array& , const tag_array&); template bool operator!=(const tag_array&, const tag_array&); diff --git a/src/tag_primitive.cpp b/src/tag_primitive.cpp index 27aabcfed5..69bd9695bd 100644 --- a/src/tag_primitive.cpp +++ b/src/tag_primitive.cpp @@ -81,7 +81,12 @@ template class tag_primitive; template class tag_primitive; template class tag_primitive; template class tag_primitive; -//Need to also instantiate operator!=, whereas operator== already gets instantiated in crtp_tag +template bool operator== (const tag_primitive& , const tag_primitive&); +template bool operator==(const tag_primitive&, const tag_primitive&); +template bool operator==(const tag_primitive&, const tag_primitive&); +template bool operator==(const tag_primitive&, const tag_primitive&); +template bool operator== (const tag_primitive& , const tag_primitive&); +template bool operator== (const tag_primitive& , const tag_primitive&); template bool operator!= (const tag_primitive& , const tag_primitive&); template bool operator!=(const tag_primitive&, const tag_primitive&); template bool operator!=(const tag_primitive&, const tag_primitive&); -- cgit 0.0.5-2-1-g0f52 From c4ea821426c246dbfaa244de477c8d039524bb5a Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 28 Jul 2015 22:50:31 +0200 Subject: Get rid of unneeded bitwise ands --- src/endian_str.cpp | 56 +++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/endian_str.cpp b/src/endian_str.cpp index bc2f4b149c..8d136b09a5 100644 --- a/src/endian_str.cpp +++ b/src/endian_str.cpp @@ -183,32 +183,32 @@ void write_little(std::ostream& os, uint8_t x) void write_little(std::ostream& os, uint16_t x) { uint8_t tmp[2] { - uint8_t( x & 0x00FF), - uint8_t((x & 0xFF00) >> 8)}; + uint8_t(x), + uint8_t(x >> 8)}; os.write(reinterpret_cast(tmp), 2); } void write_little(std::ostream& os, uint32_t x) { uint8_t tmp[4] { - uint8_t( x & 0x000000FFUL), - uint8_t((x & 0x0000FF00UL) >> 8), - uint8_t((x & 0x00FF0000UL) >> 16), - uint8_t((x & 0xFF000000UL) >> 24)}; + uint8_t(x), + uint8_t(x >> 8), + uint8_t(x >> 16), + uint8_t(x >> 24)}; os.write(reinterpret_cast(tmp), 4); } void write_little(std::ostream& os, uint64_t x) { uint8_t tmp[8] { - uint8_t( x & 0x00000000000000FFULL), - uint8_t((x & 0x000000000000FF00ULL) >> 8), - uint8_t((x & 0x0000000000FF0000ULL) >> 16), - uint8_t((x & 0x00000000FF000000ULL) >> 24), - uint8_t((x & 0x000000FF00000000ULL) >> 32), - uint8_t((x & 0x0000FF0000000000ULL) >> 40), - uint8_t((x & 0x00FF000000000000ULL) >> 48), - uint8_t((x & 0xFF00000000000000ULL) >> 56)}; + uint8_t(x), + uint8_t(x >> 8), + uint8_t(x >> 16), + uint8_t(x >> 24), + uint8_t(x >> 32), + uint8_t(x >> 40), + uint8_t(x >> 48), + uint8_t(x >> 56)}; os.write(reinterpret_cast(tmp), 8); } @@ -237,32 +237,32 @@ void write_big(std::ostream& os, uint8_t x) void write_big(std::ostream& os, uint16_t x) { uint8_t tmp[2] { - uint8_t((x & 0xFF00) >> 8), - uint8_t( x & 0x00FF)}; + uint8_t(x >> 8), + uint8_t(x)}; os.write(reinterpret_cast(tmp), 2); } void write_big(std::ostream& os, uint32_t x) { uint8_t tmp[4] { - uint8_t((x & 0xFF000000UL) >> 24), - uint8_t((x & 0x00FF0000UL) >> 16), - uint8_t((x & 0x0000FF00UL) >> 8), - uint8_t( x & 0x000000FFUL)}; + uint8_t(x >> 24), + uint8_t(x >> 16), + uint8_t(x >> 8), + uint8_t(x)}; os.write(reinterpret_cast(tmp), 4); } void write_big(std::ostream& os, uint64_t x) { uint8_t tmp[8] { - uint8_t((x & 0xFF00000000000000ULL) >> 56), - uint8_t((x & 0x00FF000000000000ULL) >> 48), - uint8_t((x & 0x0000FF0000000000ULL) >> 40), - uint8_t((x & 0x000000FF00000000ULL) >> 32), - uint8_t((x & 0x00000000FF000000ULL) >> 24), - uint8_t((x & 0x0000000000FF0000ULL) >> 16), - uint8_t((x & 0x000000000000FF00ULL) >> 8), - uint8_t( x & 0x00000000000000FFULL)}; + uint8_t(x >> 56), + uint8_t(x >> 48), + uint8_t(x >> 40), + uint8_t(x >> 32), + uint8_t(x >> 24), + uint8_t(x >> 16), + uint8_t(x >> 8), + uint8_t(x)}; os.write(reinterpret_cast(tmp), 8); } -- cgit 0.0.5-2-1-g0f52 From 89425523eac6e16eb85a8a0126134f2bf3c54180 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 30 Jul 2015 13:45:20 +0200 Subject: Rename libnbt.h to nbt_tags.h Add missing forward declaration to tagfwd.h --- include/libnbt.h | 29 ----------------------------- include/nbt_tags.h | 29 +++++++++++++++++++++++++++++ include/tagfwd.h | 2 ++ src/tag_list.cpp | 2 +- src/value.cpp | 2 +- src/value_initializer.cpp | 2 +- test/nbttest.cpp | 2 +- 7 files changed, 35 insertions(+), 33 deletions(-) delete mode 100644 include/libnbt.h create mode 100644 include/nbt_tags.h diff --git a/include/libnbt.h b/include/libnbt.h deleted file mode 100644 index 9a2198778c..0000000000 --- a/include/libnbt.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * libnbt++ - A library for the Minecraft Named Binary Tag format. - * Copyright (C) 2013, 2015 ljfa-ag - * - * This file is part of libnbt++. - * - * libnbt++ is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * libnbt++ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with libnbt++. If not, see . - */ -#ifndef LIBNBT_H_INCLUDED -#define LIBNBT_H_INCLUDED - -#include "tag_primitive.h" -#include "tag_string.h" -#include "tag_compound.h" -#include "tag_list.h" -#include "tag_array.h" - -#endif // LIBNBT_H_INCLUDED diff --git a/include/nbt_tags.h b/include/nbt_tags.h new file mode 100644 index 0000000000..810bf0d661 --- /dev/null +++ b/include/nbt_tags.h @@ -0,0 +1,29 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#ifndef NBT_TAGS_H_INCLUDED +#define NBT_TAGS_H_INCLUDED + +#include "tag_primitive.h" +#include "tag_string.h" +#include "tag_array.h" +#include "tag_list.h" +#include "tag_compound.h" + +#endif // NBT_TAGS_H_INCLUDED diff --git a/include/tagfwd.h b/include/tagfwd.h index 98d75b8328..622e39d056 100644 --- a/include/tagfwd.h +++ b/include/tagfwd.h @@ -36,6 +36,8 @@ typedef tag_primitive tag_long; typedef tag_primitive tag_float; typedef tag_primitive tag_double; +class tag_string; + template class tag_array; typedef tag_array tag_byte_array; typedef tag_array tag_int_array; diff --git a/src/tag_list.cpp b/src/tag_list.cpp index e23f162eb2..7c7176835b 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -18,7 +18,7 @@ * along with libnbt++. If not, see . */ #include "tag_list.h" -#include "libnbt.h" +#include "nbt_tags.h" namespace nbt { diff --git a/src/value.cpp b/src/value.cpp index bba76b0c8b..4b3fa47678 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -18,7 +18,7 @@ * along with libnbt++. If not, see . */ #include "value.h" -#include "libnbt.h" +#include "nbt_tags.h" #include namespace nbt diff --git a/src/value_initializer.cpp b/src/value_initializer.cpp index 468b46e99c..18a4f58d96 100644 --- a/src/value_initializer.cpp +++ b/src/value_initializer.cpp @@ -18,7 +18,7 @@ * along with libnbt++. If not, see . */ #include "value_initializer.h" -#include "libnbt.h" +#include "nbt_tags.h" namespace nbt { diff --git a/test/nbttest.cpp b/test/nbttest.cpp index dc29d1a6ed..4bd9cd005d 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -18,7 +18,7 @@ * along with libnbt++. If not, see . */ #include "microtest.h" -#include "libnbt.h" +#include "nbt_tags.h" #include using namespace nbt; -- cgit 0.0.5-2-1-g0f52 From 0577e90fd1d73777e0c73995ac83bb2e0fc77db3 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 2 Aug 2015 18:33:39 +0200 Subject: Add reset method to tag_list Changes to doxygen --- include/tag_list.h | 21 ++++++++++++++++++--- src/tag_list.cpp | 6 ++++++ test/nbttest.cpp | 12 +++++++++++- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/include/tag_list.h b/include/tag_list.h index 87b2742e92..af5a87d5ee 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -32,6 +32,13 @@ 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(). + * + * 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. + * * The list's behavior is undefined if the contained values are changed in a * way that their type differs from the list's content type. */ @@ -46,8 +53,10 @@ public: static constexpr tag_type type = tag_type::List; /** - * @brief Constructs a list with the given contents of type T - * @param init list of values that are, one by one, given to a constructor of T + * @brief Constructs a list of type T with the given values + * + * Example: @code tag_list::of({3, 4, 5}) @endcode + * @param init list of values from which the elements are constructed */ template static tag_list of(std::initializer_list init); @@ -135,6 +144,12 @@ public: ///Erases all tags from the list. Preserves the content type. void 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(); iterator end(); @@ -147,7 +162,7 @@ public: * @brief Equality comparison for lists * * Lists are considered equal if they contain equal tags. Empty lists - * are always considered equal. + * are always considered equal to each other. */ friend bool operator==(const tag_list& lhs, const tag_list& rhs); friend bool operator!=(const tag_list& lhs, const tag_list& rhs); diff --git a/src/tag_list.cpp b/src/tag_list.cpp index 7c7176835b..34112b3eca 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -117,6 +117,12 @@ void tag_list::clear() tags.clear(); } +void tag_list::reset(tag_type type) +{ + clear(); + el_type_ = type; +} + auto tag_list::begin() -> iterator { return tags.begin(); } auto tag_list::end() -> iterator { return tags.end(); } auto tag_list::begin() const -> const_iterator { return tags.begin(); } diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 4bd9cd005d..347dad3e3a 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -294,9 +294,19 @@ void test_tag_list() ASSERT((list != tag_list{2, 3, 5, 7})); list.clear(); - ASSERT(list.size() == 0); + ASSERT(list.size() == 0 && list.el_type() == tag_type::String); EXPECT_EXCEPTION(list.push_back(tag_short(25)), std::bad_cast); EXPECT_EXCEPTION(list.push_back(value(nullptr)), std::bad_cast); + + list.reset(); + ASSERT(list.el_type() == tag_type::Null); + list.emplace_back(17); + ASSERT(list.el_type() == tag_type::Int); + + list.reset(tag_type::Float); + ASSERT(list.el_type() == tag_type::Float); + list.emplace_back(17.0f); + ASSERT(list == tag_list({17.0f})); ASSERT(tag_list() == tag_list(tag_type::Int)); ASSERT(tag_list(tag_type::Short) == tag_list(tag_type::Int)); -- cgit 0.0.5-2-1-g0f52 From 06644a4f989fa9fe673281859734be7a7b72aeac Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 2 Aug 2015 18:48:27 +0200 Subject: Change semantics of empty list equality I considered making empty lists of undetermined type equal to all empty lists, but then the comparison would not be transitive --- include/tag_list.h | 4 ++-- src/tag_list.cpp | 2 -- test/nbttest.cpp | 10 ++++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/tag_list.h b/include/tag_list.h index af5a87d5ee..adb8623f45 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -161,8 +161,8 @@ public: /** * @brief Equality comparison for lists * - * Lists are considered equal if they contain equal tags. Empty lists - * are always considered equal to each other. + * Lists are considered equal if their content types and the contained tags + * are equal. */ friend bool operator==(const tag_list& lhs, const tag_list& rhs); friend bool operator!=(const tag_list& lhs, const tag_list& rhs); diff --git a/src/tag_list.cpp b/src/tag_list.cpp index 34112b3eca..7900f9872e 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -132,8 +132,6 @@ auto tag_list::cend() const -> const_iterator { return tags.cend(); } bool operator==(const tag_list& lhs, const tag_list& rhs) { - if(lhs.size() == 0 && rhs.size() == 0) - return true; return lhs.el_type_ == rhs.el_type_ && lhs.tags == rhs.tags; } diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 347dad3e3a..c253a5d59b 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -297,19 +297,21 @@ void test_tag_list() ASSERT(list.size() == 0 && list.el_type() == tag_type::String); EXPECT_EXCEPTION(list.push_back(tag_short(25)), std::bad_cast); EXPECT_EXCEPTION(list.push_back(value(nullptr)), std::bad_cast); - + list.reset(); ASSERT(list.el_type() == tag_type::Null); list.emplace_back(17); ASSERT(list.el_type() == tag_type::Int); - + list.reset(tag_type::Float); ASSERT(list.el_type() == tag_type::Float); list.emplace_back(17.0f); ASSERT(list == tag_list({17.0f})); - ASSERT(tag_list() == tag_list(tag_type::Int)); - ASSERT(tag_list(tag_type::Short) == tag_list(tag_type::Int)); + ASSERT(tag_list() != tag_list(tag_type::Int)); + ASSERT(tag_list() == tag_list()); + ASSERT(tag_list(tag_type::Short) != tag_list(tag_type::Int)); + ASSERT(tag_list(tag_type::Short) == tag_list(tag_type::Short)); tag_list short_list = tag_list::of({25, 36}); ASSERT(short_list.el_type() == tag_type::Short); -- cgit 0.0.5-2-1-g0f52 From a3fb29ae603a293f12959abbc35af1bf4a156c76 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 2 Aug 2015 21:17:38 +0200 Subject: Add more tests for tag --- test/nbttest.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index c253a5d59b..eeb5a84d01 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -23,6 +23,30 @@ using namespace nbt; +void test_tag() +{ + ASSERT(*tag::create(tag_type::Byte) == tag_byte()); + ASSERT(tag::create(tag_type::Null) == nullptr); + ASSERT(tag::create(tag_type::End) == nullptr); + + tag_string tstr("foo"); + auto cl = tstr.clone(); + ASSERT(tstr.get() == "foo"); + ASSERT(tstr == *cl); + + cl = std::move(tstr).clone(); + ASSERT(*cl == tag_string("foo")); + ASSERT(*cl != tag_string("bar")); + + cl = std::move(*cl).move_clone(); + ASSERT(*cl == tag_string("foo")); + + tstr.assign(tag_string("bar")); + EXPECT_EXCEPTION(tstr.assign(tag_int(6)), std::bad_cast); + ASSERT(tstr.get() == "bar"); + std::clog << "test_tag passed" << std::endl; +} + void test_get_type() { ASSERT(tag_byte().get_type() == tag_type::Byte); @@ -390,6 +414,7 @@ void test_tag_int_array() int main() { + test_tag(); test_get_type(); test_tag_primitive(); test_tag_string(); -- cgit 0.0.5-2-1-g0f52 From be7e646d091bd1f250d77f443462607b5c27a419 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 2 Aug 2015 21:47:01 +0200 Subject: Create tag::create function --- include/tag.h | 7 +++++++ src/tag.cpp | 22 ++++++++++++++++++++++ test/nbttest.cpp | 5 +++-- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/include/tag.h b/include/tag.h index 63125f1dc5..75a41f2e1f 100644 --- a/include/tag.h +++ b/include/tag.h @@ -55,6 +55,7 @@ public: ///Returns the type of the tag virtual tag_type get_type() const noexcept = 0; + //Polymorphic clone methods virtual std::unique_ptr clone() const& = 0; virtual std::unique_ptr move_clone() && = 0; std::unique_ptr clone() &&; @@ -65,6 +66,12 @@ public: */ virtual tag& assign(tag&& rhs) = 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 create(tag_type type); + friend bool operator==(const tag& lhs, const tag& rhs); friend bool operator!=(const tag& lhs, const tag& rhs); diff --git a/src/tag.cpp b/src/tag.cpp index fbed9fb6e1..b695bd98bb 100644 --- a/src/tag.cpp +++ b/src/tag.cpp @@ -18,7 +18,9 @@ * along with libnbt++. If not, see . */ #include "tag.h" +#include "nbt_tags.h" #include +#include #include namespace nbt @@ -29,6 +31,26 @@ std::unique_ptr tag::clone() && return std::move(*this).move_clone(); } +std::unique_ptr tag::create(tag_type type) +{ + switch(type) + { + case tag_type::Byte: return make_unique(); + case tag_type::Short: return make_unique(); + case tag_type::Int: return make_unique(); + case tag_type::Long: return make_unique(); + case tag_type::Float: return make_unique(); + case tag_type::Double: return make_unique(); + case tag_type::Byte_Array: return make_unique(); + case tag_type::String: return make_unique(); + case tag_type::List: return make_unique(); + case tag_type::Compound: return make_unique(); + case tag_type::Int_Array: return make_unique(); + + default: throw std::invalid_argument("Invalid tag type"); + } +} + bool operator==(const tag& lhs, const tag& rhs) { if(typeid(lhs) != typeid(rhs)) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index eeb5a84d01..12f80ed62e 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -20,14 +20,15 @@ #include "microtest.h" #include "nbt_tags.h" #include +#include using namespace nbt; void test_tag() { ASSERT(*tag::create(tag_type::Byte) == tag_byte()); - ASSERT(tag::create(tag_type::Null) == nullptr); - ASSERT(tag::create(tag_type::End) == nullptr); + EXPECT_EXCEPTION(tag::create(tag_type::Null), std::invalid_argument); + EXPECT_EXCEPTION(tag::create(tag_type::End), std::invalid_argument); tag_string tstr("foo"); auto cl = tstr.clone(); -- cgit 0.0.5-2-1-g0f52 From a46a8340864f5840d4f5981edbccec6256765c84 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 2 Aug 2015 21:53:13 +0200 Subject: Use stof and stod instead of strtof and strtod --- test/endian_str_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/endian_str_test.cpp b/test/endian_str_test.cpp index 873cf0276d..7f583249ca 100644 --- a/test/endian_str_test.cpp +++ b/test/endian_str_test.cpp @@ -135,8 +135,8 @@ void test_float() std::stringstream str(std::ios::in | std::ios::out | std::ios::binary); //C99 has hexadecimal floating point literals, C++ doesn't... - const float fconst = strtof("-0xCDEF01p-63", nullptr); //-1.46325e-012 - const double dconst = strtod("-0x1DEF0102030405p-375", nullptr); //-1.09484e-097 + const float fconst = std::stof("-0xCDEF01p-63"); //-1.46325e-012 + const double dconst = std::stod("-0x1DEF0102030405p-375"); //-1.09484e-097 //We will be assuming IEEE 754 here write_little(str, fconst); -- cgit 0.0.5-2-1-g0f52 From b1fddf89e7238ad51d6dc1a83ebb3dc3e4b86b4e Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 3 Aug 2015 11:24:21 +0200 Subject: Make tag_list constructor from type explicit --- include/tag_list.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/tag_list.h b/include/tag_list.h index adb8623f45..ac35b1d40d 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -69,7 +69,7 @@ public: tag_list(); ///Constructs an empty list with the given content type - tag_list(tag_type type); + explicit tag_list(tag_type type); ///Constructs a list with the given contents tag_list(std::initializer_list init); -- cgit 0.0.5-2-1-g0f52 From 99f15267e1f6db299d87bfb9744dec11c40963e4 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 4 Aug 2015 17:38:49 +0200 Subject: Create endian enum and read/write functions with specified endian --- include/endian_str.h | 28 ++++++++++++++++++++++++++++ test/endian_str_test.cpp | 16 ++++++++-------- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/include/endian_str.h b/include/endian_str.h index 3ccd9817c7..b1549bc27c 100644 --- a/include/endian_str.h +++ b/include/endian_str.h @@ -30,6 +30,12 @@ namespace endian { +enum endian { little, big }; + +///Reads number from stream in specified endian +template +void read(std::istream& is, T& x, endian e); + ///Reads number from stream in little endian void read_little(std::istream& is, uint8_t& x); void read_little(std::istream& is, uint16_t& x); @@ -54,6 +60,10 @@ void read_big(std::istream& is, int64_t& x); void read_big(std::istream& is, float& x); void read_big(std::istream& is, double& x); +///Writes number to stream in specified endian +template +void write(std::ostream& os, T x, endian e); + ///Writes number to stream in little endian void write_little(std::ostream& os, uint8_t x); void write_little(std::ostream& os, uint16_t x); @@ -78,6 +88,24 @@ void write_big(std::ostream& os, int64_t x); void write_big(std::ostream& os, float x); void write_big(std::ostream& os, double x); +template +void read(std::istream& is, T& x, endian e) +{ + if(e == little) + read_little(is, x); + else + read_big(is, x); +} + +template +void write(std::ostream& os, T x, endian e) +{ + if(e == little) + write_little(os, x); + else + write_big(os, x); +} + } #endif // ENDIAN_STR_H_INCLUDED diff --git a/test/endian_str_test.cpp b/test/endian_str_test.cpp index 7f583249ca..1e8b14606b 100644 --- a/test/endian_str_test.cpp +++ b/test/endian_str_test.cpp @@ -30,13 +30,13 @@ void test_uint() write_little(str, uint8_t (0x01)); write_little(str, uint16_t(0x0102)); - write_little(str, uint32_t(0x01020304UL)); + write (str, uint32_t(0x01020304UL), endian::little); write_little(str, uint64_t(0x0102030405060708ULL)); write_big (str, uint8_t (0x09)); write_big (str, uint16_t(0x090A)); write_big (str, uint32_t(0x090A0B0CUL)); - write_big (str, uint64_t(0x090A0B0C0D0E0F10ULL)); + write (str, uint64_t(0x090A0B0C0D0E0F10ULL), endian::big); const char expected[] = { 1, @@ -62,14 +62,14 @@ void test_uint() ASSERT(u16 == 0x0102); read_little(str, u32); ASSERT(u32 == 0x01020304UL); - read_little(str, u64); + read(str, u64, endian::little); ASSERT(u64 == 0x0102030405060708ULL); read_big(str, u8); ASSERT(u8 == 0x09); read_big(str, u16); ASSERT(u16 == 0x090A); - read_big(str, u32); + read(str, u32, endian::big); ASSERT(u32 == 0x090A0B0CUL); read_big(str, u64); ASSERT(u64 == 0x090A0B0C0D0E0F10ULL); @@ -84,11 +84,11 @@ void test_sint() write_little(str, int8_t (-0x01)); write_little(str, int16_t(-0x0102)); write_little(str, int32_t(-0x01020304L)); - write_little(str, int64_t(-0x0102030405060708LL)); + write (str, int64_t(-0x0102030405060708LL), endian::little); write_big (str, int8_t (-0x09)); write_big (str, int16_t(-0x090A)); - write_big (str, int32_t(-0x090A0B0CL)); + write (str, int32_t(-0x090A0B0CL), endian::big); write_big (str, int64_t(-0x090A0B0C0D0E0F10LL)); const char expected[] = { //meh, stupid narrowing conversions @@ -113,7 +113,7 @@ void test_sint() ASSERT(i8 == -0x01); read_little(str, i16); ASSERT(i16 == -0x0102); - read_little(str, i32); + read(str, i32, endian::little); ASSERT(i32 == -0x01020304L); read_little(str, i64); ASSERT(i64 == -0x0102030405060708LL); @@ -124,7 +124,7 @@ void test_sint() ASSERT(i16 == -0x090A); read_big(str, i32); ASSERT(i32 == -0x090A0B0CL); - read_big(str, i64); + read(str, i64, endian::big); ASSERT(i64 == -0x090A0B0C0D0E0F10LL); ASSERT(str); //Check if stream has failed -- cgit 0.0.5-2-1-g0f52 From 61b2560a524ad7fe7d5e34cfccd70a7e92e7b8b9 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 4 Aug 2015 17:58:14 +0200 Subject: Create is_valid_type function --- include/tag.h | 6 ++++++ src/tag.cpp | 5 +++++ test/nbttest.cpp | 9 +++++++++ 3 files changed, 20 insertions(+) diff --git a/include/tag.h b/include/tag.h index 75a41f2e1f..ae84891c76 100644 --- a/include/tag.h +++ b/include/tag.h @@ -45,6 +45,12 @@ enum class tag_type : int8_t 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 + */ +bool is_valid_type(int type, bool allow_end = false); + ///Base class for all NBT tag classes class tag { diff --git a/src/tag.cpp b/src/tag.cpp index b695bd98bb..db4c09aa2e 100644 --- a/src/tag.cpp +++ b/src/tag.cpp @@ -26,6 +26,11 @@ namespace nbt { +bool is_valid_type(int type, bool allow_end) +{ + return (allow_end ? 0 : 1) <= type && type <= 11; +} + std::unique_ptr tag::clone() && { return std::move(*this).move_clone(); diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 12f80ed62e..15b0c67fba 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -26,6 +26,15 @@ using namespace nbt; void test_tag() { + ASSERT(!is_valid_type(-1)); + ASSERT(!is_valid_type(0)); + ASSERT(is_valid_type(0, true)); + ASSERT(is_valid_type(1)); + ASSERT(is_valid_type(5, false)); + ASSERT(is_valid_type(7, true)); + ASSERT(is_valid_type(11)); + ASSERT(!is_valid_type(12)); + ASSERT(*tag::create(tag_type::Byte) == tag_byte()); EXPECT_EXCEPTION(tag::create(tag_type::Null), std::invalid_argument); EXPECT_EXCEPTION(tag::create(tag_type::End), std::invalid_argument); -- cgit 0.0.5-2-1-g0f52 From 47531478a60d979faf8d744d17c246c8e40a7d24 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 4 Aug 2015 18:24:04 +0200 Subject: Lay foundations for stream_reader class --- include/io/stream_reader.h | 79 ++++++++++++++++++++++++++++++++++++++++++++++ test/CMakeLists.txt | 4 +++ test/io/read_test.cpp | 25 +++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 include/io/stream_reader.h create mode 100644 test/io/read_test.cpp diff --git a/include/io/stream_reader.h b/include/io/stream_reader.h new file mode 100644 index 0000000000..142402a516 --- /dev/null +++ b/include/io/stream_reader.h @@ -0,0 +1,79 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#ifndef STREAM_READER_H_INCLUDED +#define STREAM_READER_H_INCLUDED + +#include "endian_str.h" +#include "tag.h" +#include +#include + +namespace nbt +{ +namespace io +{ + +/** + * @brief Helper class for reading NBT tags from input streams + */ +class stream_reader +{ +public: + ///Exception that gets thrown when reading is not successful + class input_error : public std::runtime_error + { + input_error(const std::string& what_arg): + std::runtime_error(what_arg) {} + }; + + /** + * @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 + */ + 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; + + ///Reads a tag type from the stream + tag_type read_type(bool allow_end = false); + ///Reads a number from the stream + template + 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. + */ + std::string read_string(); + +private: + std::istream& is; + const endian::endian endian; +}; + +} +} + +#endif // STREAM_READER_H_INCLUDED diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 735ca2bb4c..a02101a516 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -8,3 +8,7 @@ add_test(nbttest nbttest) add_executable(endian_str_test endian_str_test.cpp) target_link_libraries(endian_str_test nbt++) add_test(endian_str_test endian_str_test) + +add_executable(read_test io/read_test.cpp) +target_link_libraries(read_test nbt++) +add_test(read_test read_test) diff --git a/test/io/read_test.cpp b/test/io/read_test.cpp new file mode 100644 index 0000000000..f47b1e832b --- /dev/null +++ b/test/io/read_test.cpp @@ -0,0 +1,25 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include "io/stream_reader.h" + +int main() +{ + +} -- cgit 0.0.5-2-1-g0f52 From 5f86fd9785b417aef6fec7a9f913bc4349d6c59b Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 4 Aug 2015 19:33:58 +0200 Subject: Add tests for stream_reader --- include/io/stream_reader.h | 2 +- test/CMakeLists.txt | 1 + test/io/read_test.cpp | 79 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/include/io/stream_reader.h b/include/io/stream_reader.h index 142402a516..659aacbd82 100644 --- a/include/io/stream_reader.h +++ b/include/io/stream_reader.h @@ -48,7 +48,7 @@ public: * @param e the byte order of the source data. The Java edition * of Minecraft uses Big Endian, the Pocket edition uses Little Endian */ - stream_reader(std::istream& is, endian::endian e = endian::big) noexcept; + explicit stream_reader(std::istream& is, endian::endian e = endian::big) noexcept; ///Returns the stream std::istream& get_istr() const; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a02101a516..9da0eb61c5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,5 @@ enable_testing() +include_directories(.) include_directories(${libnbt++_SOURCE_DIR}/include) add_executable(nbttest nbttest.cpp) diff --git a/test/io/read_test.cpp b/test/io/read_test.cpp index f47b1e832b..aaed5e4471 100644 --- a/test/io/read_test.cpp +++ b/test/io/read_test.cpp @@ -17,9 +17,86 @@ * You should have received a copy of the GNU Lesser General Public License * along with libnbt++. If not, see . */ +#include "microtest.h" #include "io/stream_reader.h" +#include -int main() +using namespace nbt; + +void test_stream_reader_big() +{ + const char input[] { + 1, //tag_type::Byte + 0, //tag_type::End + 11, //tag_type::Int_Array + + 0x0a, 0x0b, 0x0c, 0x0d, //0x0a0b0c0d in Big Endian + + 0x00, 0x06, //String length in Big Endian + 'f', 'o', 'o', 'b', 'a', 'r', + + 0 //tag_type::End (invalid with allow_end = false) + }; + std::istringstream is(std::string(input, sizeof input)); + nbt::io::stream_reader reader(is); + + ASSERT(&reader.get_istr() == &is); + ASSERT(reader.get_endian() == endian::big); + + ASSERT(reader.read_type() == tag_type::Byte); + ASSERT(reader.read_type(true) == tag_type::End); + ASSERT(reader.read_type(false) == tag_type::Int_Array); + + int32_t i; + reader.read_num(i); + ASSERT(i == 0x0a0b0c0dL); + + ASSERT(reader.read_string() == "foobar"); + + EXPECT_EXCEPTION(reader.read_type(false), io::stream_reader::input_error); + ASSERT(!is); + is.clear(); + + //Test for invalid tag type 12 + is.str("\x0c"); + EXPECT_EXCEPTION(reader.read_type(), io::stream_reader::input_error); + ASSERT(!is); + is.clear(); + + //Test for unexpcted EOF on numbers (input too short for int32_t) + is.str("\x03\x04"); + EXPECT_EXCEPTION(reader.read_num(i), io::stream_reader::input_error); + ASSERT(!is); +} + +void test_stream_reader_little() { + const char input[] { + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, //0x0d0c0b0a09080706 in Little Endian + + 0x06, 0x00, //String length in Little Endian + 'f', 'o', 'o', 'b', 'a', 'r', + + 0x10, 0x00, //String length (intentionally too large) + 'a', 'b', 'c', 'd' //unexpected EOF + }; + std::istringstream is(std::string(input, sizeof input)); + nbt::io::stream_reader reader(is, endian::little); + + ASSERT(reader.get_endian() == endian::little); + int64_t i; + reader.read_num(i); + ASSERT(i == 0x0d0c0b0a09080706LL); + + ASSERT(reader.read_string() == "foobar"); + + EXPECT_EXCEPTION(reader.read_string(), io::stream_reader::input_error); + ASSERT(!is); +} + +int main() +{ + test_stream_reader_big(); + test_stream_reader_little(); } -- cgit 0.0.5-2-1-g0f52 From 5e750cf89484c3229100534137def1627368146c Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 4 Aug 2015 20:00:24 +0200 Subject: Small changes to stream_reader documentation --- include/io/stream_reader.h | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/include/io/stream_reader.h b/include/io/stream_reader.h index 659aacbd82..ab07f83ad9 100644 --- a/include/io/stream_reader.h +++ b/include/io/stream_reader.h @@ -39,6 +39,7 @@ public: ///Exception that gets thrown when reading is not successful class input_error : public std::runtime_error { + public: input_error(const std::string& what_arg): std::runtime_error(what_arg) {} }; @@ -55,16 +56,27 @@ public: ///Returns the byte order endian::endian get_endian() const; - ///Reads a tag type from the stream + /** + * @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); - ///Reads a number from the stream + + /** + * @brief Reads a binary number from the stream + * + * Does not check if the reading actually succeeds + */ template 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(); -- cgit 0.0.5-2-1-g0f52 From 40435a6da8392c86fd120ec6b4fbda0bd1c89ab3 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 4 Aug 2015 20:19:45 +0200 Subject: Implement stream_reader --- CMakeLists.txt | 4 ++- include/io/stream_reader.h | 9 ++++-- src/io/stream_reader.cpp | 70 ++++++++++++++++++++++++++++++++++++++++++++++ test/io/read_test.cpp | 2 +- 4 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 src/io/stream_reader.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d30f8a820c..39de6c6715 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,9 @@ add_library(nbt++ STATIC src/tag_primitive.cpp src/tag_string.cpp src/value.cpp - src/value_initializer.cpp) + src/value_initializer.cpp + + src/io/stream_reader.cpp) enable_testing() add_subdirectory(test) diff --git a/include/io/stream_reader.h b/include/io/stream_reader.h index ab07f83ad9..5d10e021ed 100644 --- a/include/io/stream_reader.h +++ b/include/io/stream_reader.h @@ -65,8 +65,7 @@ public: /** * @brief Reads a binary number from the stream - * - * Does not check if the reading actually succeeds + * @throw input_error on failure */ template void read_num(T& x); @@ -85,6 +84,12 @@ private: const endian::endian endian; }; +template +void stream_reader::read_num(T& x) +{ + endian::read(is, x, endian); +} + } } diff --git a/src/io/stream_reader.cpp b/src/io/stream_reader.cpp new file mode 100644 index 0000000000..7605d62776 --- /dev/null +++ b/src/io/stream_reader.cpp @@ -0,0 +1,70 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include "io/stream_reader.h" +#include + +namespace nbt +{ +namespace io +{ + +stream_reader::stream_reader(std::istream& is, endian::endian e) noexcept: + is(is), endian(e) +{} + +std::istream& stream_reader::get_istr() const +{ + return is; +} + +endian::endian stream_reader::get_endian() const +{ + return endian; +} + +tag_type stream_reader::read_type(bool allow_end) +{ + int type = is.get(); + if(!is) + throw input_error("Error reading tag type"); + if(!is_valid_type(type, allow_end)) + { + is.setstate(std::ios::failbit); + throw input_error("Invalid tag type: " + std::to_string(type)); + } + return static_cast(type); +} + +std::string stream_reader::read_string() +{ + uint16_t len; + read_num(len); + if(!is) + throw input_error("Error reading string"); + + std::string ret(len, '\0'); + is.read(&ret[0], len); //C++11 allows us to do this + if(!is) + throw input_error("Error reading string"); + return ret; +} + +} +} diff --git a/test/io/read_test.cpp b/test/io/read_test.cpp index aaed5e4471..2b37846f2f 100644 --- a/test/io/read_test.cpp +++ b/test/io/read_test.cpp @@ -65,7 +65,7 @@ void test_stream_reader_big() //Test for unexpcted EOF on numbers (input too short for int32_t) is.str("\x03\x04"); - EXPECT_EXCEPTION(reader.read_num(i), io::stream_reader::input_error); + reader.read_num(i); ASSERT(!is); } -- cgit 0.0.5-2-1-g0f52 From 2676e8cc9137d093a54c9a9586be5f5a48862c39 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 4 Aug 2015 20:40:00 +0200 Subject: Correct doxygen on read_num --- include/io/stream_reader.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/io/stream_reader.h b/include/io/stream_reader.h index 5d10e021ed..74f9536105 100644 --- a/include/io/stream_reader.h +++ b/include/io/stream_reader.h @@ -65,7 +65,8 @@ public: /** * @brief Reads a binary number from the stream - * @throw input_error on failure + * + * On failure, will set the failbit on the stream. */ template void read_num(T& x); -- cgit 0.0.5-2-1-g0f52 From 783cc495dc762363e4cce0196e06f201184737a5 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 5 Aug 2015 11:43:01 +0200 Subject: Add bigtest.nbt file --- test/CMakeLists.txt | 3 +++ test/io/bigtest.nbt | Bin 0 -> 515 bytes 2 files changed, 3 insertions(+) create mode 100644 test/io/bigtest.nbt diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9da0eb61c5..c338144f62 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,4 +12,7 @@ add_test(endian_str_test endian_str_test) add_executable(read_test io/read_test.cpp) target_link_libraries(read_test nbt++) +add_custom_command(TARGET read_test POST_BUILD + COMMAND ${CMAKE_COMMAND} -E + copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/bigtest.nbt ${CMAKE_CURRENT_BINARY_DIR}) add_test(read_test read_test) diff --git a/test/io/bigtest.nbt b/test/io/bigtest.nbt new file mode 100644 index 0000000000..945b86f562 Binary files /dev/null and b/test/io/bigtest.nbt differ -- cgit 0.0.5-2-1-g0f52 From 95fd640876ef0f49c5667a4888ce95c5d36ca2b9 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 5 Aug 2015 11:50:26 +0200 Subject: Use "using" for inheriting input_error constructors --- include/io/stream_reader.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/io/stream_reader.h b/include/io/stream_reader.h index 74f9536105..9d503f8ea4 100644 --- a/include/io/stream_reader.h +++ b/include/io/stream_reader.h @@ -39,9 +39,7 @@ public: ///Exception that gets thrown when reading is not successful class input_error : public std::runtime_error { - public: - input_error(const std::string& what_arg): - std::runtime_error(what_arg) {} + using std::runtime_error::runtime_error; }; /** -- cgit 0.0.5-2-1-g0f52 From 4ea7f74128aa4f7ea847a49d95c261372fa44abc Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 5 Aug 2015 12:05:36 +0200 Subject: Use bigtest_uncompressed instead --- test/CMakeLists.txt | 2 +- test/io/bigtest_uncompressed | Bin 0 -> 1544 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 test/io/bigtest_uncompressed diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c338144f62..ae8c7c03e0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -14,5 +14,5 @@ add_executable(read_test io/read_test.cpp) target_link_libraries(read_test nbt++) add_custom_command(TARGET read_test POST_BUILD COMMAND ${CMAKE_COMMAND} -E - copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/bigtest.nbt ${CMAKE_CURRENT_BINARY_DIR}) + copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/bigtest_uncompressed ${CMAKE_CURRENT_BINARY_DIR}) add_test(read_test read_test) diff --git a/test/io/bigtest_uncompressed b/test/io/bigtest_uncompressed new file mode 100644 index 0000000000..a2021df7cb Binary files /dev/null and b/test/io/bigtest_uncompressed differ -- cgit 0.0.5-2-1-g0f52 From 17c81269da417affccfbd16087d5a4d020712a90 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 5 Aug 2015 13:01:49 +0200 Subject: Create test for reading the bigtest file --- include/io/stream_reader.h | 6 ++++++ test/io/read_test.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/include/io/stream_reader.h b/include/io/stream_reader.h index 9d503f8ea4..07eb36192d 100644 --- a/include/io/stream_reader.h +++ b/include/io/stream_reader.h @@ -54,6 +54,12 @@ public: ///Returns the byte order endian::endian get_endian() const; + /** + * @brief Reads a tag of the given type without name from the stream + * @throw input_error on failure + */ + std::unique_ptr read_payload(tag_type type); + /** * @brief Reads a tag type from the stream * @param allow_end whether to consider tag_type::End valid diff --git a/test/io/read_test.cpp b/test/io/read_test.cpp index 2b37846f2f..9e70387f2e 100644 --- a/test/io/read_test.cpp +++ b/test/io/read_test.cpp @@ -19,6 +19,8 @@ */ #include "microtest.h" #include "io/stream_reader.h" +#include "nbt_tags.h" +#include #include using namespace nbt; @@ -95,8 +97,52 @@ void test_stream_reader_little() ASSERT(!is); } +void test_read_bigtest() +{ + std::ifstream file("bigtest_uncompressed", std::ios::binary); + ASSERT(file); + nbt::io::stream_reader reader(file); + + ASSERT(reader.read_type() == tag_type::Compound); + ASSERT(reader.read_string() == "Level"); + auto tagptr = reader.read_payload(tag_type::Compound); + + ASSERT(tagptr->get_type() == tag_type::Compound); + const tag_compound& comp = static_cast(*tagptr); + + ASSERT(comp.size() == 11); + + ASSERT(comp.at("byteTest") == tag_byte(127)); + ASSERT(comp.at("shortTest") == tag_short(32767)); + ASSERT(comp.at("intTest") == tag_int(2147483647L)); + ASSERT(comp.at("longTest") == tag_long(9223372036854775807LL)); + ASSERT(comp.at("floatTest") == tag_float(std::stof("0xff1832p-25"))); //0.4982315 + ASSERT(comp.at("doubleTest") == tag_double(std::stod("0x1f8f6bbbff6a5ep-54"))); //0.493128713218231 + + //From bigtest.nbt: "the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...)" + tag_byte_array byteArrayTest; + for(int n = 0; n < 1000; ++n) + byteArrayTest.get().push_back((n*n*255 + n*7) % 100); + ASSERT(comp.at("byteArrayTest") == byteArrayTest); + + ASSERT(comp.at("stringTest") == tag_string("HELLO WORLD THIS IS A TEST STRING \u00C5\u00C4\u00D6!")); + + ASSERT(comp.at("listTest (compound)") == tag_list::of({ + {{"created-on", tag_long(1264099775885)}, {"name", "Compound tag #0"}}, + {{"created-on", tag_long(1264099775885)}, {"name", "Compound tag #1"}} + })); + + ASSERT(comp.at("listTest (long)") == tag_list::of({11, 12, 13, 14, 15})); + + ASSERT((comp.at("nested compound test") == tag_compound{ + {"egg", tag_compound{{"value", 0.5f}, {"name", "Eggbert"}}}, + {"ham", tag_compound{{"value", 0.75f}, {"name", "Hampus"}}} + })); +} + int main() { test_stream_reader_big(); test_stream_reader_little(); + test_read_bigtest(); } -- cgit 0.0.5-2-1-g0f52 From 92dd084fdd7f44d7aba88424134d97e3eceb3b0f Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 5 Aug 2015 13:50:30 +0200 Subject: Implement stream_reader::read_payload --- include/io/stream_reader.h | 1 + include/tag.h | 7 +++++++ src/io/stream_reader.cpp | 7 +++++++ test/io/read_test.cpp | 2 +- 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/include/io/stream_reader.h b/include/io/stream_reader.h index 07eb36192d..056d14e9dd 100644 --- a/include/io/stream_reader.h +++ b/include/io/stream_reader.h @@ -23,6 +23,7 @@ #include "endian_str.h" #include "tag.h" #include +#include #include namespace nbt diff --git a/include/tag.h b/include/tag.h index ae84891c76..4b547e013c 100644 --- a/include/tag.h +++ b/include/tag.h @@ -51,6 +51,10 @@ enum class tag_type : int8_t */ bool is_valid_type(int type, bool allow_end = false); +//Forward declaration +namespace io +{ class stream_reader; } + ///Base class for all NBT tag classes class tag { @@ -72,6 +76,9 @@ public: */ virtual tag& assign(tag&& rhs) = 0; + ///Reads the tag's payload from the stream + virtual void read_payload(io::stream_reader& reader) = 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) diff --git a/src/io/stream_reader.cpp b/src/io/stream_reader.cpp index 7605d62776..ff0fe183ea 100644 --- a/src/io/stream_reader.cpp +++ b/src/io/stream_reader.cpp @@ -39,6 +39,13 @@ endian::endian stream_reader::get_endian() const return endian; } +std::unique_ptr stream_reader::read_payload(tag_type type) +{ + std::unique_ptr t = tag::create(type); + t->read_payload(*this); + return t; +} + tag_type stream_reader::read_type(bool allow_end) { int type = is.get(); diff --git a/test/io/read_test.cpp b/test/io/read_test.cpp index 9e70387f2e..78a6ea345f 100644 --- a/test/io/read_test.cpp +++ b/test/io/read_test.cpp @@ -122,7 +122,7 @@ void test_read_bigtest() //From bigtest.nbt: "the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...)" tag_byte_array byteArrayTest; for(int n = 0; n < 1000; ++n) - byteArrayTest.get().push_back((n*n*255 + n*7) % 100); + byteArrayTest.push_back((n*n*255 + n*7) % 100); ASSERT(comp.at("byteArrayTest") == byteArrayTest); ASSERT(comp.at("stringTest") == tag_string("HELLO WORLD THIS IS A TEST STRING \u00C5\u00C4\u00D6!")); -- cgit 0.0.5-2-1-g0f52 From 43842d2246731a1fab79d9326bd5bec8098505d1 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 5 Aug 2015 14:01:13 +0200 Subject: Implement read_payload for all tags --- include/tag_array.h | 2 ++ include/tag_compound.h | 2 ++ include/tag_list.h | 2 ++ include/tag_primitive.h | 2 ++ include/tag_string.h | 2 ++ src/tag_array.cpp | 37 +++++++++++++++++++++++++++++++++++++ src/tag_compound.cpp | 14 ++++++++++++++ src/tag_list.cpp | 18 ++++++++++++++++++ src/tag_primitive.cpp | 14 ++++++++++++++ src/tag_string.cpp | 6 ++++++ 10 files changed, 99 insertions(+) diff --git a/include/tag_array.h b/include/tag_array.h index 9a79489c56..5ce7dd366e 100644 --- a/include/tag_array.h +++ b/include/tag_array.h @@ -104,6 +104,8 @@ public: const_iterator cbegin() const; const_iterator cend() const; + void read_payload(io::stream_reader& reader) override; + private: std::vector data; }; diff --git a/include/tag_compound.h b/include/tag_compound.h index 8e09ff4834..60376d2446 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -117,6 +117,8 @@ public: const_iterator cbegin() const; const_iterator cend() const; + void read_payload(io::stream_reader& reader) override; + friend bool operator==(const tag_compound& lhs, const tag_compound& rhs); friend bool operator!=(const tag_compound& lhs, const tag_compound& rhs); diff --git a/include/tag_list.h b/include/tag_list.h index ac35b1d40d..324d2de6d8 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -158,6 +158,8 @@ public: const_iterator cbegin() const; const_iterator cend() const; + void read_payload(io::stream_reader& reader) override; + /** * @brief Equality comparison for lists * diff --git a/include/tag_primitive.h b/include/tag_primitive.h index ac41568920..b1bb9920c0 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -53,6 +53,8 @@ public: tag_primitive& operator=(T value); void set(T value); + void read_payload(io::stream_reader& reader) override; + private: T value; }; diff --git a/include/tag_string.h b/include/tag_string.h index 0cb057125f..2a8a3d3f4e 100644 --- a/include/tag_string.h +++ b/include/tag_string.h @@ -51,6 +51,8 @@ public: void set(const std::string& str); void set(std::string&& str); + void read_payload(io::stream_reader& reader) override; + private: std::string value; }; diff --git a/src/tag_array.cpp b/src/tag_array.cpp index a944899402..f521768dd5 100644 --- a/src/tag_array.cpp +++ b/src/tag_array.cpp @@ -18,6 +18,8 @@ * along with libnbt++. If not, see . */ #include "tag_array.h" +#include "io/stream_reader.h" +#include namespace nbt { @@ -99,6 +101,41 @@ template auto tag_array::end() const -> const_iterator { return d template auto tag_array::cbegin() const -> const_iterator { return data.cbegin(); } template auto tag_array::cend() const -> const_iterator { return data.cend(); } +//Slightly different between byte_array and int_array +template<> +void tag_array::read_payload(io::stream_reader& reader) +{ + int32_t length; + reader.read_num(length); + if(length < 0 || !reader.get_istr()) + throw io::stream_reader::input_error("Error reading length of tag_byte_array"); + + data.resize(length); + reader.get_istr().read(reinterpret_cast(data.data()), length); + if(!reader.get_istr()) + throw io::stream_reader::input_error("Error reading contents of tag_byte_array"); +} + +template<> +void tag_array::read_payload(io::stream_reader& reader) +{ + int32_t length; + reader.read_num(length); + if(length < 0 || !reader.get_istr()) + throw io::stream_reader::input_error("Error reading length of tag_int_array"); + + data.clear(); + data.reserve(length); + for(int32_t i = 0; i < length; ++i) + { + int32_t val; + reader.read_num(val); + data.push_back(val); + } + if(!reader.get_istr()) + throw io::stream_reader::input_error("Error reading contents of tag_int_array"); +} + template bool operator==(const tag_array& lhs, const tag_array& rhs) { diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index d3eaf06440..f6aeea5e79 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -18,6 +18,8 @@ * along with libnbt++. If not, see . */ #include "tag_compound.h" +#include "io/stream_reader.h" +#include namespace nbt { @@ -95,6 +97,18 @@ auto tag_compound::end() const -> const_iterator { return tags.end(); } auto tag_compound::cbegin() const -> const_iterator { return tags.cbegin(); } auto tag_compound::cend() const -> const_iterator { return tags.cend(); } +void tag_compound::read_payload(io::stream_reader& reader) +{ + clear(); + tag_type tt; + while((tt = reader.read_type(true)) != tag_type::End) + { + std::string key = reader.read_string(); + auto tptr = reader.read_payload(tt); + tags.emplace(key, value(std::move(tptr))); + } +} + bool operator==(const tag_compound& lhs, const tag_compound& rhs) { return lhs.tags == rhs.tags; diff --git a/src/tag_list.cpp b/src/tag_list.cpp index 7900f9872e..c5d9eb5901 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -19,6 +19,8 @@ */ #include "tag_list.h" #include "nbt_tags.h" +#include "io/stream_reader.h" +#include namespace nbt { @@ -130,6 +132,22 @@ auto tag_list::end() const -> const_iterator { return tags.end(); } auto tag_list::cbegin() const -> const_iterator { return tags.cbegin(); } auto tag_list::cend() const -> const_iterator { return tags.cend(); } +void tag_list::read_payload(io::stream_reader& reader) +{ + tag_type lt = reader.read_type(); + + int32_t length; + reader.read_num(length); + if(length < 0 || !reader.get_istr()) + throw io::stream_reader::input_error("Error reading length of tag_list"); + + reset(lt); + tags.reserve(length); + + for(int32_t i = 0; i < length; ++i) + tags.emplace_back(reader.read_payload(lt)); +} + bool operator==(const tag_list& lhs, const tag_list& rhs) { return lhs.el_type_ == rhs.el_type_ && lhs.tags == rhs.tags; diff --git a/src/tag_primitive.cpp b/src/tag_primitive.cpp index 69bd9695bd..15fc87d752 100644 --- a/src/tag_primitive.cpp +++ b/src/tag_primitive.cpp @@ -18,7 +18,9 @@ * along with libnbt++. If not, see . */ #include "tag_primitive.h" +#include "io/stream_reader.h" #include +#include namespace nbt { @@ -62,6 +64,18 @@ T tag_primitive::get() const return value; } +template +void tag_primitive::read_payload(io::stream_reader& reader) +{ + reader.read_num(value); + if(!reader.get_istr()) + { + std::ostringstream str; + str << "Error reading " << type; + throw io::stream_reader::input_error(str.str()); + } +} + template bool operator==(const tag_primitive& lhs, const tag_primitive& rhs) { diff --git a/src/tag_string.cpp b/src/tag_string.cpp index 58635d5e0a..4671339561 100644 --- a/src/tag_string.cpp +++ b/src/tag_string.cpp @@ -18,6 +18,7 @@ * along with libnbt++. If not, see . */ #include "tag_string.h" +#include "io/stream_reader.h" namespace nbt { @@ -77,6 +78,11 @@ void tag_string::set(std::string&& str) value = std::move(str); } +void tag_string::read_payload(io::stream_reader& reader) +{ + value = reader.read_string(); +} + bool operator==(const tag_string& lhs, const tag_string& rhs) { return lhs.get() == rhs.get(); -- cgit 0.0.5-2-1-g0f52 From 51553817630748b1f8ca342db59b0834032222cb Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 5 Aug 2015 14:01:20 +0200 Subject: Fix read_test --- test/io/read_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/io/read_test.cpp b/test/io/read_test.cpp index 78a6ea345f..a2c6b48844 100644 --- a/test/io/read_test.cpp +++ b/test/io/read_test.cpp @@ -123,7 +123,7 @@ void test_read_bigtest() tag_byte_array byteArrayTest; for(int n = 0; n < 1000; ++n) byteArrayTest.push_back((n*n*255 + n*7) % 100); - ASSERT(comp.at("byteArrayTest") == byteArrayTest); + ASSERT(comp.at("byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...))") == byteArrayTest); ASSERT(comp.at("stringTest") == tag_string("HELLO WORLD THIS IS A TEST STRING \u00C5\u00C4\u00D6!")); -- cgit 0.0.5-2-1-g0f52 From 7f2dc607b7ba1192feeae00f4aeff50385e097ae Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 5 Aug 2015 14:37:07 +0200 Subject: Make tag_list::read_payload handle lists of tag_end --- include/tag.h | 5 ++++- include/tag_list.h | 4 ++++ src/tag_list.cpp | 18 +++++++++++++----- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/include/tag.h b/include/tag.h index 4b547e013c..58b99c082c 100644 --- a/include/tag.h +++ b/include/tag.h @@ -76,7 +76,10 @@ public: */ virtual tag& assign(tag&& rhs) = 0; - ///Reads the tag's payload from the stream + /** + * @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; /** diff --git a/include/tag_list.h b/include/tag_list.h index 324d2de6d8..dff89c4fcc 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -158,6 +158,10 @@ public: const_iterator cbegin() const; const_iterator cend() const; + /** + * @inheritdoc + * In case of a list of tag_end, the content type will be undetermined. + */ void read_payload(io::stream_reader& reader) override; /** diff --git a/src/tag_list.cpp b/src/tag_list.cpp index c5d9eb5901..3bcc42e8b6 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -134,18 +134,26 @@ auto tag_list::cend() const -> const_iterator { return tags.cend(); } void tag_list::read_payload(io::stream_reader& reader) { - tag_type lt = reader.read_type(); + tag_type lt = reader.read_type(true); int32_t length; reader.read_num(length); if(length < 0 || !reader.get_istr()) throw io::stream_reader::input_error("Error reading length of tag_list"); - reset(lt); - tags.reserve(length); + if(lt != tag_type::End) + { + reset(lt); + tags.reserve(length); - for(int32_t i = 0; i < length; ++i) - tags.emplace_back(reader.read_payload(lt)); + for(int32_t i = 0; i < length; ++i) + tags.emplace_back(reader.read_payload(lt)); + } + else + { + //In case of tag_end, ignore the length and leave the type undetermined + reset(tag_type::Null); + } } bool operator==(const tag_list& lhs, const tag_list& rhs) -- cgit 0.0.5-2-1-g0f52 From 9ac0ed01c014b75441601271c5ab0cf9b8ec4791 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 5 Aug 2015 14:47:19 +0200 Subject: Extend bigtest with tag_list (end) and tag_int_array --- test/CMakeLists.txt | 2 +- test/io/bigtest.nbt | Bin 515 -> 561 bytes test/io/bigtest_uncompr | Bin 0 -> 1601 bytes test/io/bigtest_uncompressed | Bin 1544 -> 0 bytes test/io/read_test.cpp | 10 +++++++--- 5 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 test/io/bigtest_uncompr delete mode 100644 test/io/bigtest_uncompressed diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ae8c7c03e0..09520c31bd 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -14,5 +14,5 @@ add_executable(read_test io/read_test.cpp) target_link_libraries(read_test nbt++) add_custom_command(TARGET read_test POST_BUILD COMMAND ${CMAKE_COMMAND} -E - copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/bigtest_uncompressed ${CMAKE_CURRENT_BINARY_DIR}) + copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/bigtest_uncompr ${CMAKE_CURRENT_BINARY_DIR}) add_test(read_test read_test) diff --git a/test/io/bigtest.nbt b/test/io/bigtest.nbt index 945b86f562..de1a91268b 100644 Binary files a/test/io/bigtest.nbt and b/test/io/bigtest.nbt differ diff --git a/test/io/bigtest_uncompr b/test/io/bigtest_uncompr new file mode 100644 index 0000000000..dc1c9c113f Binary files /dev/null and b/test/io/bigtest_uncompr differ diff --git a/test/io/bigtest_uncompressed b/test/io/bigtest_uncompressed deleted file mode 100644 index a2021df7cb..0000000000 Binary files a/test/io/bigtest_uncompressed and /dev/null differ diff --git a/test/io/read_test.cpp b/test/io/read_test.cpp index a2c6b48844..d057a686f9 100644 --- a/test/io/read_test.cpp +++ b/test/io/read_test.cpp @@ -99,7 +99,8 @@ void test_stream_reader_little() void test_read_bigtest() { - std::ifstream file("bigtest_uncompressed", std::ios::binary); + //Uses an extended variant of Notch's original bigtest file + std::ifstream file("bigtest_uncompr", std::ios::binary); ASSERT(file); nbt::io::stream_reader reader(file); @@ -110,7 +111,7 @@ void test_read_bigtest() ASSERT(tagptr->get_type() == tag_type::Compound); const tag_compound& comp = static_cast(*tagptr); - ASSERT(comp.size() == 11); + ASSERT(comp.size() == 13); ASSERT(comp.at("byteTest") == tag_byte(127)); ASSERT(comp.at("shortTest") == tag_short(32767)); @@ -131,13 +132,16 @@ void test_read_bigtest() {{"created-on", tag_long(1264099775885)}, {"name", "Compound tag #0"}}, {{"created-on", tag_long(1264099775885)}, {"name", "Compound tag #1"}} })); - ASSERT(comp.at("listTest (long)") == tag_list::of({11, 12, 13, 14, 15})); + ASSERT(comp.at("listTest (end)") == tag_list()); ASSERT((comp.at("nested compound test") == tag_compound{ {"egg", tag_compound{{"value", 0.5f}, {"name", "Eggbert"}}}, {"ham", tag_compound{{"value", 0.75f}, {"name", "Hampus"}}} })); + + ASSERT(comp.at("intArrayTest") == tag_int_array( + {0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f})); } int main() -- cgit 0.0.5-2-1-g0f52 From b79ebd5096321cf8051f483bc20ac11fab9f9d6c Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 5 Aug 2015 16:50:30 +0200 Subject: Remove unnecessary suffixes --- test/endian_str_test.cpp | 38 +++++++++++++++++++------------------- test/io/read_test.cpp | 8 ++++---- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/test/endian_str_test.cpp b/test/endian_str_test.cpp index 1e8b14606b..3f7f481777 100644 --- a/test/endian_str_test.cpp +++ b/test/endian_str_test.cpp @@ -30,15 +30,15 @@ void test_uint() write_little(str, uint8_t (0x01)); write_little(str, uint16_t(0x0102)); - write (str, uint32_t(0x01020304UL), endian::little); - write_little(str, uint64_t(0x0102030405060708ULL)); + write (str, uint32_t(0x01020304), endian::little); + write_little(str, uint64_t(0x0102030405060708)); write_big (str, uint8_t (0x09)); write_big (str, uint16_t(0x090A)); - write_big (str, uint32_t(0x090A0B0CUL)); - write (str, uint64_t(0x090A0B0C0D0E0F10ULL), endian::big); + write_big (str, uint32_t(0x090A0B0C)); + write (str, uint64_t(0x090A0B0C0D0E0F10), endian::big); - const char expected[] = { + const char expected[] { 1, 2, 1, 4, 3, 2, 1, @@ -61,18 +61,18 @@ void test_uint() read_little(str, u16); ASSERT(u16 == 0x0102); read_little(str, u32); - ASSERT(u32 == 0x01020304UL); + ASSERT(u32 == 0x01020304); read(str, u64, endian::little); - ASSERT(u64 == 0x0102030405060708ULL); + ASSERT(u64 == 0x0102030405060708); read_big(str, u8); ASSERT(u8 == 0x09); read_big(str, u16); ASSERT(u16 == 0x090A); read(str, u32, endian::big); - ASSERT(u32 == 0x090A0B0CUL); + ASSERT(u32 == 0x090A0B0C); read_big(str, u64); - ASSERT(u64 == 0x090A0B0C0D0E0F10ULL); + ASSERT(u64 == 0x090A0B0C0D0E0F10); ASSERT(str); //Check if stream has failed } @@ -83,15 +83,15 @@ void test_sint() write_little(str, int8_t (-0x01)); write_little(str, int16_t(-0x0102)); - write_little(str, int32_t(-0x01020304L)); - write (str, int64_t(-0x0102030405060708LL), endian::little); + write_little(str, int32_t(-0x01020304)); + write (str, int64_t(-0x0102030405060708), endian::little); write_big (str, int8_t (-0x09)); write_big (str, int16_t(-0x090A)); - write (str, int32_t(-0x090A0B0CL), endian::big); - write_big (str, int64_t(-0x090A0B0C0D0E0F10LL)); + write (str, int32_t(-0x090A0B0C), endian::big); + write_big (str, int64_t(-0x090A0B0C0D0E0F10)); - const char expected[] = { //meh, stupid narrowing conversions + const char expected[] { //meh, stupid narrowing conversions '\xFF', '\xFE', '\xFE', '\xFC', '\xFC', '\xFD', '\xFE', @@ -114,18 +114,18 @@ void test_sint() read_little(str, i16); ASSERT(i16 == -0x0102); read(str, i32, endian::little); - ASSERT(i32 == -0x01020304L); + ASSERT(i32 == -0x01020304); read_little(str, i64); - ASSERT(i64 == -0x0102030405060708LL); + ASSERT(i64 == -0x0102030405060708); read_big(str, i8); ASSERT(i8 == -0x09); read_big(str, i16); ASSERT(i16 == -0x090A); read_big(str, i32); - ASSERT(i32 == -0x090A0B0CL); + ASSERT(i32 == -0x090A0B0C); read(str, i64, endian::big); - ASSERT(i64 == -0x090A0B0C0D0E0F10LL); + ASSERT(i64 == -0x090A0B0C0D0E0F10); ASSERT(str); //Check if stream has failed } @@ -144,7 +144,7 @@ void test_float() write_big (str, fconst); write_big (str, dconst); - const char expected[] = { + const char expected[] { '\x01', '\xEF', '\xCD', '\xAB', '\x05', '\x04', '\x03', '\x02', '\x01', '\xEF', '\xCD', '\xAB', diff --git a/test/io/read_test.cpp b/test/io/read_test.cpp index d057a686f9..aa731e1e55 100644 --- a/test/io/read_test.cpp +++ b/test/io/read_test.cpp @@ -51,7 +51,7 @@ void test_stream_reader_big() int32_t i; reader.read_num(i); - ASSERT(i == 0x0a0b0c0dL); + ASSERT(i == 0x0a0b0c0d); ASSERT(reader.read_string() == "foobar"); @@ -89,7 +89,7 @@ void test_stream_reader_little() int64_t i; reader.read_num(i); - ASSERT(i == 0x0d0c0b0a09080706LL); + ASSERT(i == 0x0d0c0b0a09080706); ASSERT(reader.read_string() == "foobar"); @@ -115,8 +115,8 @@ void test_read_bigtest() ASSERT(comp.at("byteTest") == tag_byte(127)); ASSERT(comp.at("shortTest") == tag_short(32767)); - ASSERT(comp.at("intTest") == tag_int(2147483647L)); - ASSERT(comp.at("longTest") == tag_long(9223372036854775807LL)); + ASSERT(comp.at("intTest") == tag_int(2147483647)); + ASSERT(comp.at("longTest") == tag_long(9223372036854775807)); ASSERT(comp.at("floatTest") == tag_float(std::stof("0xff1832p-25"))); //0.4982315 ASSERT(comp.at("doubleTest") == tag_double(std::stod("0x1f8f6bbbff6a5ep-54"))); //0.493128713218231 -- cgit 0.0.5-2-1-g0f52 From 7626425b2c6299b38865aa10c8d912e946f5f88f Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 5 Aug 2015 17:06:40 +0200 Subject: Add tag::as method --- include/tag.h | 23 +++++++++++++++++++++++ include/value.h | 8 +++----- test/io/read_test.cpp | 2 +- test/nbttest.cpp | 3 +++ 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/include/tag.h b/include/tag.h index 58b99c082c..569da446d6 100644 --- a/include/tag.h +++ b/include/tag.h @@ -70,6 +70,15 @@ public: virtual std::unique_ptr move_clone() && = 0; std::unique_ptr 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 + T& as(); + template + 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 @@ -101,6 +110,20 @@ private: std::ostream& operator<<(std::ostream& os, tag_type tt); +template +T& tag::as() +{ + static_assert(std::is_base_of::value, "T must be a subclass of tag"); + return dynamic_cast(*this); +} + +template +const T& tag::as() const +{ + static_assert(std::is_base_of::value, "T must be a subclass of tag"); + return dynamic_cast(*this); +} + } #endif // TAG_H_INCLUDED diff --git a/include/value.h b/include/value.h index c017bcbd27..4865c8fd1c 100644 --- a/include/value.h +++ b/include/value.h @@ -93,7 +93,7 @@ public: const tag& get() const; /** - * @brief Returns the contained tag as an instance of T + * @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 @@ -207,15 +207,13 @@ private: template T& value::as() { - static_assert(std::is_base_of::value, "T must be a subclass of tag"); - return dynamic_cast(get()); + return tag_->as(); } template const T& value::as() const { - static_assert(std::is_base_of::value, "T must be a subclass of tag"); - return dynamic_cast(get()); + return tag_->as();; } } diff --git a/test/io/read_test.cpp b/test/io/read_test.cpp index aa731e1e55..d3ea8b7765 100644 --- a/test/io/read_test.cpp +++ b/test/io/read_test.cpp @@ -109,7 +109,7 @@ void test_read_bigtest() auto tagptr = reader.read_payload(tag_type::Compound); ASSERT(tagptr->get_type() == tag_type::Compound); - const tag_compound& comp = static_cast(*tagptr); + const tag_compound& comp = tagptr->as(); ASSERT(comp.size() == 13); diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 15b0c67fba..b041d44aa8 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -54,6 +54,9 @@ void test_tag() tstr.assign(tag_string("bar")); EXPECT_EXCEPTION(tstr.assign(tag_int(6)), std::bad_cast); ASSERT(tstr.get() == "bar"); + + ASSERT(&tstr.as() == &tstr); + EXPECT_EXCEPTION(tstr.as(), std::bad_cast); std::clog << "test_tag passed" << std::endl; } -- cgit 0.0.5-2-1-g0f52 From b5cff8cc5c72c8d895e97075c0ab0f223cd81b09 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 6 Aug 2015 10:27:06 +0200 Subject: Move input_error out from stream_reader --- include/io/stream_reader.h | 12 ++++++------ src/tag_array.cpp | 8 ++++---- src/tag_list.cpp | 2 +- src/tag_primitive.cpp | 2 +- test/io/read_test.cpp | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/include/io/stream_reader.h b/include/io/stream_reader.h index 056d14e9dd..7d97824927 100644 --- a/include/io/stream_reader.h +++ b/include/io/stream_reader.h @@ -31,18 +31,18 @@ namespace nbt namespace io { +///Exception that gets thrown when reading is not successful +class input_error : public std::runtime_error +{ + using std::runtime_error::runtime_error; +}; + /** * @brief Helper class for reading NBT tags from input streams */ class stream_reader { public: - ///Exception that gets thrown when reading is not successful - class input_error : public std::runtime_error - { - using std::runtime_error::runtime_error; - }; - /** * @param is the stream to read from * @param e the byte order of the source data. The Java edition diff --git a/src/tag_array.cpp b/src/tag_array.cpp index f521768dd5..94b8ffe7ac 100644 --- a/src/tag_array.cpp +++ b/src/tag_array.cpp @@ -108,12 +108,12 @@ void tag_array::read_payload(io::stream_reader& reader) int32_t length; reader.read_num(length); if(length < 0 || !reader.get_istr()) - throw io::stream_reader::input_error("Error reading length of tag_byte_array"); + throw io::input_error("Error reading length of tag_byte_array"); data.resize(length); reader.get_istr().read(reinterpret_cast(data.data()), length); if(!reader.get_istr()) - throw io::stream_reader::input_error("Error reading contents of tag_byte_array"); + throw io::input_error("Error reading contents of tag_byte_array"); } template<> @@ -122,7 +122,7 @@ void tag_array::read_payload(io::stream_reader& reader) int32_t length; reader.read_num(length); if(length < 0 || !reader.get_istr()) - throw io::stream_reader::input_error("Error reading length of tag_int_array"); + throw io::input_error("Error reading length of tag_int_array"); data.clear(); data.reserve(length); @@ -133,7 +133,7 @@ void tag_array::read_payload(io::stream_reader& reader) data.push_back(val); } if(!reader.get_istr()) - throw io::stream_reader::input_error("Error reading contents of tag_int_array"); + throw io::input_error("Error reading contents of tag_int_array"); } template diff --git a/src/tag_list.cpp b/src/tag_list.cpp index 3bcc42e8b6..53c1a8e392 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -139,7 +139,7 @@ void tag_list::read_payload(io::stream_reader& reader) int32_t length; reader.read_num(length); if(length < 0 || !reader.get_istr()) - throw io::stream_reader::input_error("Error reading length of tag_list"); + throw io::input_error("Error reading length of tag_list"); if(lt != tag_type::End) { diff --git a/src/tag_primitive.cpp b/src/tag_primitive.cpp index 15fc87d752..358d946e6a 100644 --- a/src/tag_primitive.cpp +++ b/src/tag_primitive.cpp @@ -72,7 +72,7 @@ void tag_primitive::read_payload(io::stream_reader& reader) { std::ostringstream str; str << "Error reading " << type; - throw io::stream_reader::input_error(str.str()); + throw io::input_error(str.str()); } } diff --git a/test/io/read_test.cpp b/test/io/read_test.cpp index d3ea8b7765..a6de378e1d 100644 --- a/test/io/read_test.cpp +++ b/test/io/read_test.cpp @@ -55,13 +55,13 @@ void test_stream_reader_big() ASSERT(reader.read_string() == "foobar"); - EXPECT_EXCEPTION(reader.read_type(false), io::stream_reader::input_error); + EXPECT_EXCEPTION(reader.read_type(false), io::input_error); ASSERT(!is); is.clear(); //Test for invalid tag type 12 is.str("\x0c"); - EXPECT_EXCEPTION(reader.read_type(), io::stream_reader::input_error); + EXPECT_EXCEPTION(reader.read_type(), io::input_error); ASSERT(!is); is.clear(); @@ -93,7 +93,7 @@ void test_stream_reader_little() ASSERT(reader.read_string() == "foobar"); - EXPECT_EXCEPTION(reader.read_string(), io::stream_reader::input_error); + EXPECT_EXCEPTION(reader.read_string(), io::input_error); ASSERT(!is); } -- cgit 0.0.5-2-1-g0f52 From 9993ebf57fa5822d4d573b359220610962b4a78b Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 6 Aug 2015 10:38:50 +0200 Subject: Move the key for reading compounds as well --- src/tag_compound.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index f6aeea5e79..3821ba8f98 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -105,7 +105,7 @@ void tag_compound::read_payload(io::stream_reader& reader) { std::string key = reader.read_string(); auto tptr = reader.read_payload(tt); - tags.emplace(key, value(std::move(tptr))); + tags.emplace(std::move(key), value(std::move(tptr))); } } -- cgit 0.0.5-2-1-g0f52 From 7e3bd6ce1cc07dab0b5f7aaf30d213af802dbd0a Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 6 Aug 2015 10:49:08 +0200 Subject: Add stream_reader::read_tag method --- include/io/stream_reader.h | 7 +++++++ src/io/stream_reader.cpp | 8 ++++++++ test/io/read_test.cpp | 9 ++++----- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/include/io/stream_reader.h b/include/io/stream_reader.h index 7d97824927..75dba6dddb 100644 --- a/include/io/stream_reader.h +++ b/include/io/stream_reader.h @@ -25,6 +25,7 @@ #include #include #include +#include namespace nbt { @@ -55,6 +56,12 @@ public: ///Returns the byte order endian::endian get_endian() const; + /** + * @brief Reads a named tag from the stream + * @throw input_error on failure + */ + std::pair> read_tag(); + /** * @brief Reads a tag of the given type without name from the stream * @throw input_error on failure diff --git a/src/io/stream_reader.cpp b/src/io/stream_reader.cpp index ff0fe183ea..541a416d83 100644 --- a/src/io/stream_reader.cpp +++ b/src/io/stream_reader.cpp @@ -39,6 +39,14 @@ endian::endian stream_reader::get_endian() const return endian; } +std::pair> stream_reader::read_tag() +{ + tag_type type = read_type(); + std::string key = read_string(); + std::unique_ptr t = read_payload(type); + return {std::move(key), std::move(t)}; +} + std::unique_ptr stream_reader::read_payload(tag_type type) { std::unique_ptr t = tag::create(type); diff --git a/test/io/read_test.cpp b/test/io/read_test.cpp index a6de378e1d..0b29483f1e 100644 --- a/test/io/read_test.cpp +++ b/test/io/read_test.cpp @@ -104,12 +104,11 @@ void test_read_bigtest() ASSERT(file); nbt::io::stream_reader reader(file); - ASSERT(reader.read_type() == tag_type::Compound); - ASSERT(reader.read_string() == "Level"); - auto tagptr = reader.read_payload(tag_type::Compound); + auto pair = reader.read_tag(); + ASSERT(pair.first == "Level"); - ASSERT(tagptr->get_type() == tag_type::Compound); - const tag_compound& comp = tagptr->as(); + ASSERT(pair.second->get_type() == tag_type::Compound); + const tag_compound& comp = pair.second->as(); ASSERT(comp.size() == 13); -- cgit 0.0.5-2-1-g0f52 From ebf36da97fb36c3afb66a1990ab9c1d5ecd559c0 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 6 Aug 2015 13:12:14 +0200 Subject: Add "tag_" prefix to tag type --- src/tag_primitive.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tag_primitive.cpp b/src/tag_primitive.cpp index 358d946e6a..dd07fea662 100644 --- a/src/tag_primitive.cpp +++ b/src/tag_primitive.cpp @@ -71,7 +71,7 @@ void tag_primitive::read_payload(io::stream_reader& reader) if(!reader.get_istr()) { std::ostringstream str; - str << "Error reading " << type; + str << "Error reading tag_" << type; throw io::input_error(str.str()); } } -- cgit 0.0.5-2-1-g0f52 From f39de6ad5bb4689ec13877508b55156d2d67ce79 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 6 Aug 2015 13:31:10 +0200 Subject: Add *.bak to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a7f172c739..05dee84b0f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.depend *.layout *.cbtemp +*.bak /bin /lib /obj -- cgit 0.0.5-2-1-g0f52 From e9dd438bd78b10c56e47479db7c7b3e2a3e9ab3a Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 6 Aug 2015 13:33:23 +0200 Subject: Add test for read failure (EOF in tag) --- test/CMakeLists.txt | 4 ++-- test/io/errortest_eof1 | Bin 0 -> 43 bytes test/io/read_test.cpp | 13 +++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 test/io/errortest_eof1 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 09520c31bd..63e6f20ed7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -13,6 +13,6 @@ add_test(endian_str_test endian_str_test) add_executable(read_test io/read_test.cpp) target_link_libraries(read_test nbt++) add_custom_command(TARGET read_test POST_BUILD - COMMAND ${CMAKE_COMMAND} -E - copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/bigtest_uncompr ${CMAKE_CURRENT_BINARY_DIR}) + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/bigtest_uncompr ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/errortest_eof1 ${CMAKE_CURRENT_BINARY_DIR}) add_test(read_test read_test) diff --git a/test/io/errortest_eof1 b/test/io/errortest_eof1 new file mode 100644 index 0000000000..abb7ac564f Binary files /dev/null and b/test/io/errortest_eof1 differ diff --git a/test/io/read_test.cpp b/test/io/read_test.cpp index 0b29483f1e..d13402c2ea 100644 --- a/test/io/read_test.cpp +++ b/test/io/read_test.cpp @@ -143,9 +143,22 @@ void test_read_bigtest() {0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f})); } +void test_read_errors() +{ + std::ifstream file; + nbt::io::stream_reader reader(file); + + //EOF within a tag_double payload + file.open("errortest_eof1", std::ios::binary); + ASSERT(file); + EXPECT_EXCEPTION(reader.read_tag(), io::input_error); + ASSERT(!file); +} + int main() { test_stream_reader_big(); test_stream_reader_little(); test_read_bigtest(); + test_read_errors(); } -- cgit 0.0.5-2-1-g0f52 From 8565a07570ccd38209dd593c1116e917cd7c7230 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 6 Aug 2015 13:34:32 +0200 Subject: Add test for EOF within key --- test/CMakeLists.txt | 3 ++- test/io/errortest_eof2 | Bin 0 -> 94 bytes test/io/read_test.cpp | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 test/io/errortest_eof2 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 63e6f20ed7..c24b7b7c23 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -14,5 +14,6 @@ add_executable(read_test io/read_test.cpp) target_link_libraries(read_test nbt++) add_custom_command(TARGET read_test POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/bigtest_uncompr ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/errortest_eof1 ${CMAKE_CURRENT_BINARY_DIR}) + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/errortest_eof1 ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/errortest_eof2 ${CMAKE_CURRENT_BINARY_DIR}) add_test(read_test read_test) diff --git a/test/io/errortest_eof2 b/test/io/errortest_eof2 new file mode 100644 index 0000000000..1e9a503484 Binary files /dev/null and b/test/io/errortest_eof2 differ diff --git a/test/io/read_test.cpp b/test/io/read_test.cpp index d13402c2ea..ab1a46cc97 100644 --- a/test/io/read_test.cpp +++ b/test/io/read_test.cpp @@ -153,6 +153,13 @@ void test_read_errors() ASSERT(file); EXPECT_EXCEPTION(reader.read_tag(), io::input_error); ASSERT(!file); + + //EOF within a key in a compound + file.close(); + file.open("errortest_eof2", std::ios::binary); + ASSERT(file); + EXPECT_EXCEPTION(reader.read_tag(), io::input_error); + ASSERT(!file); } int main() -- cgit 0.0.5-2-1-g0f52 From 16a9df1556afc1650bbc883303ab0ee283710ce9 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 6 Aug 2015 13:47:22 +0200 Subject: Add test for missing tag_end --- test/CMakeLists.txt | 3 ++- test/io/errortest_noend | Bin 0 -> 48 bytes test/io/read_test.cpp | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 test/io/errortest_noend diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c24b7b7c23..cffce2ad7b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -15,5 +15,6 @@ target_link_libraries(read_test nbt++) add_custom_command(TARGET read_test POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/bigtest_uncompr ${CMAKE_CURRENT_BINARY_DIR} COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/errortest_eof1 ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/errortest_eof2 ${CMAKE_CURRENT_BINARY_DIR}) + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/errortest_eof2 ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/errortest_noend ${CMAKE_CURRENT_BINARY_DIR}) add_test(read_test read_test) diff --git a/test/io/errortest_noend b/test/io/errortest_noend new file mode 100644 index 0000000000..d90614628c Binary files /dev/null and b/test/io/errortest_noend differ diff --git a/test/io/read_test.cpp b/test/io/read_test.cpp index ab1a46cc97..85970fc069 100644 --- a/test/io/read_test.cpp +++ b/test/io/read_test.cpp @@ -160,6 +160,13 @@ void test_read_errors() ASSERT(file); EXPECT_EXCEPTION(reader.read_tag(), io::input_error); ASSERT(!file); + + //Missing tag_end + file.close(); + file.open("errortest_noend", std::ios::binary); + ASSERT(file); + EXPECT_EXCEPTION(reader.read_tag(), io::input_error); + ASSERT(!file); } int main() -- cgit 0.0.5-2-1-g0f52 From b3fedb345162c1242b22446792aa80b6c10c7885 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 6 Aug 2015 13:49:13 +0200 Subject: Change directory structure of read tests --- test/CMakeLists.txt | 8 +- test/io/bigtest.nbt | Bin 561 -> 0 bytes test/io/bigtest_uncompr | Bin 1601 -> 0 bytes test/io/errortest_eof1 | Bin 43 -> 0 bytes test/io/errortest_eof2 | Bin 94 -> 0 bytes test/io/errortest_noend | Bin 48 -> 0 bytes test/io/read_test.cpp | 178 ----------------------------------------- test/read_test.cpp | 178 +++++++++++++++++++++++++++++++++++++++++ test/testfiles/bigtest.nbt | Bin 0 -> 561 bytes test/testfiles/bigtest_uncompr | Bin 0 -> 1601 bytes test/testfiles/errortest_eof1 | Bin 0 -> 43 bytes test/testfiles/errortest_eof2 | Bin 0 -> 94 bytes test/testfiles/errortest_noend | Bin 0 -> 48 bytes 13 files changed, 181 insertions(+), 183 deletions(-) delete mode 100644 test/io/bigtest.nbt delete mode 100644 test/io/bigtest_uncompr delete mode 100644 test/io/errortest_eof1 delete mode 100644 test/io/errortest_eof2 delete mode 100644 test/io/errortest_noend delete mode 100644 test/io/read_test.cpp create mode 100644 test/read_test.cpp create mode 100644 test/testfiles/bigtest.nbt create mode 100644 test/testfiles/bigtest_uncompr create mode 100644 test/testfiles/errortest_eof1 create mode 100644 test/testfiles/errortest_eof2 create mode 100644 test/testfiles/errortest_noend diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cffce2ad7b..af5dd4532b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -10,11 +10,9 @@ add_executable(endian_str_test endian_str_test.cpp) target_link_libraries(endian_str_test nbt++) add_test(endian_str_test endian_str_test) -add_executable(read_test io/read_test.cpp) +add_executable(read_test read_test.cpp) target_link_libraries(read_test nbt++) add_custom_command(TARGET read_test POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/bigtest_uncompr ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/errortest_eof1 ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/errortest_eof2 ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/io/errortest_noend ${CMAKE_CURRENT_BINARY_DIR}) + COMMAND ${CMAKE_COMMAND} -E + copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/testfiles ${CMAKE_CURRENT_BINARY_DIR}) add_test(read_test read_test) diff --git a/test/io/bigtest.nbt b/test/io/bigtest.nbt deleted file mode 100644 index de1a91268b..0000000000 Binary files a/test/io/bigtest.nbt and /dev/null differ diff --git a/test/io/bigtest_uncompr b/test/io/bigtest_uncompr deleted file mode 100644 index dc1c9c113f..0000000000 Binary files a/test/io/bigtest_uncompr and /dev/null differ diff --git a/test/io/errortest_eof1 b/test/io/errortest_eof1 deleted file mode 100644 index abb7ac564f..0000000000 Binary files a/test/io/errortest_eof1 and /dev/null differ diff --git a/test/io/errortest_eof2 b/test/io/errortest_eof2 deleted file mode 100644 index 1e9a503484..0000000000 Binary files a/test/io/errortest_eof2 and /dev/null differ diff --git a/test/io/errortest_noend b/test/io/errortest_noend deleted file mode 100644 index d90614628c..0000000000 Binary files a/test/io/errortest_noend and /dev/null differ diff --git a/test/io/read_test.cpp b/test/io/read_test.cpp deleted file mode 100644 index 85970fc069..0000000000 --- a/test/io/read_test.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/* - * libnbt++ - A library for the Minecraft Named Binary Tag format. - * Copyright (C) 2013, 2015 ljfa-ag - * - * This file is part of libnbt++. - * - * libnbt++ is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * libnbt++ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with libnbt++. If not, see . - */ -#include "microtest.h" -#include "io/stream_reader.h" -#include "nbt_tags.h" -#include -#include - -using namespace nbt; - -void test_stream_reader_big() -{ - const char input[] { - 1, //tag_type::Byte - 0, //tag_type::End - 11, //tag_type::Int_Array - - 0x0a, 0x0b, 0x0c, 0x0d, //0x0a0b0c0d in Big Endian - - 0x00, 0x06, //String length in Big Endian - 'f', 'o', 'o', 'b', 'a', 'r', - - 0 //tag_type::End (invalid with allow_end = false) - }; - std::istringstream is(std::string(input, sizeof input)); - nbt::io::stream_reader reader(is); - - ASSERT(&reader.get_istr() == &is); - ASSERT(reader.get_endian() == endian::big); - - ASSERT(reader.read_type() == tag_type::Byte); - ASSERT(reader.read_type(true) == tag_type::End); - ASSERT(reader.read_type(false) == tag_type::Int_Array); - - int32_t i; - reader.read_num(i); - ASSERT(i == 0x0a0b0c0d); - - ASSERT(reader.read_string() == "foobar"); - - EXPECT_EXCEPTION(reader.read_type(false), io::input_error); - ASSERT(!is); - is.clear(); - - //Test for invalid tag type 12 - is.str("\x0c"); - EXPECT_EXCEPTION(reader.read_type(), io::input_error); - ASSERT(!is); - is.clear(); - - //Test for unexpcted EOF on numbers (input too short for int32_t) - is.str("\x03\x04"); - reader.read_num(i); - ASSERT(!is); -} - -void test_stream_reader_little() -{ - const char input[] { - 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, //0x0d0c0b0a09080706 in Little Endian - - 0x06, 0x00, //String length in Little Endian - 'f', 'o', 'o', 'b', 'a', 'r', - - 0x10, 0x00, //String length (intentionally too large) - 'a', 'b', 'c', 'd' //unexpected EOF - }; - std::istringstream is(std::string(input, sizeof input)); - nbt::io::stream_reader reader(is, endian::little); - - ASSERT(reader.get_endian() == endian::little); - - int64_t i; - reader.read_num(i); - ASSERT(i == 0x0d0c0b0a09080706); - - ASSERT(reader.read_string() == "foobar"); - - EXPECT_EXCEPTION(reader.read_string(), io::input_error); - ASSERT(!is); -} - -void test_read_bigtest() -{ - //Uses an extended variant of Notch's original bigtest file - std::ifstream file("bigtest_uncompr", std::ios::binary); - ASSERT(file); - nbt::io::stream_reader reader(file); - - auto pair = reader.read_tag(); - ASSERT(pair.first == "Level"); - - ASSERT(pair.second->get_type() == tag_type::Compound); - const tag_compound& comp = pair.second->as(); - - ASSERT(comp.size() == 13); - - ASSERT(comp.at("byteTest") == tag_byte(127)); - ASSERT(comp.at("shortTest") == tag_short(32767)); - ASSERT(comp.at("intTest") == tag_int(2147483647)); - ASSERT(comp.at("longTest") == tag_long(9223372036854775807)); - ASSERT(comp.at("floatTest") == tag_float(std::stof("0xff1832p-25"))); //0.4982315 - ASSERT(comp.at("doubleTest") == tag_double(std::stod("0x1f8f6bbbff6a5ep-54"))); //0.493128713218231 - - //From bigtest.nbt: "the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...)" - tag_byte_array byteArrayTest; - for(int n = 0; n < 1000; ++n) - byteArrayTest.push_back((n*n*255 + n*7) % 100); - ASSERT(comp.at("byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...))") == byteArrayTest); - - ASSERT(comp.at("stringTest") == tag_string("HELLO WORLD THIS IS A TEST STRING \u00C5\u00C4\u00D6!")); - - ASSERT(comp.at("listTest (compound)") == tag_list::of({ - {{"created-on", tag_long(1264099775885)}, {"name", "Compound tag #0"}}, - {{"created-on", tag_long(1264099775885)}, {"name", "Compound tag #1"}} - })); - ASSERT(comp.at("listTest (long)") == tag_list::of({11, 12, 13, 14, 15})); - ASSERT(comp.at("listTest (end)") == tag_list()); - - ASSERT((comp.at("nested compound test") == tag_compound{ - {"egg", tag_compound{{"value", 0.5f}, {"name", "Eggbert"}}}, - {"ham", tag_compound{{"value", 0.75f}, {"name", "Hampus"}}} - })); - - ASSERT(comp.at("intArrayTest") == tag_int_array( - {0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f})); -} - -void test_read_errors() -{ - std::ifstream file; - nbt::io::stream_reader reader(file); - - //EOF within a tag_double payload - file.open("errortest_eof1", std::ios::binary); - ASSERT(file); - EXPECT_EXCEPTION(reader.read_tag(), io::input_error); - ASSERT(!file); - - //EOF within a key in a compound - file.close(); - file.open("errortest_eof2", std::ios::binary); - ASSERT(file); - EXPECT_EXCEPTION(reader.read_tag(), io::input_error); - ASSERT(!file); - - //Missing tag_end - file.close(); - file.open("errortest_noend", std::ios::binary); - ASSERT(file); - EXPECT_EXCEPTION(reader.read_tag(), io::input_error); - ASSERT(!file); -} - -int main() -{ - test_stream_reader_big(); - test_stream_reader_little(); - test_read_bigtest(); - test_read_errors(); -} diff --git a/test/read_test.cpp b/test/read_test.cpp new file mode 100644 index 0000000000..85970fc069 --- /dev/null +++ b/test/read_test.cpp @@ -0,0 +1,178 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include "microtest.h" +#include "io/stream_reader.h" +#include "nbt_tags.h" +#include +#include + +using namespace nbt; + +void test_stream_reader_big() +{ + const char input[] { + 1, //tag_type::Byte + 0, //tag_type::End + 11, //tag_type::Int_Array + + 0x0a, 0x0b, 0x0c, 0x0d, //0x0a0b0c0d in Big Endian + + 0x00, 0x06, //String length in Big Endian + 'f', 'o', 'o', 'b', 'a', 'r', + + 0 //tag_type::End (invalid with allow_end = false) + }; + std::istringstream is(std::string(input, sizeof input)); + nbt::io::stream_reader reader(is); + + ASSERT(&reader.get_istr() == &is); + ASSERT(reader.get_endian() == endian::big); + + ASSERT(reader.read_type() == tag_type::Byte); + ASSERT(reader.read_type(true) == tag_type::End); + ASSERT(reader.read_type(false) == tag_type::Int_Array); + + int32_t i; + reader.read_num(i); + ASSERT(i == 0x0a0b0c0d); + + ASSERT(reader.read_string() == "foobar"); + + EXPECT_EXCEPTION(reader.read_type(false), io::input_error); + ASSERT(!is); + is.clear(); + + //Test for invalid tag type 12 + is.str("\x0c"); + EXPECT_EXCEPTION(reader.read_type(), io::input_error); + ASSERT(!is); + is.clear(); + + //Test for unexpcted EOF on numbers (input too short for int32_t) + is.str("\x03\x04"); + reader.read_num(i); + ASSERT(!is); +} + +void test_stream_reader_little() +{ + const char input[] { + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, //0x0d0c0b0a09080706 in Little Endian + + 0x06, 0x00, //String length in Little Endian + 'f', 'o', 'o', 'b', 'a', 'r', + + 0x10, 0x00, //String length (intentionally too large) + 'a', 'b', 'c', 'd' //unexpected EOF + }; + std::istringstream is(std::string(input, sizeof input)); + nbt::io::stream_reader reader(is, endian::little); + + ASSERT(reader.get_endian() == endian::little); + + int64_t i; + reader.read_num(i); + ASSERT(i == 0x0d0c0b0a09080706); + + ASSERT(reader.read_string() == "foobar"); + + EXPECT_EXCEPTION(reader.read_string(), io::input_error); + ASSERT(!is); +} + +void test_read_bigtest() +{ + //Uses an extended variant of Notch's original bigtest file + std::ifstream file("bigtest_uncompr", std::ios::binary); + ASSERT(file); + nbt::io::stream_reader reader(file); + + auto pair = reader.read_tag(); + ASSERT(pair.first == "Level"); + + ASSERT(pair.second->get_type() == tag_type::Compound); + const tag_compound& comp = pair.second->as(); + + ASSERT(comp.size() == 13); + + ASSERT(comp.at("byteTest") == tag_byte(127)); + ASSERT(comp.at("shortTest") == tag_short(32767)); + ASSERT(comp.at("intTest") == tag_int(2147483647)); + ASSERT(comp.at("longTest") == tag_long(9223372036854775807)); + ASSERT(comp.at("floatTest") == tag_float(std::stof("0xff1832p-25"))); //0.4982315 + ASSERT(comp.at("doubleTest") == tag_double(std::stod("0x1f8f6bbbff6a5ep-54"))); //0.493128713218231 + + //From bigtest.nbt: "the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...)" + tag_byte_array byteArrayTest; + for(int n = 0; n < 1000; ++n) + byteArrayTest.push_back((n*n*255 + n*7) % 100); + ASSERT(comp.at("byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...))") == byteArrayTest); + + ASSERT(comp.at("stringTest") == tag_string("HELLO WORLD THIS IS A TEST STRING \u00C5\u00C4\u00D6!")); + + ASSERT(comp.at("listTest (compound)") == tag_list::of({ + {{"created-on", tag_long(1264099775885)}, {"name", "Compound tag #0"}}, + {{"created-on", tag_long(1264099775885)}, {"name", "Compound tag #1"}} + })); + ASSERT(comp.at("listTest (long)") == tag_list::of({11, 12, 13, 14, 15})); + ASSERT(comp.at("listTest (end)") == tag_list()); + + ASSERT((comp.at("nested compound test") == tag_compound{ + {"egg", tag_compound{{"value", 0.5f}, {"name", "Eggbert"}}}, + {"ham", tag_compound{{"value", 0.75f}, {"name", "Hampus"}}} + })); + + ASSERT(comp.at("intArrayTest") == tag_int_array( + {0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f})); +} + +void test_read_errors() +{ + std::ifstream file; + nbt::io::stream_reader reader(file); + + //EOF within a tag_double payload + file.open("errortest_eof1", std::ios::binary); + ASSERT(file); + EXPECT_EXCEPTION(reader.read_tag(), io::input_error); + ASSERT(!file); + + //EOF within a key in a compound + file.close(); + file.open("errortest_eof2", std::ios::binary); + ASSERT(file); + EXPECT_EXCEPTION(reader.read_tag(), io::input_error); + ASSERT(!file); + + //Missing tag_end + file.close(); + file.open("errortest_noend", std::ios::binary); + ASSERT(file); + EXPECT_EXCEPTION(reader.read_tag(), io::input_error); + ASSERT(!file); +} + +int main() +{ + test_stream_reader_big(); + test_stream_reader_little(); + test_read_bigtest(); + test_read_errors(); +} diff --git a/test/testfiles/bigtest.nbt b/test/testfiles/bigtest.nbt new file mode 100644 index 0000000000..de1a91268b Binary files /dev/null and b/test/testfiles/bigtest.nbt differ diff --git a/test/testfiles/bigtest_uncompr b/test/testfiles/bigtest_uncompr new file mode 100644 index 0000000000..dc1c9c113f Binary files /dev/null and b/test/testfiles/bigtest_uncompr differ diff --git a/test/testfiles/errortest_eof1 b/test/testfiles/errortest_eof1 new file mode 100644 index 0000000000..abb7ac564f Binary files /dev/null and b/test/testfiles/errortest_eof1 differ diff --git a/test/testfiles/errortest_eof2 b/test/testfiles/errortest_eof2 new file mode 100644 index 0000000000..1e9a503484 Binary files /dev/null and b/test/testfiles/errortest_eof2 differ diff --git a/test/testfiles/errortest_noend b/test/testfiles/errortest_noend new file mode 100644 index 0000000000..d90614628c Binary files /dev/null and b/test/testfiles/errortest_noend differ -- cgit 0.0.5-2-1-g0f52 From bf685ed428c7ecbd0c76828ccec392473fafb9d4 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 6 Aug 2015 14:04:51 +0200 Subject: Set failbit on negative length --- src/tag_array.cpp | 8 ++++++-- src/tag_list.cpp | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/tag_array.cpp b/src/tag_array.cpp index 94b8ffe7ac..f83bce11f7 100644 --- a/src/tag_array.cpp +++ b/src/tag_array.cpp @@ -107,7 +107,9 @@ void tag_array::read_payload(io::stream_reader& reader) { int32_t length; reader.read_num(length); - if(length < 0 || !reader.get_istr()) + 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); @@ -121,7 +123,9 @@ void tag_array::read_payload(io::stream_reader& reader) { int32_t length; reader.read_num(length); - if(length < 0 || !reader.get_istr()) + if(length < 0) + reader.get_istr().setstate(std::ios::failbit); + if(!reader.get_istr()) throw io::input_error("Error reading length of tag_int_array"); data.clear(); diff --git a/src/tag_list.cpp b/src/tag_list.cpp index 53c1a8e392..f5a5b91758 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -138,7 +138,9 @@ void tag_list::read_payload(io::stream_reader& reader) int32_t length; reader.read_num(length); - if(length < 0 || !reader.get_istr()) + if(length < 0) + reader.get_istr().setstate(std::ios::failbit); + if(!reader.get_istr()) throw io::input_error("Error reading length of tag_list"); if(lt != tag_type::End) -- cgit 0.0.5-2-1-g0f52 From 7d449043818864fa94cd1c29ade71f06abd7ce0a Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 6 Aug 2015 14:05:15 +0200 Subject: Add test for negative list length --- test/read_test.cpp | 7 +++++++ test/testfiles/errortest_neg_length | Bin 0 -> 47 bytes 2 files changed, 7 insertions(+) create mode 100644 test/testfiles/errortest_neg_length diff --git a/test/read_test.cpp b/test/read_test.cpp index 85970fc069..a45d74b1f6 100644 --- a/test/read_test.cpp +++ b/test/read_test.cpp @@ -167,6 +167,13 @@ void test_read_errors() ASSERT(file); EXPECT_EXCEPTION(reader.read_tag(), io::input_error); ASSERT(!file); + + //Negative list length + file.close(); + file.open("errortest_neg_length", std::ios::binary); + ASSERT(file); + EXPECT_EXCEPTION(reader.read_tag(), io::input_error); + ASSERT(!file); } int main() diff --git a/test/testfiles/errortest_neg_length b/test/testfiles/errortest_neg_length new file mode 100644 index 0000000000..228de89545 Binary files /dev/null and b/test/testfiles/errortest_neg_length differ -- cgit 0.0.5-2-1-g0f52 From 8e2fe9c45f69d6dc0ab9157f5198238bb0c2583e Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 6 Aug 2015 14:16:46 +0200 Subject: Add test for toplevel tags other than compound --- test/read_test.cpp | 15 +++++++++++++++ test/testfiles/toplevel_string | Bin 0 -> 165 bytes 2 files changed, 15 insertions(+) create mode 100644 test/testfiles/toplevel_string diff --git a/test/read_test.cpp b/test/read_test.cpp index a45d74b1f6..291bf35309 100644 --- a/test/read_test.cpp +++ b/test/read_test.cpp @@ -176,10 +176,25 @@ void test_read_errors() ASSERT(!file); } +void test_read_misc() +{ + std::ifstream file; + nbt::io::stream_reader reader(file); + + file.open("toplevel_string", std::ios::binary); + ASSERT(file); + auto pair = reader.read_tag(); + ASSERT(pair.first == "Test (toplevel tag_string)"); + ASSERT(*pair.second == tag_string( + "Even though unprovided for by NBT, the library should also handle " + "the case where the file consists of something else than tag_compound")); +} + int main() { test_stream_reader_big(); test_stream_reader_little(); test_read_bigtest(); test_read_errors(); + test_read_misc(); } diff --git a/test/testfiles/toplevel_string b/test/testfiles/toplevel_string new file mode 100644 index 0000000000..996cc78d0b Binary files /dev/null and b/test/testfiles/toplevel_string differ -- cgit 0.0.5-2-1-g0f52 From 4805f8d6680c99a416764a03156907adabdc534b Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 6 Aug 2015 14:22:03 +0200 Subject: Move bigtest validation to its own function --- test/read_test.cpp | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/test/read_test.cpp b/test/read_test.cpp index 291bf35309..e7b0e2796a 100644 --- a/test/read_test.cpp +++ b/test/read_test.cpp @@ -97,19 +97,9 @@ void test_stream_reader_little() ASSERT(!is); } -void test_read_bigtest() +//Tests if comp equals an extended variant of Notch's bigtest NBT +void verify_bigtest_structure(const tag_compound& comp) { - //Uses an extended variant of Notch's original bigtest file - std::ifstream file("bigtest_uncompr", std::ios::binary); - ASSERT(file); - nbt::io::stream_reader reader(file); - - auto pair = reader.read_tag(); - ASSERT(pair.first == "Level"); - - ASSERT(pair.second->get_type() == tag_type::Compound); - const tag_compound& comp = pair.second->as(); - ASSERT(comp.size() == 13); ASSERT(comp.at("byteTest") == tag_byte(127)); @@ -143,6 +133,19 @@ void test_read_bigtest() {0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f})); } +void test_read_bigtest() +{ + //Uses an extended variant of Notch's original bigtest file + std::ifstream file("bigtest_uncompr", std::ios::binary); + ASSERT(file); + nbt::io::stream_reader reader(file); + + auto pair = reader.read_tag(); + ASSERT(pair.first == "Level"); + ASSERT(pair.second->get_type() == tag_type::Compound); + verify_bigtest_structure(pair.second->as()); +} + void test_read_errors() { std::ifstream file; -- cgit 0.0.5-2-1-g0f52 From d36095d36140bb6e60838d2bf379102b4b0f7bf8 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 6 Aug 2015 15:10:59 +0200 Subject: Test reading little endian files --- test/read_test.cpp | 14 ++++++++++++++ test/testfiles/littletest_uncompr | Bin 0 -> 1601 bytes 2 files changed, 14 insertions(+) create mode 100644 test/testfiles/littletest_uncompr diff --git a/test/read_test.cpp b/test/read_test.cpp index e7b0e2796a..ae900a502d 100644 --- a/test/read_test.cpp +++ b/test/read_test.cpp @@ -146,6 +146,19 @@ void test_read_bigtest() verify_bigtest_structure(pair.second->as()); } +void test_read_littletest() +{ + //Same as bigtest, but little endian + std::ifstream file("littletest_uncompr", std::ios::binary); + ASSERT(file); + nbt::io::stream_reader reader(file, endian::little); + + auto pair = reader.read_tag(); + ASSERT(pair.first == "Level"); + ASSERT(pair.second->get_type() == tag_type::Compound); + verify_bigtest_structure(pair.second->as()); +} + void test_read_errors() { std::ifstream file; @@ -198,6 +211,7 @@ int main() test_stream_reader_big(); test_stream_reader_little(); test_read_bigtest(); + test_read_littletest(); test_read_errors(); test_read_misc(); } diff --git a/test/testfiles/littletest_uncompr b/test/testfiles/littletest_uncompr new file mode 100644 index 0000000000..86619e9654 Binary files /dev/null and b/test/testfiles/littletest_uncompr differ -- cgit 0.0.5-2-1-g0f52 From c596c4f7ae529e2dd9976c1963634825595c2218 Mon Sep 17 00:00:00 2001 From: ljfa Date: Thu, 6 Aug 2015 22:17:35 +0200 Subject: Change exception message for compound keys and tag_string --- src/tag_compound.cpp | 13 ++++++++++++- src/tag_string.cpp | 9 ++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index 3821ba8f98..3581dd149b 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -20,6 +20,7 @@ #include "tag_compound.h" #include "io/stream_reader.h" #include +#include namespace nbt { @@ -103,7 +104,17 @@ void tag_compound::read_payload(io::stream_reader& reader) tag_type tt; while((tt = reader.read_type(true)) != tag_type::End) { - std::string key = reader.read_string(); + std::string key; + try + { + key = reader.read_string(); + } + catch(io::input_error& ex) + { + std::ostringstream str; + str << "Error reading key of tag_" << tt; + throw io::input_error(str.str()); + } auto tptr = reader.read_payload(tt); tags.emplace(std::move(key), value(std::move(tptr))); } diff --git a/src/tag_string.cpp b/src/tag_string.cpp index 4671339561..83cb815c52 100644 --- a/src/tag_string.cpp +++ b/src/tag_string.cpp @@ -80,7 +80,14 @@ void tag_string::set(std::string&& str) void tag_string::read_payload(io::stream_reader& reader) { - value = reader.read_string(); + try + { + value = reader.read_string(); + } + catch(io::input_error& ex) + { + throw io::input_error("Error reading tag_string"); + } } bool operator==(const tag_string& lhs, const tag_string& rhs) -- cgit 0.0.5-2-1-g0f52 From 20fb31d81b3dfcf66867b4660a887f7b113f4833 Mon Sep 17 00:00:00 2001 From: ljfa Date: Fri, 7 Aug 2015 15:24:13 +0200 Subject: Add read_compound method --- include/io/stream_reader.h | 7 +++++++ src/io/stream_reader.cpp | 15 +++++++++++++++ test/read_test.cpp | 17 +++++++++++------ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/include/io/stream_reader.h b/include/io/stream_reader.h index 75dba6dddb..330d9c5c69 100644 --- a/include/io/stream_reader.h +++ b/include/io/stream_reader.h @@ -22,6 +22,7 @@ #include "endian_str.h" #include "tag.h" +#include "tagfwd.h" #include #include #include @@ -56,6 +57,12 @@ public: ///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> read_compound(); + /** * @brief Reads a named tag from the stream * @throw input_error on failure diff --git a/src/io/stream_reader.cpp b/src/io/stream_reader.cpp index 541a416d83..15f29dba1d 100644 --- a/src/io/stream_reader.cpp +++ b/src/io/stream_reader.cpp @@ -18,6 +18,8 @@ * along with libnbt++. If not, see . */ #include "io/stream_reader.h" +#include "make_unique.h" +#include "tag_compound.h" #include namespace nbt @@ -39,6 +41,19 @@ endian::endian stream_reader::get_endian() const return endian; } +std::pair> stream_reader::read_compound() +{ + if(read_type() != tag_type::Compound) + { + is.setstate(std::ios::failbit); + throw input_error("Tag is not a compound"); + } + std::string key = read_string(); + auto comp = make_unique(); + comp->read_payload(*this); + return {std::move(key), std::move(comp)}; +} + std::pair> stream_reader::read_tag() { tag_type type = read_type(); diff --git a/test/read_test.cpp b/test/read_test.cpp index ae900a502d..d95c762d43 100644 --- a/test/read_test.cpp +++ b/test/read_test.cpp @@ -140,10 +140,9 @@ void test_read_bigtest() ASSERT(file); nbt::io::stream_reader reader(file); - auto pair = reader.read_tag(); + auto pair = reader.read_compound(); ASSERT(pair.first == "Level"); - ASSERT(pair.second->get_type() == tag_type::Compound); - verify_bigtest_structure(pair.second->as()); + verify_bigtest_structure(*pair.second); } void test_read_littletest() @@ -153,10 +152,10 @@ void test_read_littletest() ASSERT(file); nbt::io::stream_reader reader(file, endian::little); - auto pair = reader.read_tag(); + auto pair = reader.read_compound(); ASSERT(pair.first == "Level"); ASSERT(pair.second->get_type() == tag_type::Compound); - verify_bigtest_structure(pair.second->as()); + verify_bigtest_structure(*pair.second); } void test_read_errors() @@ -197,8 +196,14 @@ void test_read_misc() std::ifstream file; nbt::io::stream_reader reader(file); + //Toplevel tag other than compound file.open("toplevel_string", std::ios::binary); - ASSERT(file); + EXPECT_EXCEPTION(reader.read_compound(), io::input_error); + ASSERT(!file); + + //Rewind and try again with read_tag + file.clear(); + ASSERT(file.seekg(0)); auto pair = reader.read_tag(); ASSERT(pair.first == "Test (toplevel tag_string)"); ASSERT(*pair.second == tag_string( -- cgit 0.0.5-2-1-g0f52 From 53db961b58415fe966ca6cdf431ac7ba89bffd30 Mon Sep 17 00:00:00 2001 From: ljfa Date: Fri, 7 Aug 2015 15:34:53 +0200 Subject: Add missing assertion --- test/read_test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/read_test.cpp b/test/read_test.cpp index d95c762d43..e6ec82d59f 100644 --- a/test/read_test.cpp +++ b/test/read_test.cpp @@ -198,6 +198,7 @@ void test_read_misc() //Toplevel tag other than compound file.open("toplevel_string", std::ios::binary); + ASSERT(file); EXPECT_EXCEPTION(reader.read_compound(), io::input_error); ASSERT(!file); -- cgit 0.0.5-2-1-g0f52 From b992d074054dc03b548e6b8a169cff5e1788ea73 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 9 Aug 2015 18:42:10 +0200 Subject: Add missing include --- include/tagfwd.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/tagfwd.h b/include/tagfwd.h index 622e39d056..178edd7e47 100644 --- a/include/tagfwd.h +++ b/include/tagfwd.h @@ -22,6 +22,7 @@ */ #ifndef TAGFWD_H_INCLUDED #define TAGFWD_H_INCLUDED +#include namespace nbt { -- cgit 0.0.5-2-1-g0f52 From 12374391af5b876094b802408e9b0e92429ae3c1 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 9 Aug 2015 19:33:39 +0200 Subject: Implement Visitor design pattern for tag --- include/crtp_tag.h | 9 +++++++++ include/tag.h | 9 ++++++++- include/tag_visitor.h | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++ test/nbttest.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 include/tag_visitor.h diff --git a/include/crtp_tag.h b/include/crtp_tag.h index 25f87bf6e8..86f070e147 100644 --- a/include/crtp_tag.h +++ b/include/crtp_tag.h @@ -21,6 +21,7 @@ #define CRTP_TAG_H_INCLUDED #include "tag.h" +#include "tag_visitor.h" #include "make_unique.h" namespace nbt @@ -43,6 +44,8 @@ namespace detail tag& assign(tag&& rhs) override final; + void accept(tag_visitor& visitor); + private: bool equals(const tag& rhs) const override final; }; @@ -74,6 +77,12 @@ namespace detail return static_cast(*this) = dynamic_cast(rhs); } + template + void crtp_tag::accept(tag_visitor& visitor) + { + visitor.visit(static_cast(*this)); + } + template bool crtp_tag::equals(const tag& rhs) const { diff --git a/include/tag.h b/include/tag.h index 569da446d6..f3e9696a53 100644 --- a/include/tag.h +++ b/include/tag.h @@ -51,7 +51,8 @@ enum class tag_type : int8_t */ bool is_valid_type(int type, bool allow_end = false); -//Forward declaration +//Forward declarations +class tag_visitor; namespace io { class stream_reader; } @@ -85,6 +86,12 @@ public: */ virtual tag& assign(tag&& rhs) = 0; + /** + * @brief Calls the appropriate overload of @c visit() on the visitor with + * @c *this as argument + */ + virtual void accept(tag_visitor& visitor) = 0; + /** * @brief Reads the tag's payload from the stream * @throw io::stream_reader::input_error on failure diff --git a/include/tag_visitor.h b/include/tag_visitor.h new file mode 100644 index 0000000000..bc5113918c --- /dev/null +++ b/include/tag_visitor.h @@ -0,0 +1,55 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#ifndef TAG_VISITOR_H_INCLUDED +#define TAG_VISITOR_H_INCLUDED + +#include "tagfwd.h" + +namespace nbt +{ + +/** + * @brief Base class for visitors of tags + * + * Implementing the Visitor pattern + */ +class tag_visitor +{ +public: + virtual ~tag_visitor() noexcept = 0; //Abstract class + + virtual void visit(tag_byte& tag) {} + virtual void visit(tag_short& tag) {} + virtual void visit(tag_int& tag) {} + virtual void visit(tag_long& tag) {} + virtual void visit(tag_float& tag) {} + virtual void visit(tag_double& tag) {} + virtual void visit(tag_byte_array& tag) {} + virtual void visit(tag_string& tag) {} + virtual void visit(tag_list& tag) {} + virtual void visit(tag_compound& tag) {} + virtual void visit(tag_int_array& tag) {} +}; + +inline tag_visitor::~tag_visitor() noexcept {} + +} + +#endif // TAG_VISITOR_H_INCLUDED diff --git a/test/nbttest.cpp b/test/nbttest.cpp index b041d44aa8..4c46668f0b 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -19,6 +19,7 @@ */ #include "microtest.h" #include "nbt_tags.h" +#include "tag_visitor.h" #include #include @@ -425,6 +426,50 @@ void test_tag_int_array() std::clog << "test_tag_int_array passed" << std::endl; } +void test_visitor() +{ + struct : public tag_visitor + { + tag_type visited = tag_type::Null; + + void visit(tag_byte& tag) override { visited = tag_type::Byte; } + void visit(tag_short& tag) override { visited = tag_type::Short; } + void visit(tag_int& tag) override { visited = tag_type::Int; } + void visit(tag_long& tag) override { visited = tag_type::Long; } + void visit(tag_float& tag) override { visited = tag_type::Float; } + void visit(tag_double& tag) override { visited = tag_type::Double; } + void visit(tag_byte_array& tag) override { visited = tag_type::Byte_Array; } + void visit(tag_string& tag) override { visited = tag_type::String; } + void visit(tag_list& tag) override { visited = tag_type::List; } + void visit(tag_compound& tag) override { visited = tag_type::Compound; } + void visit(tag_int_array& tag) override { visited = tag_type::Int_Array; } + } v; + + tag_byte().accept(v); + ASSERT(v.visited == tag_type::Byte); + tag_short().accept(v); + ASSERT(v.visited == tag_type::Short); + tag_int().accept(v); + ASSERT(v.visited == tag_type::Int); + tag_long().accept(v); + ASSERT(v.visited == tag_type::Long); + tag_float().accept(v); + ASSERT(v.visited == tag_type::Float); + tag_double().accept(v); + ASSERT(v.visited == tag_type::Double); + tag_byte_array().accept(v); + ASSERT(v.visited == tag_type::Byte_Array); + tag_string().accept(v); + ASSERT(v.visited == tag_type::String); + tag_list().accept(v); + ASSERT(v.visited == tag_type::List); + tag_compound().accept(v); + ASSERT(v.visited == tag_type::Compound); + tag_int_array().accept(v); + ASSERT(v.visited == tag_type::Int_Array); + std::clog << "test_visitor passed" << std::endl; +} + int main() { test_tag(); @@ -436,4 +481,5 @@ int main() test_tag_list(); test_tag_byte_array(); test_tag_int_array(); + test_visitor(); } -- cgit 0.0.5-2-1-g0f52 From 70e84ad70772dbacf4554c12650bb11a87f5a1ad Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 9 Aug 2015 20:18:06 +0200 Subject: Create crtp_tag::sub_this method for convenience --- include/crtp_tag.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/include/crtp_tag.h b/include/crtp_tag.h index 86f070e147..11c41cb5b3 100644 --- a/include/crtp_tag.h +++ b/include/crtp_tag.h @@ -48,6 +48,9 @@ namespace detail private: bool equals(const tag& rhs) const override final; + + Sub& sub_this() { return static_cast(*this); } + const Sub& sub_this() const { return static_cast(*this); } }; template @@ -62,31 +65,31 @@ namespace detail template std::unique_ptr crtp_tag::clone() const& { - return make_unique(static_cast(*this)); + return make_unique(sub_this()); } template std::unique_ptr crtp_tag::move_clone() && { - return make_unique(static_cast(*this)); + return make_unique(std::move(sub_this())); } template tag& crtp_tag::assign(tag&& rhs) { - return static_cast(*this) = dynamic_cast(rhs); + return sub_this() = dynamic_cast(rhs); } template void crtp_tag::accept(tag_visitor& visitor) { - visitor.visit(static_cast(*this)); + visitor.visit(sub_this()); } template bool crtp_tag::equals(const tag& rhs) const { - return static_cast(*this) == static_cast(rhs); + return sub_this() == static_cast(rhs); } } -- cgit 0.0.5-2-1-g0f52 From 50dc555de3198938109d2a595245374e9dde8709 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 9 Aug 2015 22:44:09 +0200 Subject: Rename tag_visitor to nbt_visitor Change some override/final modifiers --- include/crtp_tag.h | 6 +++--- include/nbt_visitor.h | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/tag.h | 4 ++-- include/tag_visitor.h | 55 --------------------------------------------------- test/nbttest.cpp | 26 ++++++++++++------------ 5 files changed, 73 insertions(+), 73 deletions(-) create mode 100644 include/nbt_visitor.h delete mode 100644 include/tag_visitor.h diff --git a/include/crtp_tag.h b/include/crtp_tag.h index 11c41cb5b3..637e5be244 100644 --- a/include/crtp_tag.h +++ b/include/crtp_tag.h @@ -21,7 +21,7 @@ #define CRTP_TAG_H_INCLUDED #include "tag.h" -#include "tag_visitor.h" +#include "nbt_visitor.h" #include "make_unique.h" namespace nbt @@ -44,7 +44,7 @@ namespace detail tag& assign(tag&& rhs) override final; - void accept(tag_visitor& visitor); + void accept(nbt_visitor& visitor) override final; private: bool equals(const tag& rhs) const override final; @@ -81,7 +81,7 @@ namespace detail } template - void crtp_tag::accept(tag_visitor& visitor) + void crtp_tag::accept(nbt_visitor& visitor) { visitor.visit(sub_this()); } diff --git a/include/nbt_visitor.h b/include/nbt_visitor.h new file mode 100644 index 0000000000..dc281bd7e4 --- /dev/null +++ b/include/nbt_visitor.h @@ -0,0 +1,55 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#ifndef NBT_VISITOR_H_INCLUDED +#define NBT_VISITOR_H_INCLUDED + +#include "tagfwd.h" + +namespace nbt +{ + +/** + * @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& tag) {} + virtual void visit(tag_short& tag) {} + virtual void visit(tag_int& tag) {} + virtual void visit(tag_long& tag) {} + virtual void visit(tag_float& tag) {} + virtual void visit(tag_double& tag) {} + virtual void visit(tag_byte_array& tag) {} + virtual void visit(tag_string& tag) {} + virtual void visit(tag_list& tag) {} + virtual void visit(tag_compound& tag) {} + virtual void visit(tag_int_array& tag) {} +}; + +inline nbt_visitor::~nbt_visitor() noexcept {} + +} + +#endif // NBT_VISITOR_H_INCLUDED diff --git a/include/tag.h b/include/tag.h index f3e9696a53..c7ddd44c52 100644 --- a/include/tag.h +++ b/include/tag.h @@ -52,7 +52,7 @@ enum class tag_type : int8_t bool is_valid_type(int type, bool allow_end = false); //Forward declarations -class tag_visitor; +class nbt_visitor; namespace io { class stream_reader; } @@ -90,7 +90,7 @@ public: * @brief Calls the appropriate overload of @c visit() on the visitor with * @c *this as argument */ - virtual void accept(tag_visitor& visitor) = 0; + virtual void accept(nbt_visitor& visitor) = 0; /** * @brief Reads the tag's payload from the stream diff --git a/include/tag_visitor.h b/include/tag_visitor.h deleted file mode 100644 index bc5113918c..0000000000 --- a/include/tag_visitor.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * libnbt++ - A library for the Minecraft Named Binary Tag format. - * Copyright (C) 2013, 2015 ljfa-ag - * - * This file is part of libnbt++. - * - * libnbt++ is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * libnbt++ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with libnbt++. If not, see . - */ -#ifndef TAG_VISITOR_H_INCLUDED -#define TAG_VISITOR_H_INCLUDED - -#include "tagfwd.h" - -namespace nbt -{ - -/** - * @brief Base class for visitors of tags - * - * Implementing the Visitor pattern - */ -class tag_visitor -{ -public: - virtual ~tag_visitor() noexcept = 0; //Abstract class - - virtual void visit(tag_byte& tag) {} - virtual void visit(tag_short& tag) {} - virtual void visit(tag_int& tag) {} - virtual void visit(tag_long& tag) {} - virtual void visit(tag_float& tag) {} - virtual void visit(tag_double& tag) {} - virtual void visit(tag_byte_array& tag) {} - virtual void visit(tag_string& tag) {} - virtual void visit(tag_list& tag) {} - virtual void visit(tag_compound& tag) {} - virtual void visit(tag_int_array& tag) {} -}; - -inline tag_visitor::~tag_visitor() noexcept {} - -} - -#endif // TAG_VISITOR_H_INCLUDED diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 4c46668f0b..fd74995d67 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -19,7 +19,7 @@ */ #include "microtest.h" #include "nbt_tags.h" -#include "tag_visitor.h" +#include "nbt_visitor.h" #include #include @@ -428,21 +428,21 @@ void test_tag_int_array() void test_visitor() { - struct : public tag_visitor + struct : public nbt_visitor { tag_type visited = tag_type::Null; - void visit(tag_byte& tag) override { visited = tag_type::Byte; } - void visit(tag_short& tag) override { visited = tag_type::Short; } - void visit(tag_int& tag) override { visited = tag_type::Int; } - void visit(tag_long& tag) override { visited = tag_type::Long; } - void visit(tag_float& tag) override { visited = tag_type::Float; } - void visit(tag_double& tag) override { visited = tag_type::Double; } - void visit(tag_byte_array& tag) override { visited = tag_type::Byte_Array; } - void visit(tag_string& tag) override { visited = tag_type::String; } - void visit(tag_list& tag) override { visited = tag_type::List; } - void visit(tag_compound& tag) override { visited = tag_type::Compound; } - void visit(tag_int_array& tag) override { visited = tag_type::Int_Array; } + void visit(tag_byte& tag) { visited = tag_type::Byte; } + void visit(tag_short& tag) { visited = tag_type::Short; } + void visit(tag_int& tag) { visited = tag_type::Int; } + void visit(tag_long& tag) { visited = tag_type::Long; } + void visit(tag_float& tag) { visited = tag_type::Float; } + void visit(tag_double& tag) { visited = tag_type::Double; } + void visit(tag_byte_array& tag) { visited = tag_type::Byte_Array; } + void visit(tag_string& tag) { visited = tag_type::String; } + void visit(tag_list& tag) { visited = tag_type::List; } + void visit(tag_compound& tag) { visited = tag_type::Compound; } + void visit(tag_int_array& tag) { visited = tag_type::Int_Array; } } v; tag_byte().accept(v); -- cgit 0.0.5-2-1-g0f52 From 1a4d8ab0f31a3c1417f3f78a9013151424e9f7f3 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 10 Aug 2015 19:35:57 +0200 Subject: Add const_nbt_visitor class --- include/crtp_tag.h | 7 +++++++ include/nbt_visitor.h | 25 +++++++++++++++++++++++++ include/tag.h | 4 ++++ 3 files changed, 36 insertions(+) diff --git a/include/crtp_tag.h b/include/crtp_tag.h index 637e5be244..3f3a528b21 100644 --- a/include/crtp_tag.h +++ b/include/crtp_tag.h @@ -45,6 +45,7 @@ namespace detail tag& assign(tag&& rhs) override final; void accept(nbt_visitor& visitor) override final; + void accept(const_nbt_visitor& visitor) const override final; private: bool equals(const tag& rhs) const override final; @@ -86,6 +87,12 @@ namespace detail visitor.visit(sub_this()); } + template + void crtp_tag::accept(const_nbt_visitor& visitor) const + { + visitor.visit(sub_this()); + } + template bool crtp_tag::equals(const tag& rhs) const { diff --git a/include/nbt_visitor.h b/include/nbt_visitor.h index dc281bd7e4..edecc8edfb 100644 --- a/include/nbt_visitor.h +++ b/include/nbt_visitor.h @@ -48,8 +48,33 @@ public: virtual void visit(tag_int_array& tag) {} }; +/** + * @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& tag) {} + virtual void visit(const tag_short& tag) {} + virtual void visit(const tag_int& tag) {} + virtual void visit(const tag_long& tag) {} + virtual void visit(const tag_float& tag) {} + virtual void visit(const tag_double& tag) {} + virtual void visit(const tag_byte_array& tag) {} + virtual void visit(const tag_string& tag) {} + virtual void visit(const tag_list& tag) {} + virtual void visit(const tag_compound& tag) {} + virtual void visit(const tag_int_array& tag) {} +}; + inline nbt_visitor::~nbt_visitor() noexcept {} +inline const_nbt_visitor::~const_nbt_visitor() noexcept {} + } #endif // NBT_VISITOR_H_INCLUDED diff --git a/include/tag.h b/include/tag.h index c7ddd44c52..c365f7543e 100644 --- a/include/tag.h +++ b/include/tag.h @@ -53,6 +53,7 @@ bool is_valid_type(int type, bool allow_end = false); //Forward declarations class nbt_visitor; +class const_nbt_visitor; namespace io { class stream_reader; } @@ -89,8 +90,11 @@ public: /** * @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 -- cgit 0.0.5-2-1-g0f52 From 65d8091ddc1e78629ae8268fdb38a8654c92423b Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 10 Aug 2015 19:42:25 +0200 Subject: Add output to clog for endian_str and read tests --- test/endian_str_test.cpp | 4 ++++ test/read_test.cpp | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/test/endian_str_test.cpp b/test/endian_str_test.cpp index 3f7f481777..d519dee049 100644 --- a/test/endian_str_test.cpp +++ b/test/endian_str_test.cpp @@ -20,6 +20,7 @@ #include "microtest.h" #include "endian_str.h" #include +#include #include using namespace endian; @@ -75,6 +76,7 @@ void test_uint() ASSERT(u64 == 0x090A0B0C0D0E0F10); ASSERT(str); //Check if stream has failed + std::clog << "test_uint passed" << std::endl; } void test_sint() @@ -128,6 +130,7 @@ void test_sint() ASSERT(i64 == -0x090A0B0C0D0E0F10); ASSERT(str); //Check if stream has failed + std::clog << "test_sint passed" << std::endl; } void test_float() @@ -167,6 +170,7 @@ void test_float() ASSERT(d == dconst); ASSERT(str); //Check if stream has failed + std::clog << "test_float passed" << std::endl; } int main() diff --git a/test/read_test.cpp b/test/read_test.cpp index e6ec82d59f..0fa6259a15 100644 --- a/test/read_test.cpp +++ b/test/read_test.cpp @@ -20,6 +20,7 @@ #include "microtest.h" #include "io/stream_reader.h" #include "nbt_tags.h" +#include #include #include @@ -69,6 +70,7 @@ void test_stream_reader_big() is.str("\x03\x04"); reader.read_num(i); ASSERT(!is); + std::clog << "test_stream_reader_big passed" << std::endl; } void test_stream_reader_little() @@ -95,6 +97,7 @@ void test_stream_reader_little() EXPECT_EXCEPTION(reader.read_string(), io::input_error); ASSERT(!is); + std::clog << "test_stream_reader_little passed" << std::endl; } //Tests if comp equals an extended variant of Notch's bigtest NBT @@ -143,6 +146,7 @@ void test_read_bigtest() auto pair = reader.read_compound(); ASSERT(pair.first == "Level"); verify_bigtest_structure(*pair.second); + std::clog << "test_read_bigtest passed" << std::endl; } void test_read_littletest() @@ -156,6 +160,7 @@ void test_read_littletest() ASSERT(pair.first == "Level"); ASSERT(pair.second->get_type() == tag_type::Compound); verify_bigtest_structure(*pair.second); + std::clog << "test_read_littletest passed" << std::endl; } void test_read_errors() @@ -189,6 +194,7 @@ void test_read_errors() ASSERT(file); EXPECT_EXCEPTION(reader.read_tag(), io::input_error); ASSERT(!file); + std::clog << "test_read_errors passed" << std::endl; } void test_read_misc() @@ -210,6 +216,7 @@ void test_read_misc() ASSERT(*pair.second == tag_string( "Even though unprovided for by NBT, the library should also handle " "the case where the file consists of something else than tag_compound")); + std::clog << "test_read_misc passed" << std::endl; } int main() -- cgit 0.0.5-2-1-g0f52 From 81111f5f691ccf5ebb55d992f9069de2f6731214 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 10 Aug 2015 20:59:40 +0200 Subject: Lay foundation for formatter tests --- test/CMakeLists.txt | 4 ++++ test/format_test.cpp | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 test/format_test.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index af5dd4532b..89a445d402 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -16,3 +16,7 @@ add_custom_command(TARGET read_test POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/testfiles ${CMAKE_CURRENT_BINARY_DIR}) add_test(read_test read_test) + +add_executable(format_test format_test.cpp) +target_link_libraries(format_test nbt++) +add_test(format_test format_test) diff --git a/test/format_test.cpp b/test/format_test.cpp new file mode 100644 index 0000000000..7fb7ca45d8 --- /dev/null +++ b/test/format_test.cpp @@ -0,0 +1,28 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include + +using namespace nbt; + +int main() +{ + +} + -- cgit 0.0.5-2-1-g0f52 From c72357d6b73c80ede8ffbcc2bdab6f3c6d3115e0 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 10 Aug 2015 21:09:58 +0200 Subject: Cannot use forward declaration with std::pair --- include/io/stream_reader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/io/stream_reader.h b/include/io/stream_reader.h index 330d9c5c69..926d2453c6 100644 --- a/include/io/stream_reader.h +++ b/include/io/stream_reader.h @@ -22,7 +22,7 @@ #include "endian_str.h" #include "tag.h" -#include "tagfwd.h" +#include "tag_compound.h" #include #include #include -- cgit 0.0.5-2-1-g0f52 From fda6d3bb3149ca8f328263b3716dc2f08c40c5c1 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 10 Aug 2015 22:27:08 +0200 Subject: Create json_formatter --- CMakeLists.txt | 6 +- include/text/json_formatter.h | 45 +++++++++++++ src/text/json_formatter.cpp | 151 ++++++++++++++++++++++++++++++++++++++++++ test/format_test.cpp | 14 +++- 4 files changed, 213 insertions(+), 3 deletions(-) create mode 100644 include/text/json_formatter.h create mode 100644 src/text/json_formatter.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 39de6c6715..3baaf4fd66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,8 +15,10 @@ add_library(nbt++ STATIC src/tag_string.cpp src/value.cpp src/value_initializer.cpp - - src/io/stream_reader.cpp) + + src/io/stream_reader.cpp + + src/text/json_formatter.cpp) enable_testing() add_subdirectory(test) diff --git a/include/text/json_formatter.h b/include/text/json_formatter.h new file mode 100644 index 0000000000..6288a734b0 --- /dev/null +++ b/include/text/json_formatter.h @@ -0,0 +1,45 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#ifndef JSON_FORMATTER_H_INCLUDED +#define JSON_FORMATTER_H_INCLUDED + +#include "tagfwd.h" +#include + +namespace nbt +{ +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 json_formatter +{ +public: + void write(std::ostream& os, const tag& t); +}; + +} +} + +#endif // JSON_FORMATTER_H_INCLUDED diff --git a/src/text/json_formatter.cpp b/src/text/json_formatter.cpp new file mode 100644 index 0000000000..eb94d8af63 --- /dev/null +++ b/src/text/json_formatter.cpp @@ -0,0 +1,151 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include "text/json_formatter.h" +#include "nbt_tags.h" +#include "nbt_visitor.h" +#include + +namespace nbt +{ +namespace text +{ + +namespace //anonymous +{ + class json_fmt_visitor : public const_nbt_visitor + { + public: + json_fmt_visitor(std::ostream& os): os(os) {} + + void visit(const tag_byte& b) override + { os << static_cast(b.get()) << "b"; } //We don't want to print a character + + void visit(const tag_short& s) override + { os << s.get() << "s"; } + + void visit(const tag_int& i) override + { os << i.get(); } + + void visit(const tag_long& l) override + { os << l.get() << "l"; } + + void visit(const tag_float& f) override + { + write_double(f.get()); + os << "f"; + } + + void visit(const tag_double& d) override + { + write_double(d.get()); + os << "d"; + } + + void visit(const tag_byte_array& ba) override + { os << "[" << ba.size() << " bytes]"; } + + void visit(const tag_string& s) override + { os << '"' << s.get() << '"'; } //TODO: escape special characters + + void visit(const tag_list& l) override + { + os << "[\n"; + ++indent_lvl; + for(unsigned int i = 0; i < l.size(); ++i) + { + indent(); + l[i].get().accept(*this); + if(i != l.size()-1) + os << ","; + os << "\n"; + } + --indent_lvl; + indent(); + os << "]"; + } + + void visit(const tag_compound& c) override + { + os << "{\n"; + ++indent_lvl; + unsigned int i = 0; + for(const auto& kv: c) + { + indent(); + os << kv.first << ": "; + kv.second.get().accept(*this); + if(i != c.size()-1) + os << ","; + os << "\n"; + ++i; + } + --indent_lvl; + indent(); + os << "}"; + } + + void visit(const tag_int_array& ia) override + { + os << "["; + for(unsigned int i = 0; i < ia.size(); ++i) + { + os << ia[i]; + if(i != ia.size()-1) + os << ", "; + } + os << "]"; + } + + private: + const std::string indent_str = " "; + + std::ostream& os; + int indent_lvl = 0; + + void indent() + { + for(int i = 0; i < indent_lvl; ++i) + os << indent_str; + } + + void write_double(double d) + { + if(std::isfinite(d)) + os << d; + else if(std::isinf(d)) + { + if(std::signbit(d)) + os << "-"; + os << "Infinity"; + } + else + os << "NaN"; + } + }; +} + +void json_formatter::write(std::ostream& os, const tag& t) +{ + json_fmt_visitor v(os); + t.accept(v); +} + +} +} diff --git a/test/format_test.cpp b/test/format_test.cpp index 7fb7ca45d8..893637c19d 100644 --- a/test/format_test.cpp +++ b/test/format_test.cpp @@ -17,12 +17,24 @@ * You should have received a copy of the GNU Lesser General Public License * along with libnbt++. If not, see . */ +#include "microtest.h" +#include "text/json_formatter.h" +#include "io/stream_reader.h" +#include #include +#include "nbt_tags.h" using namespace nbt; int main() { + std::ifstream file("bigtest_uncompr", std::ios::binary); + ASSERT(file); + std::string key; + std::unique_ptr comp; + std::tie(key, comp) = io::stream_reader(file).read_compound(); + std::cout << "----- json_formatter:\n"; + text::json_formatter().write(std::cout, *comp); + std::cout << "\n-----" << std::endl; } - -- cgit 0.0.5-2-1-g0f52 From 6a04146baee5662f4debe03f0a401ea434a330f0 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 10 Aug 2015 22:29:54 +0200 Subject: Make json_formatter able to handle null values --- src/text/json_formatter.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/text/json_formatter.cpp b/src/text/json_formatter.cpp index eb94d8af63..7edecf72f7 100644 --- a/src/text/json_formatter.cpp +++ b/src/text/json_formatter.cpp @@ -71,7 +71,10 @@ namespace //anonymous for(unsigned int i = 0; i < l.size(); ++i) { indent(); - l[i].get().accept(*this); + if(l[i]) + l[i].get().accept(*this); + else + write_null(); if(i != l.size()-1) os << ","; os << "\n"; @@ -90,7 +93,10 @@ namespace //anonymous { indent(); os << kv.first << ": "; - kv.second.get().accept(*this); + if(kv.second) + kv.second.get().accept(*this); + else + write_null(); if(i != c.size()-1) os << ","; os << "\n"; @@ -138,6 +144,11 @@ namespace //anonymous else os << "NaN"; } + + void write_null() + { + os << "null"; + } }; } -- cgit 0.0.5-2-1-g0f52 From d426b75daa66e0a1c109c59900be2172abe70d19 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 10 Aug 2015 22:41:15 +0200 Subject: Handle lists of lists or compounds differently for printing --- src/text/json_formatter.cpp | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/text/json_formatter.cpp b/src/text/json_formatter.cpp index 7edecf72f7..d1fce85375 100644 --- a/src/text/json_formatter.cpp +++ b/src/text/json_formatter.cpp @@ -66,21 +66,42 @@ namespace //anonymous void visit(const tag_list& l) override { - os << "[\n"; - ++indent_lvl; - for(unsigned int i = 0; i < l.size(); ++i) + //Wrap lines for lists of lists or compounds. + //Lists of other types can usually be on one line without problem. + const bool break_lines = l.size() > 0 && + (l.el_type() == tag_type::List || l.el_type() == tag_type::Compound); + + os << "["; + if(break_lines) { - indent(); - if(l[i]) - l[i].get().accept(*this); - else - write_null(); - if(i != l.size()-1) - os << ","; os << "\n"; + ++indent_lvl; + for(unsigned int i = 0; i < l.size(); ++i) + { + indent(); + if(l[i]) + l[i].get().accept(*this); + else + write_null(); + if(i != l.size()-1) + os << ","; + os << "\n"; + } + --indent_lvl; + indent(); + } + else + { + for(unsigned int i = 0; i < l.size(); ++i) + { + if(l[i]) + l[i].get().accept(*this); + else + write_null(); + if(i != l.size()-1) + os << ", "; + } } - --indent_lvl; - indent(); os << "]"; } -- cgit 0.0.5-2-1-g0f52 From 8e7a1330ae09f4b6690a42ab113226c45df4bcf8 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 10 Aug 2015 22:44:12 +0200 Subject: Add shortcut for printing empty compounds --- src/text/json_formatter.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/text/json_formatter.cpp b/src/text/json_formatter.cpp index d1fce85375..778f57c022 100644 --- a/src/text/json_formatter.cpp +++ b/src/text/json_formatter.cpp @@ -107,6 +107,12 @@ namespace //anonymous void visit(const tag_compound& c) override { + if(c.size() == 0) //No line breaks inside empty compounds please + { + os << "{}"; + return; + } + os << "{\n"; ++indent_lvl; unsigned int i = 0; -- cgit 0.0.5-2-1-g0f52 From 85df484ff7aa34dd62fb6807ad0001a72f485c86 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 10 Aug 2015 22:56:32 +0200 Subject: Make json_formatter::write method const --- include/text/json_formatter.h | 2 +- src/text/json_formatter.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/text/json_formatter.h b/include/text/json_formatter.h index 6288a734b0..6a3e6aff00 100644 --- a/include/text/json_formatter.h +++ b/include/text/json_formatter.h @@ -36,7 +36,7 @@ namespace text class json_formatter { public: - void write(std::ostream& os, const tag& t); + void write(std::ostream& os, const tag& t) const; }; } diff --git a/src/text/json_formatter.cpp b/src/text/json_formatter.cpp index 778f57c022..f5dc2bb661 100644 --- a/src/text/json_formatter.cpp +++ b/src/text/json_formatter.cpp @@ -179,7 +179,7 @@ namespace //anonymous }; } -void json_formatter::write(std::ostream& os, const tag& t) +void json_formatter::write(std::ostream& os, const tag& t) const { json_fmt_visitor v(os); t.accept(v); -- cgit 0.0.5-2-1-g0f52 From 2c43e00b12c9f26aad6f76e3a723bc231c9348b5 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 10 Aug 2015 22:58:30 +0200 Subject: Add operator<< for tag, using json_formatter --- include/tag.h | 9 +++++++++ src/tag.cpp | 8 ++++++++ src/text/json_formatter.cpp | 1 + test/format_test.cpp | 6 +++--- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/include/tag.h b/include/tag.h index c365f7543e..3bf23dae8f 100644 --- a/include/tag.h +++ b/include/tag.h @@ -119,8 +119,17 @@ private: virtual bool equals(const tag& rhs) const = 0; }; +///Output operator for tag types std::ostream& operator<<(std::ostream& os, tag_type tt); +/** + * @brief Output operator for tags + * + * Uses @ref text::json_formatter + * @relates tag + */ +std::ostream& operator<<(std::ostream& os, const tag& t); + template T& tag::as() { diff --git a/src/tag.cpp b/src/tag.cpp index db4c09aa2e..5b6f1142e2 100644 --- a/src/tag.cpp +++ b/src/tag.cpp @@ -19,6 +19,7 @@ */ #include "tag.h" #include "nbt_tags.h" +#include "text/json_formatter.h" #include #include #include @@ -90,4 +91,11 @@ std::ostream& operator<<(std::ostream& os, tag_type tt) } } +std::ostream& operator<<(std::ostream& os, const tag& t) +{ + static const text::json_formatter formatter; + formatter.write(os, t); + return os; +} + } diff --git a/src/text/json_formatter.cpp b/src/text/json_formatter.cpp index f5dc2bb661..7b73a26951 100644 --- a/src/text/json_formatter.cpp +++ b/src/text/json_formatter.cpp @@ -29,6 +29,7 @@ namespace text namespace //anonymous { + ///Helper class which uses the Visitor pattern to pretty-print tags class json_fmt_visitor : public const_nbt_visitor { public: diff --git a/test/format_test.cpp b/test/format_test.cpp index 893637c19d..9f79c66c60 100644 --- a/test/format_test.cpp +++ b/test/format_test.cpp @@ -18,7 +18,7 @@ * along with libnbt++. If not, see . */ #include "microtest.h" -#include "text/json_formatter.h" +//#include "text/json_formatter.h" #include "io/stream_reader.h" #include #include @@ -34,7 +34,7 @@ int main() std::unique_ptr comp; std::tie(key, comp) = io::stream_reader(file).read_compound(); - std::cout << "----- json_formatter:\n"; - text::json_formatter().write(std::cout, *comp); + std::cout << "----- default operator<<:\n"; + std::cout << *comp; std::cout << "\n-----" << std::endl; } -- cgit 0.0.5-2-1-g0f52 From 7122475522a290e39fb32999efc5ed20aef0a315 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 11 Aug 2015 11:04:47 +0200 Subject: Print floating point number with the necessary precision --- src/text/json_formatter.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/text/json_formatter.cpp b/src/text/json_formatter.cpp index 7b73a26951..44acd61713 100644 --- a/src/text/json_formatter.cpp +++ b/src/text/json_formatter.cpp @@ -21,6 +21,8 @@ #include "nbt_tags.h" #include "nbt_visitor.h" #include +#include +#include namespace nbt { @@ -49,13 +51,13 @@ namespace //anonymous void visit(const tag_float& f) override { - write_double(f.get()); + write_float(f.get()); os << "f"; } void visit(const tag_double& d) override { - write_double(d.get()); + write_float(d.get()); os << "d"; } @@ -159,13 +161,14 @@ namespace //anonymous os << indent_str; } - void write_double(double d) + template + void write_float(T val, int precision = std::numeric_limits::max_digits10) { - if(std::isfinite(d)) - os << d; - else if(std::isinf(d)) + if(std::isfinite(val)) + os << std::setprecision(precision) << val; + else if(std::isinf(val)) { - if(std::signbit(d)) + if(std::signbit(val)) os << "-"; os << "Infinity"; } -- cgit 0.0.5-2-1-g0f52 From b5f3570924b49eb8564b68b02e5dbd18f0ad9a06 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 11 Aug 2015 11:16:10 +0200 Subject: Inline crtp_tag and tag_primitive methods --- CMakeLists.txt | 1 - include/crtp_tag.h | 56 +++--------------------- include/tag_primitive.h | 33 ++++++++++---- src/tag.cpp | 4 ++ src/tag_primitive.cpp | 111 ------------------------------------------------ 5 files changed, 36 insertions(+), 169 deletions(-) delete mode 100644 src/tag_primitive.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3baaf4fd66..8524bcdef4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,6 @@ add_library(nbt++ STATIC src/tag_array.cpp src/tag_compound.cpp src/tag_list.cpp - src/tag_primitive.cpp src/tag_string.cpp src/value.cpp src/value_initializer.cpp diff --git a/include/crtp_tag.h b/include/crtp_tag.h index 3f3a528b21..7b802979a9 100644 --- a/include/crtp_tag.h +++ b/include/crtp_tag.h @@ -37,18 +37,18 @@ namespace detail //Pure virtual destructor to make the class abstract virtual ~crtp_tag() noexcept = 0; - tag_type get_type() const noexcept override final; + tag_type get_type() const noexcept override final { return Sub::type; }; - std::unique_ptr clone() const& override final; - std::unique_ptr move_clone() && override final; + std::unique_ptr clone() const& override final { return make_unique(sub_this()); } + std::unique_ptr move_clone() && override final { return make_unique(std::move(sub_this())); } - tag& assign(tag&& rhs) override final; + tag& assign(tag&& rhs) override final { return sub_this() = dynamic_cast(rhs); } - void accept(nbt_visitor& visitor) override final; - void accept(const_nbt_visitor& visitor) const override final; + 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; + bool equals(const tag& rhs) const override final { return sub_this() == static_cast(rhs); } Sub& sub_this() { return static_cast(*this); } const Sub& sub_this() const { return static_cast(*this); } @@ -57,48 +57,6 @@ namespace detail template crtp_tag::~crtp_tag() noexcept {} - template - tag_type crtp_tag::get_type() const noexcept - { - return Sub::type; - } - - template - std::unique_ptr crtp_tag::clone() const& - { - return make_unique(sub_this()); - } - - template - std::unique_ptr crtp_tag::move_clone() && - { - return make_unique(std::move(sub_this())); - } - - template - tag& crtp_tag::assign(tag&& rhs) - { - return sub_this() = dynamic_cast(rhs); - } - - template - void crtp_tag::accept(nbt_visitor& visitor) - { - visitor.visit(sub_this()); - } - - template - void crtp_tag::accept(const_nbt_visitor& visitor) const - { - visitor.visit(sub_this()); - } - - template - bool crtp_tag::equals(const tag& rhs) const - { - return sub_this() == static_cast(rhs); - } - } } diff --git a/include/tag_primitive.h b/include/tag_primitive.h index b1bb9920c0..740f98a539 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -22,6 +22,9 @@ #include "crtp_tag.h" #include "primitive_detail.h" +#include "io/stream_reader.h" +#include +#include namespace nbt { @@ -42,16 +45,16 @@ public: static constexpr tag_type type = detail::get_primitive_type::value; //Constructor - tag_primitive(T value = 0) noexcept; + constexpr tag_primitive(T val = 0) noexcept: value(val) {} //Getters - operator T&(); - operator T() const; - T get() const; + operator T&() { return value; } + constexpr operator T() const { return value; } + constexpr T get() const { return value; } //Setters - tag_primitive& operator=(T value); - void set(T value); + tag_primitive& operator=(T val) { value = val; return *this; } + void set(T val) { value = val; } void read_payload(io::stream_reader& reader) override; @@ -59,8 +62,10 @@ private: T value; }; -template bool operator==(const tag_primitive& lhs, const tag_primitive& rhs); -template bool operator!=(const tag_primitive& lhs, const tag_primitive& rhs); +template bool operator==(const tag_primitive& lhs, const tag_primitive& rhs) +{ return lhs.get() == rhs.get(); } +template bool operator!=(const tag_primitive& lhs, const tag_primitive& rhs) +{ return !(lhs == rhs); } //Typedefs that should be used instead of the template tag_primitive. typedef tag_primitive tag_byte; @@ -70,6 +75,18 @@ typedef tag_primitive tag_long; typedef tag_primitive tag_float; typedef tag_primitive tag_double; +template +void tag_primitive::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()); + } +} + } #endif // TAG_PRIMITIVE_H_INCLUDED diff --git a/src/tag.cpp b/src/tag.cpp index 5b6f1142e2..b0825b52e6 100644 --- a/src/tag.cpp +++ b/src/tag.cpp @@ -20,6 +20,7 @@ #include "tag.h" #include "nbt_tags.h" #include "text/json_formatter.h" +#include #include #include #include @@ -27,6 +28,9 @@ namespace nbt { +static_assert(std::numeric_limits::is_iec559 && std::numeric_limits::is_iec559, + "The floating point values for NBT must conform to IEC 559/IEEE 754"); + bool is_valid_type(int type, bool allow_end) { return (allow_end ? 0 : 1) <= type && type <= 11; diff --git a/src/tag_primitive.cpp b/src/tag_primitive.cpp deleted file mode 100644 index dd07fea662..0000000000 --- a/src/tag_primitive.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * libnbt++ - A library for the Minecraft Named Binary Tag format. - * Copyright (C) 2013, 2015 ljfa-ag - * - * This file is part of libnbt++. - * - * libnbt++ is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * libnbt++ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with libnbt++. If not, see . - */ -#include "tag_primitive.h" -#include "io/stream_reader.h" -#include -#include - -namespace nbt -{ - -static_assert(std::numeric_limits::is_iec559 && std::numeric_limits::is_iec559, - "The floating point values for NBT must conform to IEC 559/IEEE 754"); - -template -tag_primitive::tag_primitive(T val) noexcept: - value(val) -{} - -template -tag_primitive& tag_primitive::operator=(T val) -{ - value = val; - return *this; -} - -template -void tag_primitive::set(T val) -{ - value = val; -} - -template -tag_primitive::operator T&() -{ - return value; -} - -template -tag_primitive::operator T() const -{ - return value; -} - -template -T tag_primitive::get() const -{ - return value; -} - -template -void tag_primitive::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 -bool operator==(const tag_primitive& lhs, const tag_primitive& rhs) -{ - return lhs.get() == rhs.get(); -} - -template -bool operator!=(const tag_primitive& lhs, const tag_primitive& rhs) -{ - return !(lhs == rhs); -} - -//Enforce template instantiations -template class tag_primitive; -template class tag_primitive; -template class tag_primitive; -template class tag_primitive; -template class tag_primitive; -template class tag_primitive; -template bool operator== (const tag_primitive& , const tag_primitive&); -template bool operator==(const tag_primitive&, const tag_primitive&); -template bool operator==(const tag_primitive&, const tag_primitive&); -template bool operator==(const tag_primitive&, const tag_primitive&); -template bool operator== (const tag_primitive& , const tag_primitive&); -template bool operator== (const tag_primitive& , const tag_primitive&); -template bool operator!= (const tag_primitive& , const tag_primitive&); -template bool operator!=(const tag_primitive&, const tag_primitive&); -template bool operator!=(const tag_primitive&, const tag_primitive&); -template bool operator!=(const tag_primitive&, const tag_primitive&); -template bool operator!= (const tag_primitive& , const tag_primitive&); -template bool operator!= (const tag_primitive& , const tag_primitive&); - -} -- cgit 0.0.5-2-1-g0f52 From 9c2cea3e85872e1e5005b51b16fa1e6876bf1011 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 11 Aug 2015 11:26:22 +0200 Subject: Add json_formatter argument to json_fmt_visitor which will be useful later when it becomes configurable --- src/text/json_formatter.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/text/json_formatter.cpp b/src/text/json_formatter.cpp index 44acd61713..543a9db583 100644 --- a/src/text/json_formatter.cpp +++ b/src/text/json_formatter.cpp @@ -35,7 +35,9 @@ namespace //anonymous class json_fmt_visitor : public const_nbt_visitor { public: - json_fmt_visitor(std::ostream& os): os(os) {} + json_fmt_visitor(std::ostream& os, const json_formatter& fmt): + os(os) + {} void visit(const tag_byte& b) override { os << static_cast(b.get()) << "b"; } //We don't want to print a character @@ -185,7 +187,7 @@ namespace //anonymous void json_formatter::write(std::ostream& os, const tag& t) const { - json_fmt_visitor v(os); + json_fmt_visitor v(os, *this); t.accept(v); } -- cgit 0.0.5-2-1-g0f52 From a830cf0ef509ad0b0145df4f0a01f4905e360eb7 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 11 Aug 2015 11:38:53 +0200 Subject: Rename json_formatter::write to print --- include/text/json_formatter.h | 2 +- src/tag.cpp | 2 +- src/text/json_formatter.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/text/json_formatter.h b/include/text/json_formatter.h index 6a3e6aff00..c0d1bfc396 100644 --- a/include/text/json_formatter.h +++ b/include/text/json_formatter.h @@ -36,7 +36,7 @@ namespace text class json_formatter { public: - void write(std::ostream& os, const tag& t) const; + void print(std::ostream& os, const tag& t) const; }; } diff --git a/src/tag.cpp b/src/tag.cpp index b0825b52e6..df4d8cb555 100644 --- a/src/tag.cpp +++ b/src/tag.cpp @@ -98,7 +98,7 @@ std::ostream& operator<<(std::ostream& os, tag_type tt) std::ostream& operator<<(std::ostream& os, const tag& t) { static const text::json_formatter formatter; - formatter.write(os, t); + formatter.print(os, t); return os; } diff --git a/src/text/json_formatter.cpp b/src/text/json_formatter.cpp index 543a9db583..9b47210c6a 100644 --- a/src/text/json_formatter.cpp +++ b/src/text/json_formatter.cpp @@ -185,7 +185,7 @@ namespace //anonymous }; } -void json_formatter::write(std::ostream& os, const tag& t) const +void json_formatter::print(std::ostream& os, const tag& t) const { json_fmt_visitor v(os, *this); t.accept(v); -- cgit 0.0.5-2-1-g0f52 From 7c320c9c058d6f7e151860a29cb35b136011e955 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 11 Aug 2015 14:47:00 +0200 Subject: Add overload for value_initializer with nullptr --- include/value_initializer.h | 1 + src/value_initializer.cpp | 1 + test/nbttest.cpp | 1 + 3 files changed, 3 insertions(+) diff --git a/include/value_initializer.h b/include/value_initializer.h index 42cf77da77..b8f6c8b7c9 100644 --- a/include/value_initializer.h +++ b/include/value_initializer.h @@ -47,6 +47,7 @@ public: value_initializer(std::unique_ptr&& t) noexcept; value_initializer(value&& val) noexcept; value_initializer(tag&& t); + value_initializer(nullptr_t); value_initializer(int8_t val); value_initializer(int16_t val); diff --git a/src/value_initializer.cpp b/src/value_initializer.cpp index 18a4f58d96..91bbde6cfb 100644 --- a/src/value_initializer.cpp +++ b/src/value_initializer.cpp @@ -26,6 +26,7 @@ namespace nbt value_initializer::value_initializer(std::unique_ptr&& t) noexcept: value(std::move(t)) {} value_initializer::value_initializer(value&& val) noexcept : value(std::move(val)) {} value_initializer::value_initializer(tag&& t) : value(std::move(t)) {} +value_initializer::value_initializer(nullptr_t) : value(nullptr) {} value_initializer::value_initializer(int8_t val) : value(tag_byte(val)) {} value_initializer::value_initializer(int16_t val) : value(tag_short(val)) {} diff --git a/test/nbttest.cpp b/test/nbttest.cpp index fd74995d67..7430a02770 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -284,6 +284,7 @@ void test_value() val1 = val1; val2 = val2; ASSERT(!val1); + ASSERT(val1 == value_initializer(nullptr)); ASSERT(val2 == tag_int(21)); val3 = tag_short(2); -- cgit 0.0.5-2-1-g0f52 From 97c69bd53bae79a897faef9d8224aacb8e3b2c90 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 11 Aug 2015 14:50:10 +0200 Subject: Create broader test case for formatter --- test/format_test.cpp | 56 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/test/format_test.cpp b/test/format_test.cpp index 9f79c66c60..b1638efd4c 100644 --- a/test/format_test.cpp +++ b/test/format_test.cpp @@ -19,22 +19,64 @@ */ #include "microtest.h" //#include "text/json_formatter.h" -#include "io/stream_reader.h" +//#include "io/stream_reader.h" #include #include +#include #include "nbt_tags.h" using namespace nbt; int main() { - std::ifstream file("bigtest_uncompr", std::ios::binary); - ASSERT(file); - std::string key; - std::unique_ptr comp; - std::tie(key, comp) = io::stream_reader(file).read_compound(); + //TODO: Write that into a file + tag_compound comp{ + {"byte", tag_byte(-128)}, + {"short", tag_short(-32768)}, + {"int", tag_int(-2147483648)}, + {"long", tag_long(-9223372036854775808U)}, + + {"float 1", 1.618034f}, + {"float 2", 6.626070e-34f}, + {"float 3", 2.273737e+29f}, + {"float 4", -std::numeric_limits::infinity()}, + {"float 5", std::numeric_limits::quiet_NaN()}, + + {"double 1", 3.141592653589793}, + {"double 2", 1.749899444387479e-193}, + {"double 3", 2.850825855152578e+175}, + {"double 4", -std::numeric_limits::infinity()}, + {"double 5", std::numeric_limits::quiet_NaN()}, + + {"string 1", "Hello World! \u00E4\u00F6\u00FC\u00DF"}, + {"string 2", "String with\nline breaks\tand tabs"}, + + {"byte array", tag_byte_array{12, 13, 14, 15, 16}}, + {"int array", tag_int_array{0x0badc0de, -0x0dedbeef, 0x1badbabe}}, + + {"list (empty)", tag_list::of({})}, + {"list (float)", tag_list{2.0f, 1.0f, 0.5f, 0.25f}}, + {"list (list)", tag_list::of({ + {}, + {4, 5, 6}, + {tag_compound{{"egg", "ham"}}, tag_compound{{"foo", "bar"}}} + })}, + {"list (compound)", tag_list::of({ + {{"created-on", 42}, {"names", tag_list{"Compound", "tag", "#0"}}}, + {{"created-on", 45}, {"names", tag_list{"Compound", "tag", "#1"}}} + })}, + + {"compound (empty)", tag_compound()}, + {"compound (nested)", tag_compound{ + {"key", "value"}, + {"key with \u00E4\u00F6\u00FC", tag_byte(-1)}, + {"key with\nnewline and\ttab", tag_compound{}} + }}, + + {"null", nullptr} + }; std::cout << "----- default operator<<:\n"; - std::cout << *comp; + std::cout << comp; std::cout << "\n-----" << std::endl; } -- cgit 0.0.5-2-1-g0f52 From f46c0e85b30bed9cda77d63e32336d67f0ec8853 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 11 Aug 2015 16:32:41 +0200 Subject: Make compound iterator test independent of order Add some comments to it --- test/nbttest.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/test/nbttest.cpp b/test/nbttest.cpp index 7430a02770..1cf44a69ba 100644 --- a/test/nbttest.cpp +++ b/test/nbttest.cpp @@ -21,6 +21,7 @@ #include "nbt_tags.h" #include "nbt_visitor.h" #include +#include #include using namespace nbt; @@ -137,6 +138,7 @@ void test_tag_compound() {"list", tag_list{16, 17}} }; + //Test assignments and conversions, and exceptions on bad conversions ASSERT(comp["foo"].get_type() == tag_type::Short); ASSERT(static_cast(comp["foo"]) == 12); ASSERT(static_cast(comp.at("foo")) == int16_t(12)); @@ -160,6 +162,7 @@ void test_tag_compound() ASSERT(static_cast(comp["baz"]) == -2.0); EXPECT_EXCEPTION(static_cast(comp["baz"]), std::bad_cast); + //Test nested access comp["quux"] = tag_compound{{"Hello", "World"}, {"zero", 0}}; ASSERT(comp.at("quux").get_type() == tag_type::Compound); ASSERT(std::string(comp["quux"].at("Hello")) == "World"); @@ -168,6 +171,7 @@ void test_tag_compound() EXPECT_EXCEPTION(comp.at("nothing"), std::out_of_range); + //Test equality comparisons tag_compound comp2{ {"foo", int16_t(32)}, {"bar", "barbaz"}, @@ -180,17 +184,21 @@ void test_tag_compound() ASSERT(comp != comp2["quux"]); ASSERT(dynamic_cast(comp["quux"].get()) == comp2["quux"]); - ASSERT(comp2.size() == 5); - const char* keys[] = {"bar", "baz", "foo", "list", "quux"}; //alphabetic order + //Test whether begin() through end() goes through all the keys and their + //values. The order of iteration is irrelevant there. + std::set keys{"bar", "baz", "foo", "list", "quux"}; + ASSERT(comp2.size() == keys.size()); unsigned i = 0; for(const std::pair& val: comp2) { ASSERT(i < comp2.size()); - ASSERT(val.first == keys[i]); - ASSERT(val.second == comp2[keys[i]]); + ASSERT(keys.count(val.first)); + ASSERT(val.second == comp2[val.first]); ++i; } + ASSERT(i == comp2.size()); + //Test erasing and has_key ASSERT(comp.erase("nothing") == false); ASSERT(comp.has_key("quux")); ASSERT(comp.has_key("quux", tag_type::Compound)); @@ -205,6 +213,7 @@ void test_tag_compound() comp.clear(); ASSERT(comp == tag_compound{}); + //Test inserting values ASSERT(comp.put("abc", tag_double(6.0)).second == true); ASSERT(comp.put("abc", tag_long(-28)).second == false); ASSERT(comp.insert("ghi", tag_string("world")).second == true); -- cgit 0.0.5-2-1-g0f52 From 64ecc6fc635eaff48177dd58ede7b521a98f445d Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 11 Aug 2015 17:11:30 +0200 Subject: Make value_initializer constructor noexcept --- include/value_initializer.h | 2 +- src/value_initializer.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/value_initializer.h b/include/value_initializer.h index b8f6c8b7c9..55020da35c 100644 --- a/include/value_initializer.h +++ b/include/value_initializer.h @@ -45,9 +45,9 @@ class value_initializer : public value { public: value_initializer(std::unique_ptr&& t) noexcept; + value_initializer(nullptr_t) noexcept; value_initializer(value&& val) noexcept; value_initializer(tag&& t); - value_initializer(nullptr_t); value_initializer(int8_t val); value_initializer(int16_t val); diff --git a/src/value_initializer.cpp b/src/value_initializer.cpp index 91bbde6cfb..0125608480 100644 --- a/src/value_initializer.cpp +++ b/src/value_initializer.cpp @@ -24,9 +24,9 @@ namespace nbt { value_initializer::value_initializer(std::unique_ptr&& t) noexcept: value(std::move(t)) {} +value_initializer::value_initializer(nullptr_t) noexcept : value(nullptr) {} value_initializer::value_initializer(value&& val) noexcept : value(std::move(val)) {} value_initializer::value_initializer(tag&& t) : value(std::move(t)) {} -value_initializer::value_initializer(nullptr_t) : value(nullptr) {} value_initializer::value_initializer(int8_t val) : value(tag_byte(val)) {} value_initializer::value_initializer(int16_t val) : value(tag_short(val)) {} -- cgit 0.0.5-2-1-g0f52 From 82a52081693135ec6b0294994db88b47f42e706b Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 12 Aug 2015 11:42:15 +0200 Subject: Add typedef for map type used by tag_compound Allows easy switch to unordered_map --- include/tag_compound.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/tag_compound.h b/include/tag_compound.h index 60376d2446..7ec53e4f80 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -31,10 +31,12 @@ namespace nbt ///Tag that contains multiple unordered named tags of arbitrary types class tag_compound final : public detail::crtp_tag { + typedef std::map map_t_; + public: //Iterator types - typedef std::map::iterator iterator; - typedef std::map::const_iterator const_iterator; + 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; @@ -123,7 +125,7 @@ public: friend bool operator!=(const tag_compound& lhs, const tag_compound& rhs); private: - std::map tags; + map_t_ tags; }; template -- cgit 0.0.5-2-1-g0f52 From 84e7f93b8760980af5fea0475b4550f8be04dae2 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 12 Aug 2015 13:07:49 +0200 Subject: Lay foundations for stream writing --- CMakeLists.txt | 1 + include/io/stream_writer.h | 90 ++++++++++++++++++++++++++++++++++++++++++++++ src/io/stream_writer.cpp | 30 ++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 include/io/stream_writer.h create mode 100644 src/io/stream_writer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8524bcdef4..e5c971321d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ add_library(nbt++ STATIC src/value_initializer.cpp src/io/stream_reader.cpp + src/io/stream_writer.cpp src/text/json_formatter.cpp) diff --git a/include/io/stream_writer.h b/include/io/stream_writer.h new file mode 100644 index 0000000000..ea3dcd1c58 --- /dev/null +++ b/include/io/stream_writer.h @@ -0,0 +1,90 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#ifndef STREAM_WRITER_H_INCLUDED +#define STREAM_WRITER_H_INCLUDED + +#include "tag.h" +#include "endian_str.h" +#include +#include + +namespace nbt +{ +namespace io +{ + +///Exception that gets thrown when writing is not successful +class output_error : public std::runtime_error +{ + using std::runtime_error::runtime_error; +}; + +/** + * @brief Helper class for writing NBT tags to output streams + */ +class stream_writer +{ +public: + /** + * @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; + + ///Returns the stream + std::ostream& get_ostr() const; + ///Returns the byte order + endian::endian get_endian() const; + + /** + * @brief Writes a tag type to the stream + */ + void write_type(tag_type tt); + + /** + * @brief Writes a binary number to the stream + */ + template + 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. + */ + void write_string(const std::string& str); + +private: + std::ostream& os; + const endian::endian endian; +}; + +template +void stream_writer::write_num(T x) +{ + endian::write(os, x, endian); +} + +} +} + +#endif // STREAM_WRITER_H_INCLUDED diff --git a/src/io/stream_writer.cpp b/src/io/stream_writer.cpp new file mode 100644 index 0000000000..dd90c83322 --- /dev/null +++ b/src/io/stream_writer.cpp @@ -0,0 +1,30 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include "io/stream_writer.h" + +namespace nbt +{ +namespace io +{ + + + +} +} -- cgit 0.0.5-2-1-g0f52 From c74ee0c95f38012a0b452c709c512fb0cac735fa Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 12 Aug 2015 13:34:09 +0200 Subject: Add basic tests for stream_writer --- test/CMakeLists.txt | 4 ++++ test/write_test.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 test/write_test.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 89a445d402..539efc5c1f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,6 +17,10 @@ add_custom_command(TARGET read_test POST_BUILD copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/testfiles ${CMAKE_CURRENT_BINARY_DIR}) add_test(read_test read_test) +add_executable(write_test write_test.cpp) +target_link_libraries(write_test nbt++) +add_test(write_test write_test) + add_executable(format_test format_test.cpp) target_link_libraries(format_test nbt++) add_test(format_test format_test) diff --git a/test/write_test.cpp b/test/write_test.cpp new file mode 100644 index 0000000000..5c5cab055b --- /dev/null +++ b/test/write_test.cpp @@ -0,0 +1,61 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include "microtest.h" +#include "io/stream_writer.h" +#include +#include + +using namespace nbt; + +void test_stream_writer_big() +{ + std::ostringstream os; + nbt::io::stream_writer writer(os); + + ASSERT(&writer.get_ostr() == &os); + ASSERT(writer.get_endian() == endian::big); + + writer.write_type(tag_type::End); + writer.write_type(tag_type::Long); + writer.write_type(tag_type::Int_Array); + + writer.write_num(int64_t(0x0102030405060708)); + + writer.write_string("foobar"); + + ASSERT(os); + std::string expected{ + 0, //tag_type::End + 4, //tag_type::Long + 11, //tag_type::Int_Array + + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, //0x0102030405060708 in Big Endian + + 0x00, 0x06, //string length in Big Endian + 'f', 'o', 'o', 'b', 'a', 'r' + }; + ASSERT(os.str() == expected); + std::clog << "test_stream_writer_big passed" << std::endl; +} + +int main() +{ + test_stream_writer_big(); +} -- cgit 0.0.5-2-1-g0f52 From 2f0bf929fcecc47c5a5d2b3f151d8f187fb5d7f9 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 12 Aug 2015 13:54:17 +0200 Subject: Use string constructor from initializer_list No need for null terminators --- test/endian_str_test.cpp | 18 +++++++++--------- test/read_test.cpp | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/test/endian_str_test.cpp b/test/endian_str_test.cpp index d519dee049..c8db4ea674 100644 --- a/test/endian_str_test.cpp +++ b/test/endian_str_test.cpp @@ -39,7 +39,7 @@ void test_uint() write_big (str, uint32_t(0x090A0B0C)); write (str, uint64_t(0x090A0B0C0D0E0F10), endian::big); - const char expected[] { + std::string expected{ 1, 2, 1, 4, 3, 2, 1, @@ -48,8 +48,8 @@ void test_uint() 9, 9, 10, 9, 10, 11, 12, - 9, 10, 11, 12, 13, 14, 15, 16, - 0}; //Null terminator + 9, 10, 11, 12, 13, 14, 15, 16 + }; ASSERT(str.str() == expected); uint8_t u8; @@ -93,7 +93,7 @@ void test_sint() write (str, int32_t(-0x090A0B0C), endian::big); write_big (str, int64_t(-0x090A0B0C0D0E0F10)); - const char expected[] { //meh, stupid narrowing conversions + std::string expected{ //meh, stupid narrowing conversions '\xFF', '\xFE', '\xFE', '\xFC', '\xFC', '\xFD', '\xFE', @@ -102,8 +102,8 @@ void test_sint() '\xF7', '\xF6', '\xF6', '\xF6', '\xF5', '\xF4', '\xF4', - '\xF6', '\xF5', '\xF4', '\xF3', '\xF2', '\xF1', '\xF0', '\xF0', - 0}; //Null terminator + '\xF6', '\xF5', '\xF4', '\xF3', '\xF2', '\xF1', '\xF0', '\xF0' + }; ASSERT(str.str() == expected); int8_t i8; @@ -147,13 +147,13 @@ void test_float() write_big (str, fconst); write_big (str, dconst); - const char expected[] { + std::string expected{ '\x01', '\xEF', '\xCD', '\xAB', '\x05', '\x04', '\x03', '\x02', '\x01', '\xEF', '\xCD', '\xAB', '\xAB', '\xCD', '\xEF', '\x01', - '\xAB', '\xCD', '\xEF', '\x01', '\x02', '\x03', '\x04', '\x05', - 0}; //Null terminator + '\xAB', '\xCD', '\xEF', '\x01', '\x02', '\x03', '\x04', '\x05' + }; ASSERT(str.str() == expected); float f; diff --git a/test/read_test.cpp b/test/read_test.cpp index 0fa6259a15..b9d8c59f6c 100644 --- a/test/read_test.cpp +++ b/test/read_test.cpp @@ -28,7 +28,7 @@ using namespace nbt; void test_stream_reader_big() { - const char input[] { + std::string input{ 1, //tag_type::Byte 0, //tag_type::End 11, //tag_type::Int_Array @@ -40,7 +40,7 @@ void test_stream_reader_big() 0 //tag_type::End (invalid with allow_end = false) }; - std::istringstream is(std::string(input, sizeof input)); + std::istringstream is(input); nbt::io::stream_reader reader(is); ASSERT(&reader.get_istr() == &is); @@ -75,7 +75,7 @@ void test_stream_reader_big() void test_stream_reader_little() { - const char input[] { + std::string input{ 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, //0x0d0c0b0a09080706 in Little Endian 0x06, 0x00, //String length in Little Endian @@ -84,7 +84,7 @@ void test_stream_reader_little() 0x10, 0x00, //String length (intentionally too large) 'a', 'b', 'c', 'd' //unexpected EOF }; - std::istringstream is(std::string(input, sizeof input)); + std::istringstream is(input); nbt::io::stream_reader reader(is, endian::little); ASSERT(reader.get_endian() == endian::little); -- cgit 0.0.5-2-1-g0f52 From 5166cfb9b70f21b16193315cbdee69a507371a13 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 12 Aug 2015 14:13:22 +0200 Subject: Implement basic stream_writer methods --- include/io/stream_writer.h | 12 +++++++++--- src/io/stream_writer.cpp | 16 ++++++++++++++++ test/write_test.cpp | 1 + 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/include/io/stream_writer.h b/include/io/stream_writer.h index ea3dcd1c58..78d02cb2cc 100644 --- a/include/io/stream_writer.h +++ b/include/io/stream_writer.h @@ -42,17 +42,22 @@ class output_error : public std::runtime_error class stream_writer { public: + ///Maximum length of an NBT string (65535) + static constexpr size_t max_string_len = UINT16_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; + explicit stream_writer(std::ostream& os, endian::endian e = endian::big) noexcept: + os(os), endian(e) + {} ///Returns the stream - std::ostream& get_ostr() const; + std::ostream& get_ostr() const { return os; } ///Returns the byte order - endian::endian get_endian() const; + endian::endian get_endian() const { return endian; } /** * @brief Writes a tag type to the stream @@ -70,6 +75,7 @@ public: * * 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); diff --git a/src/io/stream_writer.cpp b/src/io/stream_writer.cpp index dd90c83322..26a6a7eb65 100644 --- a/src/io/stream_writer.cpp +++ b/src/io/stream_writer.cpp @@ -18,13 +18,29 @@ * along with libnbt++. If not, see . */ #include "io/stream_writer.h" +#include namespace nbt { namespace io { +void stream_writer::write_type(tag_type tt) +{ + write_num(static_cast(tt)); +} +void stream_writer::write_string(const std::string& str) +{ + if(str.size() > max_string_len) + { + std::ostringstream sstr; + sstr << "String is too long for NBT (" << str.size() << " > " << max_string_len << ")"; + throw std::length_error(sstr.str()); + } + write_num(static_cast(str.size())); + os.write(str.data(), str.size()); +} } } diff --git a/test/write_test.cpp b/test/write_test.cpp index 5c5cab055b..3f9327b978 100644 --- a/test/write_test.cpp +++ b/test/write_test.cpp @@ -51,6 +51,7 @@ void test_stream_writer_big() 0x00, 0x06, //string length in Big Endian 'f', 'o', 'o', 'b', 'a', 'r' }; + std::string s = os.str(); ASSERT(os.str() == expected); std::clog << "test_stream_writer_big passed" << std::endl; } -- cgit 0.0.5-2-1-g0f52 From 1178dbef6290d0a87d478ea06b11cf19e2e7b1d7 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 12 Aug 2015 14:17:56 +0200 Subject: Add basic tests for stream_writer in little endian --- test/write_test.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/write_test.cpp b/test/write_test.cpp index 3f9327b978..cb9890fd53 100644 --- a/test/write_test.cpp +++ b/test/write_test.cpp @@ -56,7 +56,31 @@ void test_stream_writer_big() std::clog << "test_stream_writer_big passed" << std::endl; } +void test_stream_writer_little() +{ + std::ostringstream os; + nbt::io::stream_writer writer(os, endian::little); + + ASSERT(writer.get_endian() == endian::little); + + writer.write_num(int32_t(0x0a0b0c0d)); + + writer.write_string("foobar"); + + ASSERT(os); + std::string expected{ + 0x0d, 0x0c, 0x0b, 0x0a, //0x0a0b0c0d in Little Endian + + 0x06, 0x00, //string length in Little Endian + 'f', 'o', 'o', 'b', 'a', 'r' + }; + std::string s = os.str(); + ASSERT(os.str() == expected); + std::clog << "test_stream_writer_little passed" << std::endl; +} + int main() { test_stream_writer_big(); + test_stream_writer_little(); } -- cgit 0.0.5-2-1-g0f52 From b67a3f9b0aa06984d68aee6754bd74bd7004fd97 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 12 Aug 2015 18:06:58 +0200 Subject: Add write_payload methods to stream_writer and tags --- include/io/stream_writer.h | 15 +++++++++++++-- include/tag.h | 10 +++++++++- include/tag_array.h | 1 + include/tag_compound.h | 1 + include/tag_list.h | 5 +++++ include/tag_primitive.h | 8 ++++++++ include/tag_string.h | 5 +++++ 7 files changed, 42 insertions(+), 3 deletions(-) diff --git a/include/io/stream_writer.h b/include/io/stream_writer.h index 78d02cb2cc..8c354cceba 100644 --- a/include/io/stream_writer.h +++ b/include/io/stream_writer.h @@ -23,18 +23,19 @@ #include "tag.h" #include "endian_str.h" #include -#include +//#include 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 Helper class for writing NBT tags to output streams @@ -59,6 +60,16 @@ public: ///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); + /** * @brief Writes a tag type to the stream */ diff --git a/include/tag.h b/include/tag.h index 3bf23dae8f..a2ae49628e 100644 --- a/include/tag.h +++ b/include/tag.h @@ -55,7 +55,10 @@ bool is_valid_type(int type, bool allow_end = false); class nbt_visitor; class const_nbt_visitor; namespace io -{ class stream_reader; } +{ + class stream_reader; + class stream_writer; +} ///Base class for all NBT tag classes class tag @@ -102,6 +105,11 @@ public: */ 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) diff --git a/include/tag_array.h b/include/tag_array.h index 5ce7dd366e..f2163f6486 100644 --- a/include/tag_array.h +++ b/include/tag_array.h @@ -105,6 +105,7 @@ public: const_iterator cend() const; void read_payload(io::stream_reader& reader) override; + void write_payload(io::stream_writer& writer) const override; private: std::vector data; diff --git a/include/tag_compound.h b/include/tag_compound.h index 7ec53e4f80..bcb0a6cec2 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -120,6 +120,7 @@ public: const_iterator cend() const; 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); friend bool operator!=(const tag_compound& lhs, const tag_compound& rhs); diff --git a/include/tag_list.h b/include/tag_list.h index dff89c4fcc..61ada4a5b9 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -163,6 +163,11 @@ public: * 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. + */ + void write_payload(io::stream_writer& writer) const override; /** * @brief Equality comparison for lists diff --git a/include/tag_primitive.h b/include/tag_primitive.h index 740f98a539..07ae985559 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -23,6 +23,7 @@ #include "crtp_tag.h" #include "primitive_detail.h" #include "io/stream_reader.h" +#include "io/stream_writer.h" #include #include @@ -57,6 +58,7 @@ public: 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; @@ -87,6 +89,12 @@ void tag_primitive::read_payload(io::stream_reader& reader) } } +template +void tag_primitive::write_payload(io::stream_writer& writer) const +{ + writer.write_num(value); +} + } #endif // TAG_PRIMITIVE_H_INCLUDED diff --git a/include/tag_string.h b/include/tag_string.h index 2a8a3d3f4e..ab084c44aa 100644 --- a/include/tag_string.h +++ b/include/tag_string.h @@ -52,6 +52,11 @@ public: void set(std::string&& 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; private: std::string value; -- cgit 0.0.5-2-1-g0f52 From 8b792c2a968fed130c9869db37db658e92a0a587 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 12 Aug 2015 18:40:16 +0200 Subject: Add tests for writing tag payloads --- test/write_test.cpp | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/test/write_test.cpp b/test/write_test.cpp index cb9890fd53..28cd29bb8c 100644 --- a/test/write_test.cpp +++ b/test/write_test.cpp @@ -19,7 +19,9 @@ */ #include "microtest.h" #include "io/stream_writer.h" +#include "nbt_tags.h" #include +#include #include using namespace nbt; @@ -79,8 +81,120 @@ void test_stream_writer_little() std::clog << "test_stream_writer_little passed" << std::endl; } +void test_write_tags_big() +{ + std::ostringstream os; + nbt::io::stream_writer writer(os); + + //tag_primitive + writer.write_payload(tag_byte(127)); + writer.write_payload(tag_short(32767)); + writer.write_payload(tag_int(2147483647)); + writer.write_payload(tag_long(9223372036854775807)); + + //Same values as in endian_str_test + writer.write_payload(tag_float(std::stof("-0xCDEF01p-63"))); + writer.write_payload(tag_double(std::stod("-0x1DEF0102030405p-375"))); + + ASSERT((os.str() == std::string{ + '\x7F', + '\x7F', '\xFF', + '\x7F', '\xFF', '\xFF', '\xFF', + '\x7F', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', + + '\xAB', '\xCD', '\xEF', '\x01', + '\xAB', '\xCD', '\xEF', '\x01', '\x02', '\x03', '\x04', '\x05' + })); + os.str(""); //clear and reuse the stream + + //tag_string + writer.write_payload(tag_string("barbaz")); + ASSERT((os.str() == std::string{ + 0x00, 0x06, //string length in Big Endian + 'b', 'a', 'r', 'b', 'a', 'z' + })); + + //tag_byte_array + os.str(""); + writer.write_payload(tag_byte_array{0, 1, 127, -128, -127}); + ASSERT((os.str() == std::string{ + 0x00, 0x00, 0x00, 0x05, //length in Big Endian + 0, 1, 127, -128, -127 + })); + os.str(""); + + //tag_int_array + writer.write_payload(tag_int_array{0x01020304, 0x05060708, 0x090a0b0c}); + ASSERT((os.str() == std::string{ + 0x00, 0x00, 0x00, 0x03, //length in Big Endian + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c + })); + os.str(""); + + //tag_list + writer.write_payload(tag_list()); //empty list with undetermined type, should be written as list of tag_end + writer.write_payload(tag_list(tag_type::Int)); //empty list of tag_int + writer.write_payload(tag_list{ //nested list + tag_list::of({0x3456, 0x789a}), + tag_list::of({0x0a, 0x0b, 0x0c, 0x0d}) + }); + ASSERT((os.str() == std::string{ + 0, //tag_type::End + 0x00, 0x00, 0x00, 0x00, //length + + 3, //tag_type::Int + 0x00, 0x00, 0x00, 0x00, //length + + 9, //tag_type::List + 0x00, 0x00, 0x00, 0x02, //length + //list 0 + 2, //tag_type::Short + 0x00, 0x00, 0x00, 0x02, //length + '\x34', '\x56', + '\x78', '\x9a', + //list 1 + 1, //tag_type::Byte + 0x00, 0x00, 0x00, 0x04, //length + 0x0a, + 0x0b, + 0x0c, + 0x0d + })); + os.str(""); + + //tag_compound + //Testing if writing compounds works properly is problematic because the + //order of the tags is not guaranteed. However with only two tags in a + //compound we only have two possible orderings. + writer.write_payload(tag_compound{}); + writer.write_payload(tag_compound{ + {"foo", "quux"}, + {"bar", tag_int(0x789abcde)} + }); + + std::string endtag{0x00}; + std::string subtag1{ + 8, //tag_type::String + 0x00, 0x03, //key length + 'f', 'o', 'o', + 0x00, 0x04, //string length + 'q', 'u', 'u', 'x' + }; + std::string subtag2{ + 3, //tag_type::Int + 0x00, 0x03, //key length + 'b', 'a', 'r', + '\x78', '\x9A', '\xBC', '\xDE' + }; + ASSERT(os.str() == endtag + subtag1 + subtag2 + endtag + || os.str() == endtag + subtag2 + subtag1 + endtag); +} + int main() { test_stream_writer_big(); test_stream_writer_little(); + test_write_tags_big(); } -- cgit 0.0.5-2-1-g0f52 From 1b9fd75fad70da33ad6b31a0da5f6b1c6d074849 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 12 Aug 2015 19:17:59 +0200 Subject: Implement write_payload and write_tag --- include/io/stream_writer.h | 4 ++-- include/tag_array.h | 4 ++++ include/tag_list.h | 1 + src/io/stream_writer.cpp | 6 ++++-- src/tag_array.cpp | 22 ++++++++++++++++++++++ src/tag_compound.cpp | 8 ++++++++ src/tag_list.cpp | 18 ++++++++++++++++++ src/tag_string.cpp | 6 ++++++ 8 files changed, 65 insertions(+), 4 deletions(-) diff --git a/include/io/stream_writer.h b/include/io/stream_writer.h index 8c354cceba..1502593055 100644 --- a/include/io/stream_writer.h +++ b/include/io/stream_writer.h @@ -68,12 +68,12 @@ public: /** * @brief Writes the given tag's payload into the stream */ - void write_payload(const tag& t); + void write_payload(const tag& t) { t.write_payload(*this); } /** * @brief Writes a tag type to the stream */ - void write_type(tag_type tt); + void write_type(tag_type tt) { write_num(static_cast(tt)); } /** * @brief Writes a binary number to the stream diff --git a/include/tag_array.h b/include/tag_array.h index f2163f6486..14dd4d4480 100644 --- a/include/tag_array.h +++ b/include/tag_array.h @@ -105,6 +105,10 @@ public: const_iterator cend() const; 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: diff --git a/include/tag_list.h b/include/tag_list.h index 61ada4a5b9..66f63e6ed9 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -166,6 +166,7 @@ public: /** * @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; diff --git a/src/io/stream_writer.cpp b/src/io/stream_writer.cpp index 26a6a7eb65..f21fdb701c 100644 --- a/src/io/stream_writer.cpp +++ b/src/io/stream_writer.cpp @@ -25,9 +25,11 @@ namespace nbt namespace io { -void stream_writer::write_type(tag_type tt) +void stream_writer::write_tag(const std::string& key, const tag& t) { - write_num(static_cast(tt)); + write_type(t.get_type()); + write_string(key); + write_payload(t); } void stream_writer::write_string(const std::string& str) diff --git a/src/tag_array.cpp b/src/tag_array.cpp index f83bce11f7..124c470bd1 100644 --- a/src/tag_array.cpp +++ b/src/tag_array.cpp @@ -19,6 +19,7 @@ */ #include "tag_array.h" #include "io/stream_reader.h" +#include "io/stream_writer.h" #include namespace nbt @@ -102,6 +103,7 @@ template auto tag_array::cbegin() const -> const_iterator { return d template auto tag_array::cend() const -> const_iterator { return data.cend(); } //Slightly different between byte_array and int_array +//Reading template<> void tag_array::read_payload(io::stream_reader& reader) { @@ -140,6 +142,26 @@ void tag_array::read_payload(io::stream_reader& reader) throw io::input_error("Error reading contents of tag_int_array"); } +//Writing +template<> +void tag_array::write_payload(io::stream_writer& writer) const +{ + if(size() > INT32_MAX) + throw std::length_error("Byte array is too large for NBT"); + writer.write_num(static_cast(size())); + writer.get_ostr().write(reinterpret_cast(data.data()), data.size()); +} + +template<> +void tag_array::write_payload(io::stream_writer& writer) const +{ + if(size() > INT32_MAX) + throw std::length_error("Int array is too large for NBT"); + writer.write_num(static_cast(size())); + for(int32_t i: data) + writer.write_num(i); +} + template bool operator==(const tag_array& lhs, const tag_array& rhs) { diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index 3581dd149b..d0f20512ae 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -19,6 +19,7 @@ */ #include "tag_compound.h" #include "io/stream_reader.h" +#include "io/stream_writer.h" #include #include @@ -120,6 +121,13 @@ void tag_compound::read_payload(io::stream_reader& reader) } } +void tag_compound::write_payload(io::stream_writer& writer) const +{ + for(const auto& pair: tags) + writer.write_tag(pair.first, pair.second); + writer.write_type(tag_type::End); +} + bool operator==(const tag_compound& lhs, const tag_compound& rhs) { return lhs.tags == rhs.tags; diff --git a/src/tag_list.cpp b/src/tag_list.cpp index f5a5b91758..dbdda2db77 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -20,6 +20,7 @@ #include "tag_list.h" #include "nbt_tags.h" #include "io/stream_reader.h" +#include "io/stream_writer.h" #include namespace nbt @@ -158,6 +159,23 @@ void tag_list::read_payload(io::stream_reader& reader) } } +void tag_list::write_payload(io::stream_writer& writer) const +{ + if(size() > INT32_MAX) + throw std::length_error("List is too large for NBT"); + writer.write_type(el_type_ != tag_type::Null + ? el_type_ + : tag_type::End); + writer.write_num(static_cast(size())); + for(const auto& val: tags) + { + //check if the value is of the correct type + if(val.get_type() != el_type_) + throw std::bad_cast(); + writer.write_payload(val); + } +} + bool operator==(const tag_list& lhs, const tag_list& rhs) { return lhs.el_type_ == rhs.el_type_ && lhs.tags == rhs.tags; diff --git a/src/tag_string.cpp b/src/tag_string.cpp index 83cb815c52..1a8f1cd5d2 100644 --- a/src/tag_string.cpp +++ b/src/tag_string.cpp @@ -19,6 +19,7 @@ */ #include "tag_string.h" #include "io/stream_reader.h" +#include "io/stream_writer.h" namespace nbt { @@ -90,6 +91,11 @@ void tag_string::read_payload(io::stream_reader& reader) } } +void tag_string::write_payload(io::stream_writer& writer) const +{ + writer.write_string(value); +} + bool operator==(const tag_string& lhs, const tag_string& rhs) { return lhs.get() == rhs.get(); -- cgit 0.0.5-2-1-g0f52 From 751d92893daa1d672da8b1bb5704bbed87000962 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 12 Aug 2015 19:50:51 +0200 Subject: Add tests for writing bigtest in Big and Little Endian --- include/io/stream_writer.h | 1 - test/write_test.cpp | 50 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/include/io/stream_writer.h b/include/io/stream_writer.h index 1502593055..c94a7d4ece 100644 --- a/include/io/stream_writer.h +++ b/include/io/stream_writer.h @@ -23,7 +23,6 @@ #include "tag.h" #include "endian_str.h" #include -//#include namespace nbt { diff --git a/test/write_test.cpp b/test/write_test.cpp index 28cd29bb8c..78d6d8eaa6 100644 --- a/test/write_test.cpp +++ b/test/write_test.cpp @@ -19,6 +19,7 @@ */ #include "microtest.h" #include "io/stream_writer.h" +#include "io/stream_reader.h" #include "nbt_tags.h" #include #include @@ -81,7 +82,7 @@ void test_stream_writer_little() std::clog << "test_stream_writer_little passed" << std::endl; } -void test_write_tags_big() +void test_write_payload_big() { std::ostringstream os; nbt::io::stream_writer writer(os); @@ -165,9 +166,10 @@ void test_write_tags_big() os.str(""); //tag_compound - //Testing if writing compounds works properly is problematic because the - //order of the tags is not guaranteed. However with only two tags in a - //compound we only have two possible orderings. + /* Testing if writing compounds works properly is problematic because the + order of the tags is not guaranteed. However with only two tags in a + compound we only have two possible orderings. + See below for a more thorough test that uses writing and re-reading. */ writer.write_payload(tag_compound{}); writer.write_payload(tag_compound{ {"foo", "quux"}, @@ -190,11 +192,49 @@ void test_write_tags_big() }; ASSERT(os.str() == endtag + subtag1 + subtag2 + endtag || os.str() == endtag + subtag2 + subtag1 + endtag); + + ASSERT(os); + std::clog << "test_write_payload_big passed" << std::endl; +} + +void test_write_bigtest() +{ + /* Like already stated above, because no order is guaranteed for + tag_compound, we cannot simply test it by writing into a stream and directly + comparing the output to a reference value. + Instead, we assume that reading already works correctly and re-read the + written tag. + Smaller-grained tests are already done above. */ + std::ifstream file("bigtest_uncompr", std::ios::binary); + const auto orig_pair = io::stream_reader(file).read_compound(); + std::stringstream sstr; + + //Write into stream in Big Endian + io::stream_writer(sstr).write_tag(orig_pair.first, *orig_pair.second); + ASSERT(sstr); + + //Read from stream in Big Endian and compare + auto written_pair = io::stream_reader(sstr).read_compound(); + ASSERT(orig_pair.first == written_pair.first); + ASSERT(*orig_pair.second == *written_pair.second); + + sstr.str(""); //Reset and reuse stream + //Write into stream in Little Endian + io::stream_writer(sstr, endian::little).write_tag(orig_pair.first, *orig_pair.second); + ASSERT(sstr); + + //Read from stream in Little Endian and compare + written_pair = io::stream_reader(sstr, endian::little).read_compound(); + ASSERT(orig_pair.first == written_pair.first); + ASSERT(*orig_pair.second == *written_pair.second); + + std::clog << "test_write_bigtest passed" << std::endl; } int main() { test_stream_writer_big(); test_stream_writer_little(); - test_write_tags_big(); + test_write_payload_big(); + test_write_bigtest(); } -- cgit 0.0.5-2-1-g0f52 From 3d442c00a07c68ba1e7189ef19fa0ed8a5923879 Mon Sep 17 00:00:00 2001 From: ljfa Date: Wed, 12 Aug 2015 22:20:40 +0200 Subject: Fix missing std:: for nullptr_t --- include/value_initializer.h | 2 +- src/value_initializer.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/value_initializer.h b/include/value_initializer.h index 55020da35c..843ee16885 100644 --- a/include/value_initializer.h +++ b/include/value_initializer.h @@ -45,7 +45,7 @@ class value_initializer : public value { public: value_initializer(std::unique_ptr&& t) noexcept; - value_initializer(nullptr_t) noexcept; + value_initializer(std::nullptr_t) noexcept; value_initializer(value&& val) noexcept; value_initializer(tag&& t); diff --git a/src/value_initializer.cpp b/src/value_initializer.cpp index 0125608480..0431bfaef2 100644 --- a/src/value_initializer.cpp +++ b/src/value_initializer.cpp @@ -24,7 +24,7 @@ namespace nbt { value_initializer::value_initializer(std::unique_ptr&& t) noexcept: value(std::move(t)) {} -value_initializer::value_initializer(nullptr_t) noexcept : value(nullptr) {} +value_initializer::value_initializer(std::nullptr_t) noexcept: value(nullptr) {} value_initializer::value_initializer(value&& val) noexcept : value(std::move(val)) {} value_initializer::value_initializer(tag&& t) : value(std::move(t)) {} -- cgit 0.0.5-2-1-g0f52 From baf4a77df0ac95386b80bd915c2926c86e2c5a46 Mon Sep 17 00:00:00 2001 From: ljfa Date: Thu, 13 Aug 2015 10:36:50 +0200 Subject: Bump CMake version --- CMakeLists.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e5c971321d..d67e0be642 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,6 @@ -cmake_minimum_required(VERSION 2.6) -project(libnbt++) -set(libnbt++_VERSION_MAJOR 2) -set(libnbt++_VERSION_MINOR 0) +cmake_minimum_required(VERSION 3.0) +project(libnbt++ + VERSION 2.0.1) add_definitions(-std=c++11) include_directories(include) -- cgit 0.0.5-2-1-g0f52 From aac94b91ad83ad8f7832976e6da55d15de6e54e1 Mon Sep 17 00:00:00 2001 From: ljfa Date: Thu, 13 Aug 2015 11:31:57 +0200 Subject: Add io::read_tag and write_tag functions --- CMakeLists.txt | 2 +- include/io/stream_reader.h | 20 ++++++++++++++++++++ include/io/stream_writer.h | 12 ++++++++++++ src/io/stream_reader.cpp | 10 ++++++++++ src/io/stream_writer.cpp | 5 +++++ test/read_test.cpp | 6 ++---- test/write_test.cpp | 10 +++++----- 7 files changed, 55 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d67e0be642..a9fdebbcf9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.0) project(libnbt++ - VERSION 2.0.1) + VERSION 2.1) add_definitions(-std=c++11) include_directories(include) diff --git a/include/io/stream_reader.h b/include/io/stream_reader.h index 926d2453c6..877f6498ed 100644 --- a/include/io/stream_reader.h +++ b/include/io/stream_reader.h @@ -39,8 +39,28 @@ class 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 +*/ +std::pair> 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 +*/ +std::pair> 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 stream_reader { diff --git a/include/io/stream_writer.h b/include/io/stream_writer.h index c94a7d4ece..c771426bb3 100644 --- a/include/io/stream_writer.h +++ b/include/io/stream_writer.h @@ -36,8 +36,20 @@ 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 +*/ +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 stream_writer { diff --git a/src/io/stream_reader.cpp b/src/io/stream_reader.cpp index 15f29dba1d..f6f30a5b49 100644 --- a/src/io/stream_reader.cpp +++ b/src/io/stream_reader.cpp @@ -27,6 +27,16 @@ namespace nbt namespace io { +std::pair> read_compound(std::istream& is, endian::endian e) +{ + return stream_reader(is, e).read_compound(); +} + +std::pair> read_tag(std::istream& is, endian::endian e) +{ + return stream_reader(is, e).read_tag(); +} + stream_reader::stream_reader(std::istream& is, endian::endian e) noexcept: is(is), endian(e) {} diff --git a/src/io/stream_writer.cpp b/src/io/stream_writer.cpp index f21fdb701c..ee8b0ee2b6 100644 --- a/src/io/stream_writer.cpp +++ b/src/io/stream_writer.cpp @@ -25,6 +25,11 @@ namespace nbt namespace io { +void write_tag(const std::string& key, const tag& t, std::ostream& os, endian::endian e) +{ + stream_writer(os, e).write_tag(key, t); +} + void stream_writer::write_tag(const std::string& key, const tag& t) { write_type(t.get_type()); diff --git a/test/read_test.cpp b/test/read_test.cpp index b9d8c59f6c..90ee392451 100644 --- a/test/read_test.cpp +++ b/test/read_test.cpp @@ -141,9 +141,8 @@ void test_read_bigtest() //Uses an extended variant of Notch's original bigtest file std::ifstream file("bigtest_uncompr", std::ios::binary); ASSERT(file); - nbt::io::stream_reader reader(file); - auto pair = reader.read_compound(); + auto pair = nbt::io::read_compound(file); ASSERT(pair.first == "Level"); verify_bigtest_structure(*pair.second); std::clog << "test_read_bigtest passed" << std::endl; @@ -154,9 +153,8 @@ void test_read_littletest() //Same as bigtest, but little endian std::ifstream file("littletest_uncompr", std::ios::binary); ASSERT(file); - nbt::io::stream_reader reader(file, endian::little); - auto pair = reader.read_compound(); + auto pair = nbt::io::read_compound(file, endian::little); ASSERT(pair.first == "Level"); ASSERT(pair.second->get_type() == tag_type::Compound); verify_bigtest_structure(*pair.second); diff --git a/test/write_test.cpp b/test/write_test.cpp index 78d6d8eaa6..d6eac53a53 100644 --- a/test/write_test.cpp +++ b/test/write_test.cpp @@ -206,25 +206,25 @@ void test_write_bigtest() written tag. Smaller-grained tests are already done above. */ std::ifstream file("bigtest_uncompr", std::ios::binary); - const auto orig_pair = io::stream_reader(file).read_compound(); + const auto orig_pair = io::read_compound(file); std::stringstream sstr; //Write into stream in Big Endian - io::stream_writer(sstr).write_tag(orig_pair.first, *orig_pair.second); + io::write_tag(orig_pair.first, *orig_pair.second, sstr); ASSERT(sstr); //Read from stream in Big Endian and compare - auto written_pair = io::stream_reader(sstr).read_compound(); + auto written_pair = io::read_compound(sstr); ASSERT(orig_pair.first == written_pair.first); ASSERT(*orig_pair.second == *written_pair.second); sstr.str(""); //Reset and reuse stream //Write into stream in Little Endian - io::stream_writer(sstr, endian::little).write_tag(orig_pair.first, *orig_pair.second); + io::write_tag(orig_pair.first, *orig_pair.second, sstr, endian::little); ASSERT(sstr); //Read from stream in Little Endian and compare - written_pair = io::stream_reader(sstr, endian::little).read_compound(); + written_pair = io::read_compound(sstr, endian::little); ASSERT(orig_pair.first == written_pair.first); ASSERT(*orig_pair.second == *written_pair.second); -- cgit 0.0.5-2-1-g0f52 From 5e057e152e214d7c92a03192e6c3a235df5961ea Mon Sep 17 00:00:00 2001 From: ljfa Date: Thu, 13 Aug 2015 14:50:33 +0200 Subject: Set failbit on ostream in case of error --- src/io/stream_writer.cpp | 1 + src/tag_array.cpp | 6 ++++++ src/tag_list.cpp | 6 ++++++ test/write_test.cpp | 20 ++++++++++++++++++-- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/io/stream_writer.cpp b/src/io/stream_writer.cpp index ee8b0ee2b6..036c5d40ac 100644 --- a/src/io/stream_writer.cpp +++ b/src/io/stream_writer.cpp @@ -41,6 +41,7 @@ void stream_writer::write_string(const std::string& str) { if(str.size() > max_string_len) { + os.setstate(std::ios::failbit); std::ostringstream sstr; sstr << "String is too long for NBT (" << str.size() << " > " << max_string_len << ")"; throw std::length_error(sstr.str()); diff --git a/src/tag_array.cpp b/src/tag_array.cpp index 124c470bd1..41f00eedc3 100644 --- a/src/tag_array.cpp +++ b/src/tag_array.cpp @@ -147,7 +147,10 @@ template<> void tag_array::write_payload(io::stream_writer& writer) const { if(size() > INT32_MAX) + { + writer.get_ostr().setstate(std::ios::failbit); throw std::length_error("Byte array is too large for NBT"); + } writer.write_num(static_cast(size())); writer.get_ostr().write(reinterpret_cast(data.data()), data.size()); } @@ -156,7 +159,10 @@ template<> void tag_array::write_payload(io::stream_writer& writer) const { if(size() > INT32_MAX) + { + writer.get_ostr().setstate(std::ios::failbit); throw std::length_error("Int array is too large for NBT"); + } writer.write_num(static_cast(size())); for(int32_t i: data) writer.write_num(i); diff --git a/src/tag_list.cpp b/src/tag_list.cpp index dbdda2db77..d2aa01d314 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -162,7 +162,10 @@ void tag_list::read_payload(io::stream_reader& reader) void tag_list::write_payload(io::stream_writer& writer) const { if(size() > INT32_MAX) + { + writer.get_ostr().setstate(std::ios::failbit); throw std::length_error("List is too large for NBT"); + } writer.write_type(el_type_ != tag_type::Null ? el_type_ : tag_type::End); @@ -171,7 +174,10 @@ void tag_list::write_payload(io::stream_writer& writer) const { //check if the value is of the correct type if(val.get_type() != el_type_) + { + writer.get_ostr().setstate(std::ios::failbit); throw std::bad_cast(); + } writer.write_payload(val); } } diff --git a/test/write_test.cpp b/test/write_test.cpp index d6eac53a53..9445e4ab70 100644 --- a/test/write_test.cpp +++ b/test/write_test.cpp @@ -54,8 +54,11 @@ void test_stream_writer_big() 0x00, 0x06, //string length in Big Endian 'f', 'o', 'o', 'b', 'a', 'r' }; - std::string s = os.str(); ASSERT(os.str() == expected); + + //too long for NBT + EXPECT_EXCEPTION(writer.write_string(std::string(65536, '.')), std::length_error); + ASSERT(!os); std::clog << "test_stream_writer_big passed" << std::endl; } @@ -77,8 +80,10 @@ void test_stream_writer_little() 0x06, 0x00, //string length in Little Endian 'f', 'o', 'o', 'b', 'a', 'r' }; - std::string s = os.str(); ASSERT(os.str() == expected); + + EXPECT_EXCEPTION(writer.write_string(std::string(65536, '.')), std::length_error); + ASSERT(!os); std::clog << "test_stream_writer_little passed" << std::endl; } @@ -114,6 +119,9 @@ void test_write_payload_big() 0x00, 0x06, //string length in Big Endian 'b', 'a', 'r', 'b', 'a', 'z' })); + EXPECT_EXCEPTION(writer.write_payload(tag_string(std::string(65536, '.'))), std::length_error); + ASSERT(!os); + os.clear(); //tag_byte_array os.str(""); @@ -193,7 +201,15 @@ void test_write_payload_big() ASSERT(os.str() == endtag + subtag1 + subtag2 + endtag || os.str() == endtag + subtag2 + subtag1 + endtag); + //Now for write_tag: + os.str(""); + writer.write_tag("foo", tag_string("quux")); + ASSERT(os.str() == subtag1); ASSERT(os); + + //too long key for NBT + EXPECT_EXCEPTION(writer.write_tag(std::string(65536, '.'), tag_long(-1)), std::length_error); + ASSERT(!os); std::clog << "test_write_payload_big passed" << std::endl; } -- cgit 0.0.5-2-1-g0f52 From 8c7be11e9557f06bfcc6d5ebfe5d046fa218e149 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 20 Aug 2015 18:56:42 +0200 Subject: Make building tests optional --- CMakeLists.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a9fdebbcf9..99b5e44971 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.0) project(libnbt++ VERSION 2.1) +option(NBT_BUILD_TESTS "Build the unit tests" ON) + add_definitions(-std=c++11) include_directories(include) add_library(nbt++ STATIC @@ -19,5 +21,7 @@ add_library(nbt++ STATIC src/text/json_formatter.cpp) -enable_testing() -add_subdirectory(test) +if(NBT_BUILD_TESTS) + enable_testing() + add_subdirectory(test) +endif() -- cgit 0.0.5-2-1-g0f52 From 7fbf75e5f8c0c98f77626520aab32c3fc75e88cf Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 20 Aug 2015 21:24:58 +0200 Subject: Make nbttest use CxxTest --- CMakeLists.txt | 6 +- test/CMakeLists.txt | 5 +- test/nbttest.cpp | 495 ---------------------------------------------------- test/nbttest.h | 476 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 482 insertions(+), 500 deletions(-) delete mode 100644 test/nbttest.cpp create mode 100644 test/nbttest.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 99b5e44971..9a2eb71cb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,6 @@ cmake_minimum_required(VERSION 3.0) project(libnbt++ VERSION 2.1) -option(NBT_BUILD_TESTS "Build the unit tests" ON) - add_definitions(-std=c++11) include_directories(include) add_library(nbt++ STATIC @@ -21,6 +19,10 @@ add_library(nbt++ STATIC src/text/json_formatter.cpp) +find_package(CxxTest) + +option(NBT_BUILD_TESTS "Build the unit tests. Requires CxxTest." ${CXXTEST_FOUND}) + if(NBT_BUILD_TESTS) enable_testing() add_subdirectory(test) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 539efc5c1f..8b26efcab9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,10 +1,9 @@ enable_testing() -include_directories(.) include_directories(${libnbt++_SOURCE_DIR}/include) +include_directories(${CXXTEST_INCLUDE_DIR}) -add_executable(nbttest nbttest.cpp) +CXXTEST_ADD_TEST(nbttest nbttest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/nbttest.h) target_link_libraries(nbttest nbt++) -add_test(nbttest nbttest) add_executable(endian_str_test endian_str_test.cpp) target_link_libraries(endian_str_test nbt++) diff --git a/test/nbttest.cpp b/test/nbttest.cpp deleted file mode 100644 index 1cf44a69ba..0000000000 --- a/test/nbttest.cpp +++ /dev/null @@ -1,495 +0,0 @@ -/* - * libnbt++ - A library for the Minecraft Named Binary Tag format. - * Copyright (C) 2013, 2015 ljfa-ag - * - * This file is part of libnbt++. - * - * libnbt++ is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * libnbt++ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with libnbt++. If not, see . - */ -#include "microtest.h" -#include "nbt_tags.h" -#include "nbt_visitor.h" -#include -#include -#include - -using namespace nbt; - -void test_tag() -{ - ASSERT(!is_valid_type(-1)); - ASSERT(!is_valid_type(0)); - ASSERT(is_valid_type(0, true)); - ASSERT(is_valid_type(1)); - ASSERT(is_valid_type(5, false)); - ASSERT(is_valid_type(7, true)); - ASSERT(is_valid_type(11)); - ASSERT(!is_valid_type(12)); - - ASSERT(*tag::create(tag_type::Byte) == tag_byte()); - EXPECT_EXCEPTION(tag::create(tag_type::Null), std::invalid_argument); - EXPECT_EXCEPTION(tag::create(tag_type::End), std::invalid_argument); - - tag_string tstr("foo"); - auto cl = tstr.clone(); - ASSERT(tstr.get() == "foo"); - ASSERT(tstr == *cl); - - cl = std::move(tstr).clone(); - ASSERT(*cl == tag_string("foo")); - ASSERT(*cl != tag_string("bar")); - - cl = std::move(*cl).move_clone(); - ASSERT(*cl == tag_string("foo")); - - tstr.assign(tag_string("bar")); - EXPECT_EXCEPTION(tstr.assign(tag_int(6)), std::bad_cast); - ASSERT(tstr.get() == "bar"); - - ASSERT(&tstr.as() == &tstr); - EXPECT_EXCEPTION(tstr.as(), std::bad_cast); - std::clog << "test_tag passed" << std::endl; -} - -void test_get_type() -{ - ASSERT(tag_byte().get_type() == tag_type::Byte); - ASSERT(tag_short().get_type() == tag_type::Short); - ASSERT(tag_int().get_type() == tag_type::Int); - ASSERT(tag_long().get_type() == tag_type::Long); - ASSERT(tag_float().get_type() == tag_type::Float); - ASSERT(tag_double().get_type() == tag_type::Double); - ASSERT(tag_byte_array().get_type() == tag_type::Byte_Array); - ASSERT(tag_string().get_type() == tag_type::String); - ASSERT(tag_list().get_type() == tag_type::List); - ASSERT(tag_compound().get_type() == tag_type::Compound); - ASSERT(tag_int_array().get_type() == tag_type::Int_Array); - std::clog << "test_get_type passed" << std::endl; -} - -void test_tag_primitive() -{ - tag_int tag(6); - ASSERT(tag.get() == 6); - int& ref = tag; - ref = 12; - ASSERT(tag == 12); - ASSERT(tag != 6); - tag.set(24); - ASSERT(ref == 24); - tag = 7; - ASSERT(7 == static_cast(tag)); - - ASSERT(tag == tag_int(7)); - ASSERT(tag_float(2.5) != tag_float(-2.5)); - ASSERT(tag_float(2.5) != tag_double(2.5)); - - ASSERT(tag_double() == 0.0); - - ASSERT(tag_byte(INT8_MAX).get() == INT8_MAX); - ASSERT(tag_byte(INT8_MIN).get() == INT8_MIN); - ASSERT(tag_short(INT16_MAX).get() == INT16_MAX); - ASSERT(tag_short(INT16_MIN).get() == INT16_MIN); - ASSERT(tag_int(INT32_MAX).get() == INT32_MAX); - ASSERT(tag_int(INT32_MIN).get() == INT32_MIN); - ASSERT(tag_long(INT64_MAX).get() == INT64_MAX); - ASSERT(tag_long(INT64_MIN).get() == INT64_MIN); - std::clog << "test_tag_primitive passed" << std::endl; -} - -void test_tag_string() -{ - tag_string tag("foo"); - ASSERT(tag.get() == "foo"); - std::string& ref = tag; - ref = "bar"; - ASSERT(tag.get() == "bar"); - ASSERT(tag.get() != "foo"); - tag.set("baz"); - ASSERT(ref == "baz"); - tag = "quux"; - ASSERT("quux" == static_cast(tag)); - std::string str("foo"); - tag = str; - ASSERT(tag.get() == str); - - ASSERT(tag_string(str).get() == "foo"); - ASSERT(tag_string().get() == ""); - std::clog << "test_tag_string passed" << std::endl; -} - -void test_tag_compound() -{ - tag_compound comp{ - {"foo", int16_t(12)}, - {"bar", "baz"}, - {"baz", -2.0}, - {"list", tag_list{16, 17}} - }; - - //Test assignments and conversions, and exceptions on bad conversions - ASSERT(comp["foo"].get_type() == tag_type::Short); - ASSERT(static_cast(comp["foo"]) == 12); - ASSERT(static_cast(comp.at("foo")) == int16_t(12)); - ASSERT(comp["foo"] == tag_short(12)); - EXPECT_EXCEPTION(static_cast(comp["foo"]), std::bad_cast); - EXPECT_EXCEPTION(static_cast(comp["foo"]), std::bad_cast); - - EXPECT_EXCEPTION(comp["foo"] = 32, std::bad_cast); - comp["foo"] = int8_t(32); - ASSERT(static_cast(comp["foo"]) == 32); - - ASSERT(comp["bar"].get_type() == tag_type::String); - ASSERT(static_cast(comp["bar"]) == "baz"); - EXPECT_EXCEPTION(static_cast(comp["bar"]), std::bad_cast); - - EXPECT_EXCEPTION(comp["bar"] = -128, std::bad_cast); - comp["bar"] = "barbaz"; - ASSERT(static_cast(comp["bar"]) == "barbaz"); - - ASSERT(comp["baz"].get_type() == tag_type::Double); - ASSERT(static_cast(comp["baz"]) == -2.0); - EXPECT_EXCEPTION(static_cast(comp["baz"]), std::bad_cast); - - //Test nested access - comp["quux"] = tag_compound{{"Hello", "World"}, {"zero", 0}}; - ASSERT(comp.at("quux").get_type() == tag_type::Compound); - ASSERT(std::string(comp["quux"].at("Hello")) == "World"); - ASSERT(std::string(comp["quux"]["Hello"]) == "World"); - ASSERT(comp["list"][1] == tag_int(17)); - - EXPECT_EXCEPTION(comp.at("nothing"), std::out_of_range); - - //Test equality comparisons - tag_compound comp2{ - {"foo", int16_t(32)}, - {"bar", "barbaz"}, - {"baz", -2.0}, - {"quux", tag_compound{{"Hello", "World"}, {"zero", 0}}}, - {"list", tag_list{16, 17}} - }; - ASSERT(comp == comp2); - ASSERT(comp != dynamic_cast(comp2["quux"].get())); - ASSERT(comp != comp2["quux"]); - ASSERT(dynamic_cast(comp["quux"].get()) == comp2["quux"]); - - //Test whether begin() through end() goes through all the keys and their - //values. The order of iteration is irrelevant there. - std::set keys{"bar", "baz", "foo", "list", "quux"}; - ASSERT(comp2.size() == keys.size()); - unsigned i = 0; - for(const std::pair& val: comp2) - { - ASSERT(i < comp2.size()); - ASSERT(keys.count(val.first)); - ASSERT(val.second == comp2[val.first]); - ++i; - } - ASSERT(i == comp2.size()); - - //Test erasing and has_key - ASSERT(comp.erase("nothing") == false); - ASSERT(comp.has_key("quux")); - ASSERT(comp.has_key("quux", tag_type::Compound)); - ASSERT(!comp.has_key("quux", tag_type::List)); - ASSERT(!comp.has_key("quux", tag_type::Null)); - - ASSERT(comp.erase("quux") == true); - ASSERT(!comp.has_key("quux")); - ASSERT(!comp.has_key("quux", tag_type::Compound)); - ASSERT(!comp.has_key("quux", tag_type::Null)); - - comp.clear(); - ASSERT(comp == tag_compound{}); - - //Test inserting values - ASSERT(comp.put("abc", tag_double(6.0)).second == true); - ASSERT(comp.put("abc", tag_long(-28)).second == false); - ASSERT(comp.insert("ghi", tag_string("world")).second == true); - ASSERT(comp.insert("abc", tag_string("hello")).second == false); - ASSERT(comp.emplace("def", "ghi").second == true); - ASSERT(comp.emplace("def", 4).second == false); - ASSERT((comp == tag_compound{ - {"abc", tag_long(-28)}, - {"def", tag_byte(4)}, - {"ghi", tag_string("world")} - })); - - std::clog << "test_tag_compound passed" << std::endl; -} - -void test_value() -{ - value val1; - value val2(make_unique(42)); - value val3(tag_int(42)); - - ASSERT(!val1 && val2 && val3); - ASSERT(val1 == val1); - ASSERT(val1 != val2); - ASSERT(val2 == val3); - ASSERT(val3 == val3); - - value valstr(tag_string("foo")); - ASSERT(static_cast(valstr) == "foo"); - valstr = "bar"; - EXPECT_EXCEPTION(valstr = 5, std::bad_cast); - ASSERT(static_cast(valstr) == "bar"); - ASSERT(valstr.as() == "bar"); - ASSERT(&valstr.as() == &valstr.get()); - EXPECT_EXCEPTION(valstr.as(), std::bad_cast); - - val1 = int64_t(42); - ASSERT(val2 != val1); - - EXPECT_EXCEPTION(val2 = int64_t(12), std::bad_cast); - ASSERT(static_cast(val2) == 42); - tag_int* ptr = dynamic_cast(val2.get_ptr().get()); - ASSERT(*ptr == 42); - val2 = 52; - ASSERT(static_cast(val2) == 52); - ASSERT(*ptr == 52); - - EXPECT_EXCEPTION(val1["foo"], std::bad_cast); - EXPECT_EXCEPTION(val1.at("foo"), std::bad_cast); - - val3 = 52; - ASSERT(val2 == val3); - ASSERT(val2.get_ptr() != val3.get_ptr()); - - val3 = std::move(val2); - ASSERT(val3 == tag_int(52)); - ASSERT(!val2); - - tag_int& tag = dynamic_cast(val3.get()); - ASSERT(tag == tag_int(52)); - tag = 21; - ASSERT(static_cast(val3) == 21); - val1.set_ptr(std::move(val3.get_ptr())); - ASSERT(val1.as() == 21); - - ASSERT(val1.get_type() == tag_type::Int); - ASSERT(val2.get_type() == tag_type::Null); - ASSERT(val3.get_type() == tag_type::Null); - - val2 = val1; - val1 = val3; - ASSERT(!val1 && val2 && !val3); - ASSERT(val1.get_ptr() == nullptr); - ASSERT(val2.get() == tag_int(21)); - ASSERT(value(val1) == val1); - ASSERT(value(val2) == val2); - val1 = val1; - val2 = val2; - ASSERT(!val1); - ASSERT(val1 == value_initializer(nullptr)); - ASSERT(val2 == tag_int(21)); - - val3 = tag_short(2); - EXPECT_EXCEPTION(val3 = tag_string("foo"), std::bad_cast); - ASSERT(val3.get() == tag_short(2)); - - val2.set_ptr(make_unique("foo")); - ASSERT(val2 == tag_string("foo")); - std::clog << "test_value passed" << std::endl; -} - -void test_tag_list() -{ - tag_list list; - ASSERT(list.el_type() == tag_type::Null); - EXPECT_EXCEPTION(list.push_back(value(nullptr)), std::bad_cast); - - list.emplace_back("foo"); - ASSERT(list.el_type() == tag_type::String); - list.push_back(tag_string("bar")); - EXPECT_EXCEPTION(list.push_back(tag_int(42)), std::bad_cast); - EXPECT_EXCEPTION(list.emplace_back(), std::bad_cast); - - ASSERT((list == tag_list{"foo", "bar"})); - ASSERT(list[0] == tag_string("foo")); - ASSERT(static_cast(list.at(1)) == "bar"); - - ASSERT(list.size() == 2); - EXPECT_EXCEPTION(list.at(2), std::out_of_range); - EXPECT_EXCEPTION(list.at(-1), std::out_of_range); - - list.set(1, value(tag_string("baz"))); - EXPECT_EXCEPTION(list.set(1, value(nullptr)), std::bad_cast); - EXPECT_EXCEPTION(list.set(1, value(tag_int(-42))), std::bad_cast); - ASSERT(static_cast(list[1]) == "baz"); - - ASSERT(list.size() == 2); - tag_string values[] = {"foo", "baz"}; - ASSERT(list.end() - list.begin() == int(list.size())); - ASSERT(std::equal(list.begin(), list.end(), values)); - - list.pop_back(); - ASSERT(list == tag_list{"foo"}); - ASSERT(list == tag_list::of({"foo"})); - ASSERT(tag_list::of({"foo"}) == tag_list{"foo"}); - ASSERT((list != tag_list{2, 3, 5, 7})); - - list.clear(); - ASSERT(list.size() == 0 && list.el_type() == tag_type::String); - EXPECT_EXCEPTION(list.push_back(tag_short(25)), std::bad_cast); - EXPECT_EXCEPTION(list.push_back(value(nullptr)), std::bad_cast); - - list.reset(); - ASSERT(list.el_type() == tag_type::Null); - list.emplace_back(17); - ASSERT(list.el_type() == tag_type::Int); - - list.reset(tag_type::Float); - ASSERT(list.el_type() == tag_type::Float); - list.emplace_back(17.0f); - ASSERT(list == tag_list({17.0f})); - - ASSERT(tag_list() != tag_list(tag_type::Int)); - ASSERT(tag_list() == tag_list()); - ASSERT(tag_list(tag_type::Short) != tag_list(tag_type::Int)); - ASSERT(tag_list(tag_type::Short) == tag_list(tag_type::Short)); - - tag_list short_list = tag_list::of({25, 36}); - ASSERT(short_list.el_type() == tag_type::Short); - ASSERT((short_list == tag_list{int16_t(25), int16_t(36)})); - ASSERT((short_list != tag_list{25, 36})); - ASSERT((short_list == tag_list{value(tag_short(25)), value(tag_short(36))})); - - EXPECT_EXCEPTION((tag_list{value(tag_byte(4)), value(tag_int(5))}), std::bad_cast); - EXPECT_EXCEPTION((tag_list{value(nullptr), value(tag_int(6))}), std::bad_cast); - EXPECT_EXCEPTION((tag_list{value(tag_int(7)), value(tag_int(8)), value(nullptr)}), std::bad_cast); - ASSERT((tag_list(std::initializer_list{})).el_type() == tag_type::Null); - ASSERT((tag_list{2, 3, 5, 7}).el_type() == tag_type::Int); - std::clog << "test_tag_list passed" << std::endl; -} - -void test_tag_byte_array() -{ - std::vector vec{1, 2, 127, -128}; - tag_byte_array arr{1, 2, 127, -128}; - ASSERT(arr.size() == 4); - ASSERT(arr.at(0) == 1 && arr[1] == 2 && arr[2] == 127 && arr.at(3) == -128); - EXPECT_EXCEPTION(arr.at(-1), std::out_of_range); - EXPECT_EXCEPTION(arr.at(4), std::out_of_range); - - ASSERT(arr.get() == vec); - ASSERT(arr == tag_byte_array(std::vector(vec))); - - arr.push_back(42); - vec.push_back(42); - - ASSERT(arr.size() == 5); - ASSERT(arr.end() - arr.begin() == int(arr.size())); - ASSERT(std::equal(arr.begin(), arr.end(), vec.begin())); - - arr.pop_back(); - arr.pop_back(); - ASSERT(arr.size() == 3); - ASSERT((arr == tag_byte_array{1, 2, 127})); - ASSERT((arr != tag_int_array{1, 2, 127})); - ASSERT((arr != tag_byte_array{1, 2, -1})); - - arr.clear(); - ASSERT(arr == tag_byte_array()); - std::clog << "test_tag_byte_array passed" << std::endl; -} - -void test_tag_int_array() -{ - std::vector vec{100, 200, INT32_MAX, INT32_MIN}; - tag_int_array arr{100, 200, INT32_MAX, INT32_MIN}; - ASSERT(arr.size() == 4); - ASSERT(arr.at(0) == 100 && arr[1] == 200 && arr[2] == INT32_MAX && arr.at(3) == INT32_MIN); - EXPECT_EXCEPTION(arr.at(-1), std::out_of_range); - EXPECT_EXCEPTION(arr.at(4), std::out_of_range); - - ASSERT(arr.get() == vec); - ASSERT(arr == tag_int_array(std::vector(vec))); - - arr.push_back(42); - vec.push_back(42); - - ASSERT(arr.size() == 5); - ASSERT(arr.end() - arr.begin() == int(arr.size())); - ASSERT(std::equal(arr.begin(), arr.end(), vec.begin())); - - arr.pop_back(); - arr.pop_back(); - ASSERT(arr.size() == 3); - ASSERT((arr == tag_int_array{100, 200, INT32_MAX})); - ASSERT((arr != tag_int_array{100, -56, -1})); - - arr.clear(); - ASSERT(arr == tag_int_array()); - std::clog << "test_tag_int_array passed" << std::endl; -} - -void test_visitor() -{ - struct : public nbt_visitor - { - tag_type visited = tag_type::Null; - - void visit(tag_byte& tag) { visited = tag_type::Byte; } - void visit(tag_short& tag) { visited = tag_type::Short; } - void visit(tag_int& tag) { visited = tag_type::Int; } - void visit(tag_long& tag) { visited = tag_type::Long; } - void visit(tag_float& tag) { visited = tag_type::Float; } - void visit(tag_double& tag) { visited = tag_type::Double; } - void visit(tag_byte_array& tag) { visited = tag_type::Byte_Array; } - void visit(tag_string& tag) { visited = tag_type::String; } - void visit(tag_list& tag) { visited = tag_type::List; } - void visit(tag_compound& tag) { visited = tag_type::Compound; } - void visit(tag_int_array& tag) { visited = tag_type::Int_Array; } - } v; - - tag_byte().accept(v); - ASSERT(v.visited == tag_type::Byte); - tag_short().accept(v); - ASSERT(v.visited == tag_type::Short); - tag_int().accept(v); - ASSERT(v.visited == tag_type::Int); - tag_long().accept(v); - ASSERT(v.visited == tag_type::Long); - tag_float().accept(v); - ASSERT(v.visited == tag_type::Float); - tag_double().accept(v); - ASSERT(v.visited == tag_type::Double); - tag_byte_array().accept(v); - ASSERT(v.visited == tag_type::Byte_Array); - tag_string().accept(v); - ASSERT(v.visited == tag_type::String); - tag_list().accept(v); - ASSERT(v.visited == tag_type::List); - tag_compound().accept(v); - ASSERT(v.visited == tag_type::Compound); - tag_int_array().accept(v); - ASSERT(v.visited == tag_type::Int_Array); - std::clog << "test_visitor passed" << std::endl; -} - -int main() -{ - test_tag(); - test_get_type(); - test_tag_primitive(); - test_tag_string(); - test_tag_compound(); - test_value(); - test_tag_list(); - test_tag_byte_array(); - test_tag_int_array(); - test_visitor(); -} diff --git a/test/nbttest.h b/test/nbttest.h new file mode 100644 index 0000000000..8255d4f6dc --- /dev/null +++ b/test/nbttest.h @@ -0,0 +1,476 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include +#include "nbt_tags.h" +#include "nbt_visitor.h" +#include +#include +#include + +using namespace nbt; + +class nbttest : public CxxTest::TestSuite +{ +public: + void test_tag() + { + TS_ASSERT(!is_valid_type(-1)); + TS_ASSERT(!is_valid_type(0)); + TS_ASSERT(is_valid_type(0, true)); + TS_ASSERT(is_valid_type(1)); + TS_ASSERT(is_valid_type(5, false)); + TS_ASSERT(is_valid_type(7, true)); + TS_ASSERT(is_valid_type(11)); + TS_ASSERT(!is_valid_type(12)); + + //looks like TS_ASSERT_EQUALS can't handle abstract classes... + TS_ASSERT(*tag::create(tag_type::Byte) == tag_byte()); + TS_ASSERT_THROWS(tag::create(tag_type::Null), std::invalid_argument); + TS_ASSERT_THROWS(tag::create(tag_type::End), std::invalid_argument); + + tag_string tstr("foo"); + auto cl = tstr.clone(); + TS_ASSERT_EQUALS(tstr.get(), "foo"); + TS_ASSERT(tstr == *cl); + + cl = std::move(tstr).clone(); + TS_ASSERT(*cl == tag_string("foo")); + TS_ASSERT(*cl != tag_string("bar")); + + cl = std::move(*cl).move_clone(); + TS_ASSERT(*cl == tag_string("foo")); + + tstr.assign(tag_string("bar")); + TS_ASSERT_THROWS(tstr.assign(tag_int(6)), std::bad_cast); + TS_ASSERT_EQUALS(tstr.get(), "bar"); + + TS_ASSERT_EQUALS(&tstr.as(), &tstr); + TS_ASSERT_THROWS(tstr.as(), std::bad_cast); + } + + void test_get_type() + { + TS_ASSERT_EQUALS(tag_byte().get_type() , tag_type::Byte); + TS_ASSERT_EQUALS(tag_short().get_type() , tag_type::Short); + TS_ASSERT_EQUALS(tag_int().get_type() , tag_type::Int); + TS_ASSERT_EQUALS(tag_long().get_type() , tag_type::Long); + TS_ASSERT_EQUALS(tag_float().get_type() , tag_type::Float); + TS_ASSERT_EQUALS(tag_double().get_type() , tag_type::Double); + TS_ASSERT_EQUALS(tag_byte_array().get_type(), tag_type::Byte_Array); + TS_ASSERT_EQUALS(tag_string().get_type() , tag_type::String); + TS_ASSERT_EQUALS(tag_list().get_type() , tag_type::List); + TS_ASSERT_EQUALS(tag_compound().get_type() , tag_type::Compound); + TS_ASSERT_EQUALS(tag_int_array().get_type() , tag_type::Int_Array); + } + + void test_tag_primitive() + { + tag_int tag(6); + TS_ASSERT_EQUALS(tag.get(), 6); + int& ref = tag; + ref = 12; + TS_ASSERT(tag == 12); + TS_ASSERT(tag != 6); + tag.set(24); + TS_ASSERT_EQUALS(ref, 24); + tag = 7; + TS_ASSERT_EQUALS(static_cast(tag), 7); + + TS_ASSERT_EQUALS(tag, tag_int(7)); + TS_ASSERT_DIFFERS(tag_float(2.5), tag_float(-2.5)); + TS_ASSERT_DIFFERS(tag_float(2.5), tag_double(2.5)); + + TS_ASSERT(tag_double() == 0.0); + + TS_ASSERT_EQUALS(tag_byte(INT8_MAX).get(), INT8_MAX); + TS_ASSERT_EQUALS(tag_byte(INT8_MIN).get(), INT8_MIN); + TS_ASSERT_EQUALS(tag_short(INT16_MAX).get(), INT16_MAX); + TS_ASSERT_EQUALS(tag_short(INT16_MIN).get(), INT16_MIN); + TS_ASSERT_EQUALS(tag_int(INT32_MAX).get(), INT32_MAX); + TS_ASSERT_EQUALS(tag_int(INT32_MIN).get(), INT32_MIN); + TS_ASSERT_EQUALS(tag_long(INT64_MAX).get(), INT64_MAX); + TS_ASSERT_EQUALS(tag_long(INT64_MIN).get(), INT64_MIN); + } + + void test_tag_string() + { + tag_string tag("foo"); + TS_ASSERT_EQUALS(tag.get(), "foo"); + std::string& ref = tag; + ref = "bar"; + TS_ASSERT_EQUALS(tag.get(), "bar"); + TS_ASSERT_DIFFERS(tag.get(), "foo"); + tag.set("baz"); + TS_ASSERT_EQUALS(ref, "baz"); + tag = "quux"; + TS_ASSERT_EQUALS("quux", static_cast(tag)); + std::string str("foo"); + tag = str; + TS_ASSERT_EQUALS(tag.get(),str); + + TS_ASSERT_EQUALS(tag_string(str).get(), "foo"); + TS_ASSERT_EQUALS(tag_string().get(), ""); + } + + void test_tag_compound() + { + tag_compound comp{ + {"foo", int16_t(12)}, + {"bar", "baz"}, + {"baz", -2.0}, + {"list", tag_list{16, 17}} + }; + + //Test assignments and conversions, and exceptions on bad conversions + TS_ASSERT_EQUALS(comp["foo"].get_type(), tag_type::Short); + TS_ASSERT_EQUALS(static_cast(comp["foo"]), 12); + TS_ASSERT_EQUALS(static_cast(comp.at("foo")), int16_t(12)); + TS_ASSERT(comp["foo"] == tag_short(12)); + TS_ASSERT_THROWS(static_cast(comp["foo"]), std::bad_cast); + TS_ASSERT_THROWS(static_cast(comp["foo"]), std::bad_cast); + + TS_ASSERT_THROWS(comp["foo"] = 32, std::bad_cast); + comp["foo"] = int8_t(32); + TS_ASSERT_EQUALS(static_cast(comp["foo"]), 32); + + TS_ASSERT_EQUALS(comp["bar"].get_type(), tag_type::String); + TS_ASSERT_EQUALS(static_cast(comp["bar"]), "baz"); + TS_ASSERT_THROWS(static_cast(comp["bar"]), std::bad_cast); + + TS_ASSERT_THROWS(comp["bar"] = -128, std::bad_cast); + comp["bar"] = "barbaz"; + TS_ASSERT_EQUALS(static_cast(comp["bar"]), "barbaz"); + + TS_ASSERT_EQUALS(comp["baz"].get_type(), tag_type::Double); + TS_ASSERT_EQUALS(static_cast(comp["baz"]), -2.0); + TS_ASSERT_THROWS(static_cast(comp["baz"]), std::bad_cast); + + //Test nested access + comp["quux"] = tag_compound{{"Hello", "World"}, {"zero", 0}}; + TS_ASSERT_EQUALS(comp.at("quux").get_type(), tag_type::Compound); + TS_ASSERT_EQUALS(static_cast(comp["quux"].at("Hello")), "World"); + TS_ASSERT_EQUALS(static_cast(comp["quux"]["Hello"]), "World"); + TS_ASSERT(comp["list"][1] == tag_int(17)); + + TS_ASSERT_THROWS(comp.at("nothing"), std::out_of_range); + + //Test equality comparisons + tag_compound comp2{ + {"foo", int16_t(32)}, + {"bar", "barbaz"}, + {"baz", -2.0}, + {"quux", tag_compound{{"Hello", "World"}, {"zero", 0}}}, + {"list", tag_list{16, 17}} + }; + TS_ASSERT(comp == comp2); + TS_ASSERT(comp != dynamic_cast(comp2["quux"].get())); + TS_ASSERT(comp != comp2["quux"]); + TS_ASSERT(dynamic_cast(comp["quux"].get()) == comp2["quux"]); + + //Test whether begin() through end() goes through all the keys and their + //values. The order of iteration is irrelevant there. + std::set keys{"bar", "baz", "foo", "list", "quux"}; + TS_ASSERT_EQUALS(comp2.size(), keys.size()); + unsigned int i = 0; + for(const std::pair& val: comp2) + { + TS_ASSERT_LESS_THAN(i, comp2.size()); + TS_ASSERT(keys.count(val.first)); + TS_ASSERT(val.second == comp2[val.first]); + ++i; + } + TS_ASSERT_EQUALS(i, comp2.size()); + + //Test erasing and has_key + TS_ASSERT_EQUALS(comp.erase("nothing"), false); + TS_ASSERT(comp.has_key("quux")); + TS_ASSERT(comp.has_key("quux", tag_type::Compound)); + TS_ASSERT(!comp.has_key("quux", tag_type::List)); + TS_ASSERT(!comp.has_key("quux", tag_type::Null)); + + TS_ASSERT_EQUALS(comp.erase("quux"), true); + TS_ASSERT(!comp.has_key("quux")); + TS_ASSERT(!comp.has_key("quux", tag_type::Compound)); + TS_ASSERT(!comp.has_key("quux", tag_type::Null)); + + comp.clear(); + TS_ASSERT(comp == tag_compound{}); + + //Test inserting values + TS_ASSERT_EQUALS(comp.put("abc", tag_double(6.0)).second, true); + TS_ASSERT_EQUALS(comp.put("abc", tag_long(-28)).second, false); + TS_ASSERT_EQUALS(comp.insert("ghi", tag_string("world")).second, true); + TS_ASSERT_EQUALS(comp.insert("abc", tag_string("hello")).second, false); + TS_ASSERT_EQUALS(comp.emplace("def", "ghi").second, true); + TS_ASSERT_EQUALS(comp.emplace("def", 4).second, false); + TS_ASSERT((comp == tag_compound{ + {"abc", tag_long(-28)}, + {"def", tag_byte(4)}, + {"ghi", tag_string("world")} + })); + } + + void test_value() + { + value val1; + value val2(make_unique(42)); + value val3(tag_int(42)); + + TS_ASSERT(!val1 && val2 && val3); + TS_ASSERT(val1 == val1); + TS_ASSERT(val1 != val2); + TS_ASSERT(val2 == val3); + TS_ASSERT(val3 == val3); + + value valstr(tag_string("foo")); + TS_ASSERT_EQUALS(static_cast(valstr), "foo"); + valstr = "bar"; + TS_ASSERT_THROWS(valstr = 5, std::bad_cast); + TS_ASSERT_EQUALS(static_cast(valstr), "bar"); + TS_ASSERT(valstr.as() == "bar"); + TS_ASSERT_EQUALS(&valstr.as(), &valstr.get()); + TS_ASSERT_THROWS(valstr.as(), std::bad_cast); + + val1 = int64_t(42); + TS_ASSERT(val2 != val1); + + TS_ASSERT_THROWS(val2 = int64_t(12), std::bad_cast); + TS_ASSERT_EQUALS(static_cast(val2), 42); + tag_int* ptr = dynamic_cast(val2.get_ptr().get()); + TS_ASSERT(*ptr == 42); + val2 = 52; + TS_ASSERT_EQUALS(static_cast(val2), 52); + TS_ASSERT(*ptr == 52); + + TS_ASSERT_THROWS(val1["foo"], std::bad_cast); + TS_ASSERT_THROWS(val1.at("foo"), std::bad_cast); + + val3 = 52; + TS_ASSERT(val2 == val3); + TS_ASSERT(val2.get_ptr() != val3.get_ptr()); + + val3 = std::move(val2); + TS_ASSERT(val3 == tag_int(52)); + TS_ASSERT(!val2); + + tag_int& tag = dynamic_cast(val3.get()); + TS_ASSERT(tag == tag_int(52)); + tag = 21; + TS_ASSERT_EQUALS(static_cast(val3), 21); + val1.set_ptr(std::move(val3.get_ptr())); + TS_ASSERT(val1.as() == 21); + + TS_ASSERT_EQUALS(val1.get_type(), tag_type::Int); + TS_ASSERT_EQUALS(val2.get_type(), tag_type::Null); + TS_ASSERT_EQUALS(val3.get_type(), tag_type::Null); + + val2 = val1; + val1 = val3; + TS_ASSERT(!val1 && val2 && !val3); + TS_ASSERT(val1.get_ptr() == nullptr); + TS_ASSERT(val2.get() == tag_int(21)); + TS_ASSERT(value(val1) == val1); + TS_ASSERT(value(val2) == val2); + val1 = val1; + val2 = val2; + TS_ASSERT(!val1); + TS_ASSERT(val1 == value_initializer(nullptr)); + TS_ASSERT(val2 == tag_int(21)); + + val3 = tag_short(2); + TS_ASSERT_THROWS(val3 = tag_string("foo"), std::bad_cast); + TS_ASSERT(val3.get() == tag_short(2)); + + val2.set_ptr(make_unique("foo")); + TS_ASSERT(val2 == tag_string("foo")); + } + + void test_tag_list() + { + tag_list list; + TS_ASSERT_EQUALS(list.el_type(), tag_type::Null); + TS_ASSERT_THROWS(list.push_back(value(nullptr)), std::bad_cast); + + list.emplace_back("foo"); + TS_ASSERT_EQUALS(list.el_type(), tag_type::String); + list.push_back(tag_string("bar")); + TS_ASSERT_THROWS(list.push_back(tag_int(42)), std::bad_cast); + TS_ASSERT_THROWS(list.emplace_back(), std::bad_cast); + + TS_ASSERT((list == tag_list{"foo", "bar"})); + TS_ASSERT(list[0] == tag_string("foo")); + TS_ASSERT_EQUALS(static_cast(list.at(1)), "bar"); + + TS_ASSERT_EQUALS(list.size(), 2); + TS_ASSERT_THROWS(list.at(2), std::out_of_range); + TS_ASSERT_THROWS(list.at(-1), std::out_of_range); + + list.set(1, value(tag_string("baz"))); + TS_ASSERT_THROWS(list.set(1, value(nullptr)), std::bad_cast); + TS_ASSERT_THROWS(list.set(1, value(tag_int(-42))), std::bad_cast); + TS_ASSERT_EQUALS(static_cast(list[1]), "baz"); + + TS_ASSERT_EQUALS(list.size(), 2); + tag_string values[] = {"foo", "baz"}; + TS_ASSERT_EQUALS(list.end() - list.begin(), int(list.size())); + TS_ASSERT(std::equal(list.begin(), list.end(), values)); + + list.pop_back(); + TS_ASSERT(list == tag_list{"foo"}); + TS_ASSERT(list == tag_list::of({"foo"})); + TS_ASSERT(tag_list::of({"foo"}) == tag_list{"foo"}); + TS_ASSERT((list != tag_list{2, 3, 5, 7})); + + list.clear(); + TS_ASSERT_EQUALS(list.size(), 0); + TS_ASSERT_EQUALS(list.el_type(), tag_type::String) + TS_ASSERT_THROWS(list.push_back(tag_short(25)), std::bad_cast); + TS_ASSERT_THROWS(list.push_back(value(nullptr)), std::bad_cast); + + list.reset(); + TS_ASSERT_EQUALS(list.el_type(), tag_type::Null); + list.emplace_back(17); + TS_ASSERT_EQUALS(list.el_type(), tag_type::Int); + + list.reset(tag_type::Float); + TS_ASSERT_EQUALS(list.el_type(), tag_type::Float); + list.emplace_back(17.0f); + TS_ASSERT(list == tag_list({17.0f})); + + TS_ASSERT(tag_list() != tag_list(tag_type::Int)); + TS_ASSERT(tag_list() == tag_list()); + TS_ASSERT(tag_list(tag_type::Short) != tag_list(tag_type::Int)); + TS_ASSERT(tag_list(tag_type::Short) == tag_list(tag_type::Short)); + + tag_list short_list = tag_list::of({25, 36}); + TS_ASSERT_EQUALS(short_list.el_type(), tag_type::Short); + TS_ASSERT((short_list == tag_list{int16_t(25), int16_t(36)})); + TS_ASSERT((short_list != tag_list{25, 36})); + TS_ASSERT((short_list == tag_list{value(tag_short(25)), value(tag_short(36))})); + + TS_ASSERT_THROWS((tag_list{value(tag_byte(4)), value(tag_int(5))}), std::bad_cast); + TS_ASSERT_THROWS((tag_list{value(nullptr), value(tag_int(6))}), std::bad_cast); + TS_ASSERT_THROWS((tag_list{value(tag_int(7)), value(tag_int(8)), value(nullptr)}), std::bad_cast); + TS_ASSERT_EQUALS((tag_list(std::initializer_list{})).el_type(), tag_type::Null); + TS_ASSERT_EQUALS((tag_list{2, 3, 5, 7}).el_type(), tag_type::Int); + } + + void test_tag_byte_array() + { + std::vector vec{1, 2, 127, -128}; + tag_byte_array arr{1, 2, 127, -128}; + TS_ASSERT_EQUALS(arr.size(), 4); + TS_ASSERT(arr.at(0) == 1 && arr[1] == 2 && arr[2] == 127 && arr.at(3) == -128); + TS_ASSERT_THROWS(arr.at(-1), std::out_of_range); + TS_ASSERT_THROWS(arr.at(4), std::out_of_range); + + TS_ASSERT(arr.get() == vec); + TS_ASSERT(arr == tag_byte_array(std::vector(vec))); + + arr.push_back(42); + vec.push_back(42); + + TS_ASSERT_EQUALS(arr.size(), 5); + TS_ASSERT_EQUALS(arr.end() - arr.begin(), int(arr.size())); + TS_ASSERT(std::equal(arr.begin(), arr.end(), vec.begin())); + + arr.pop_back(); + arr.pop_back(); + TS_ASSERT_EQUALS(arr.size(), 3); + TS_ASSERT((arr == tag_byte_array{1, 2, 127})); + TS_ASSERT((arr != tag_int_array{1, 2, 127})); + TS_ASSERT((arr != tag_byte_array{1, 2, -1})); + + arr.clear(); + TS_ASSERT(arr == tag_byte_array()); + } + + void test_tag_int_array() + { + std::vector vec{100, 200, INT32_MAX, INT32_MIN}; + tag_int_array arr{100, 200, INT32_MAX, INT32_MIN}; + TS_ASSERT_EQUALS(arr.size(), 4); + TS_ASSERT(arr.at(0) == 100 && arr[1] == 200 && arr[2] == INT32_MAX && arr.at(3) == INT32_MIN); + TS_ASSERT_THROWS(arr.at(-1), std::out_of_range); + TS_ASSERT_THROWS(arr.at(4), std::out_of_range); + + TS_ASSERT(arr.get() == vec); + TS_ASSERT(arr == tag_int_array(std::vector(vec))); + + arr.push_back(42); + vec.push_back(42); + + TS_ASSERT_EQUALS(arr.size(), 5); + TS_ASSERT_EQUALS(arr.end() - arr.begin(), int(arr.size())); + TS_ASSERT(std::equal(arr.begin(), arr.end(), vec.begin())); + + arr.pop_back(); + arr.pop_back(); + TS_ASSERT_EQUALS(arr.size(), 3); + TS_ASSERT((arr == tag_int_array{100, 200, INT32_MAX})); + TS_ASSERT((arr != tag_int_array{100, -56, -1})); + + arr.clear(); + TS_ASSERT(arr == tag_int_array()); + } + + void test_visitor() + { + struct : public nbt_visitor + { + tag_type visited = tag_type::Null; + + void visit(tag_byte& tag) { visited = tag_type::Byte; } + void visit(tag_short& tag) { visited = tag_type::Short; } + void visit(tag_int& tag) { visited = tag_type::Int; } + void visit(tag_long& tag) { visited = tag_type::Long; } + void visit(tag_float& tag) { visited = tag_type::Float; } + void visit(tag_double& tag) { visited = tag_type::Double; } + void visit(tag_byte_array& tag) { visited = tag_type::Byte_Array; } + void visit(tag_string& tag) { visited = tag_type::String; } + void visit(tag_list& tag) { visited = tag_type::List; } + void visit(tag_compound& tag) { visited = tag_type::Compound; } + void visit(tag_int_array& tag) { visited = tag_type::Int_Array; } + } v; + + tag_byte().accept(v); + TS_ASSERT_EQUALS(v.visited, tag_type::Byte); + tag_short().accept(v); + TS_ASSERT_EQUALS(v.visited, tag_type::Short); + tag_int().accept(v); + TS_ASSERT_EQUALS(v.visited, tag_type::Int); + tag_long().accept(v); + TS_ASSERT_EQUALS(v.visited, tag_type::Long); + tag_float().accept(v); + TS_ASSERT_EQUALS(v.visited, tag_type::Float); + tag_double().accept(v); + TS_ASSERT_EQUALS(v.visited, tag_type::Double); + tag_byte_array().accept(v); + TS_ASSERT_EQUALS(v.visited, tag_type::Byte_Array); + tag_string().accept(v); + TS_ASSERT_EQUALS(v.visited, tag_type::String); + tag_list().accept(v); + TS_ASSERT_EQUALS(v.visited, tag_type::List); + tag_compound().accept(v); + TS_ASSERT_EQUALS(v.visited, tag_type::Compound); + tag_int_array().accept(v); + TS_ASSERT_EQUALS(v.visited, tag_type::Int_Array); + } +}; -- cgit 0.0.5-2-1-g0f52 From b7b3abf11dcb94249eae0b51332c0604af134946 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 20 Aug 2015 21:41:16 +0200 Subject: Make endian_str_test use CxxTest --- test/CMakeLists.txt | 3 +- test/endian_str_test.cpp | 181 ----------------------------------------------- test/endian_str_test.h | 175 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+), 183 deletions(-) delete mode 100644 test/endian_str_test.cpp create mode 100644 test/endian_str_test.h diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8b26efcab9..4e74c0ab93 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -5,9 +5,8 @@ include_directories(${CXXTEST_INCLUDE_DIR}) CXXTEST_ADD_TEST(nbttest nbttest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/nbttest.h) target_link_libraries(nbttest nbt++) -add_executable(endian_str_test endian_str_test.cpp) +CXXTEST_ADD_TEST(endian_str_test endian_str_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/endian_str_test.h) target_link_libraries(endian_str_test nbt++) -add_test(endian_str_test endian_str_test) add_executable(read_test read_test.cpp) target_link_libraries(read_test nbt++) diff --git a/test/endian_str_test.cpp b/test/endian_str_test.cpp deleted file mode 100644 index c8db4ea674..0000000000 --- a/test/endian_str_test.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* - * libnbt++ - A library for the Minecraft Named Binary Tag format. - * Copyright (C) 2013, 2015 ljfa-ag - * - * This file is part of libnbt++. - * - * libnbt++ is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * libnbt++ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with libnbt++. If not, see . - */ -#include "microtest.h" -#include "endian_str.h" -#include -#include -#include - -using namespace endian; - -void test_uint() -{ - std::stringstream str(std::ios::in | std::ios::out | std::ios::binary); - - write_little(str, uint8_t (0x01)); - write_little(str, uint16_t(0x0102)); - write (str, uint32_t(0x01020304), endian::little); - write_little(str, uint64_t(0x0102030405060708)); - - write_big (str, uint8_t (0x09)); - write_big (str, uint16_t(0x090A)); - write_big (str, uint32_t(0x090A0B0C)); - write (str, uint64_t(0x090A0B0C0D0E0F10), endian::big); - - std::string expected{ - 1, - 2, 1, - 4, 3, 2, 1, - 8, 7, 6, 5, 4, 3, 2, 1, - - 9, - 9, 10, - 9, 10, 11, 12, - 9, 10, 11, 12, 13, 14, 15, 16 - }; - ASSERT(str.str() == expected); - - uint8_t u8; - uint16_t u16; - uint32_t u32; - uint64_t u64; - - read_little(str, u8); - ASSERT(u8 == 0x01); - read_little(str, u16); - ASSERT(u16 == 0x0102); - read_little(str, u32); - ASSERT(u32 == 0x01020304); - read(str, u64, endian::little); - ASSERT(u64 == 0x0102030405060708); - - read_big(str, u8); - ASSERT(u8 == 0x09); - read_big(str, u16); - ASSERT(u16 == 0x090A); - read(str, u32, endian::big); - ASSERT(u32 == 0x090A0B0C); - read_big(str, u64); - ASSERT(u64 == 0x090A0B0C0D0E0F10); - - ASSERT(str); //Check if stream has failed - std::clog << "test_uint passed" << std::endl; -} - -void test_sint() -{ - std::stringstream str(std::ios::in | std::ios::out | std::ios::binary); - - write_little(str, int8_t (-0x01)); - write_little(str, int16_t(-0x0102)); - write_little(str, int32_t(-0x01020304)); - write (str, int64_t(-0x0102030405060708), endian::little); - - write_big (str, int8_t (-0x09)); - write_big (str, int16_t(-0x090A)); - write (str, int32_t(-0x090A0B0C), endian::big); - write_big (str, int64_t(-0x090A0B0C0D0E0F10)); - - std::string expected{ //meh, stupid narrowing conversions - '\xFF', - '\xFE', '\xFE', - '\xFC', '\xFC', '\xFD', '\xFE', - '\xF8', '\xF8', '\xF9', '\xFA', '\xFB', '\xFC', '\xFD', '\xFE', - - '\xF7', - '\xF6', '\xF6', - '\xF6', '\xF5', '\xF4', '\xF4', - '\xF6', '\xF5', '\xF4', '\xF3', '\xF2', '\xF1', '\xF0', '\xF0' - }; - ASSERT(str.str() == expected); - - int8_t i8; - int16_t i16; - int32_t i32; - int64_t i64; - - read_little(str, i8); - ASSERT(i8 == -0x01); - read_little(str, i16); - ASSERT(i16 == -0x0102); - read(str, i32, endian::little); - ASSERT(i32 == -0x01020304); - read_little(str, i64); - ASSERT(i64 == -0x0102030405060708); - - read_big(str, i8); - ASSERT(i8 == -0x09); - read_big(str, i16); - ASSERT(i16 == -0x090A); - read_big(str, i32); - ASSERT(i32 == -0x090A0B0C); - read(str, i64, endian::big); - ASSERT(i64 == -0x090A0B0C0D0E0F10); - - ASSERT(str); //Check if stream has failed - std::clog << "test_sint passed" << std::endl; -} - -void test_float() -{ - std::stringstream str(std::ios::in | std::ios::out | std::ios::binary); - - //C99 has hexadecimal floating point literals, C++ doesn't... - const float fconst = std::stof("-0xCDEF01p-63"); //-1.46325e-012 - const double dconst = std::stod("-0x1DEF0102030405p-375"); //-1.09484e-097 - //We will be assuming IEEE 754 here - - write_little(str, fconst); - write_little(str, dconst); - write_big (str, fconst); - write_big (str, dconst); - - std::string expected{ - '\x01', '\xEF', '\xCD', '\xAB', - '\x05', '\x04', '\x03', '\x02', '\x01', '\xEF', '\xCD', '\xAB', - - '\xAB', '\xCD', '\xEF', '\x01', - '\xAB', '\xCD', '\xEF', '\x01', '\x02', '\x03', '\x04', '\x05' - }; - ASSERT(str.str() == expected); - - float f; - double d; - - read_little(str, f); - ASSERT(f == fconst); - read_little(str, d); - ASSERT(d == dconst); - - read_big(str, f); - ASSERT(f == fconst); - read_big(str, d); - ASSERT(d == dconst); - - ASSERT(str); //Check if stream has failed - std::clog << "test_float passed" << std::endl; -} - -int main() -{ - test_uint(); - test_sint(); - test_float(); -} diff --git a/test/endian_str_test.h b/test/endian_str_test.h new file mode 100644 index 0000000000..e0b960ee54 --- /dev/null +++ b/test/endian_str_test.h @@ -0,0 +1,175 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include +#include "endian_str.h" +#include +#include +#include + +using namespace endian; + +class endian_str_test : public CxxTest::TestSuite +{ +public: + void test_uint() + { + std::stringstream str(std::ios::in | std::ios::out | std::ios::binary); + + write_little(str, uint8_t (0x01)); + write_little(str, uint16_t(0x0102)); + write (str, uint32_t(0x01020304), endian::little); + write_little(str, uint64_t(0x0102030405060708)); + + write_big (str, uint8_t (0x09)); + write_big (str, uint16_t(0x090A)); + write_big (str, uint32_t(0x090A0B0C)); + write (str, uint64_t(0x090A0B0C0D0E0F10), endian::big); + + std::string expected{ + 1, + 2, 1, + 4, 3, 2, 1, + 8, 7, 6, 5, 4, 3, 2, 1, + + 9, + 9, 10, + 9, 10, 11, 12, + 9, 10, 11, 12, 13, 14, 15, 16 + }; + TS_ASSERT_EQUALS(str.str(), expected); + + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + + read_little(str, u8); + TS_ASSERT_EQUALS(u8, 0x01); + read_little(str, u16); + TS_ASSERT_EQUALS(u16, 0x0102); + read_little(str, u32); + TS_ASSERT_EQUALS(u32, 0x01020304); + read(str, u64, endian::little); + TS_ASSERT_EQUALS(u64, 0x0102030405060708); + + read_big(str, u8); + TS_ASSERT_EQUALS(u8, 0x09); + read_big(str, u16); + TS_ASSERT_EQUALS(u16, 0x090A); + read(str, u32, endian::big); + TS_ASSERT_EQUALS(u32, 0x090A0B0C); + read_big(str, u64); + TS_ASSERT_EQUALS(u64, 0x090A0B0C0D0E0F10); + + TS_ASSERT(str); //Check if stream has failed + } + + void test_sint() + { + std::stringstream str(std::ios::in | std::ios::out | std::ios::binary); + + write_little(str, int8_t (-0x01)); + write_little(str, int16_t(-0x0102)); + write_little(str, int32_t(-0x01020304)); + write (str, int64_t(-0x0102030405060708), endian::little); + + write_big (str, int8_t (-0x09)); + write_big (str, int16_t(-0x090A)); + write (str, int32_t(-0x090A0B0C), endian::big); + write_big (str, int64_t(-0x090A0B0C0D0E0F10)); + + std::string expected{ //meh, stupid narrowing conversions + '\xFF', + '\xFE', '\xFE', + '\xFC', '\xFC', '\xFD', '\xFE', + '\xF8', '\xF8', '\xF9', '\xFA', '\xFB', '\xFC', '\xFD', '\xFE', + + '\xF7', + '\xF6', '\xF6', + '\xF6', '\xF5', '\xF4', '\xF4', + '\xF6', '\xF5', '\xF4', '\xF3', '\xF2', '\xF1', '\xF0', '\xF0' + }; + TS_ASSERT_EQUALS(str.str(), expected); + + int8_t i8; + int16_t i16; + int32_t i32; + int64_t i64; + + read_little(str, i8); + TS_ASSERT_EQUALS(i8, -0x01); + read_little(str, i16); + TS_ASSERT_EQUALS(i16, -0x0102); + read(str, i32, endian::little); + TS_ASSERT_EQUALS(i32, -0x01020304); + read_little(str, i64); + TS_ASSERT_EQUALS(i64, -0x0102030405060708); + + read_big(str, i8); + TS_ASSERT_EQUALS(i8, -0x09); + read_big(str, i16); + TS_ASSERT_EQUALS(i16, -0x090A); + read_big(str, i32); + TS_ASSERT_EQUALS(i32, -0x090A0B0C); + read(str, i64, endian::big); + TS_ASSERT_EQUALS(i64, -0x090A0B0C0D0E0F10); + + TS_ASSERT(str); //Check if stream has failed + } + + void test_float() + { + std::stringstream str(std::ios::in | std::ios::out | std::ios::binary); + + //C99 has hexadecimal floating point literals, C++ doesn't... + const float fconst = std::stof("-0xCDEF01p-63"); //-1.46325e-012 + const double dconst = std::stod("-0x1DEF0102030405p-375"); //-1.09484e-097 + //We will be assuming IEEE 754 here + + write_little(str, fconst); + write_little(str, dconst); + write_big (str, fconst); + write_big (str, dconst); + + std::string expected{ + '\x01', '\xEF', '\xCD', '\xAB', + '\x05', '\x04', '\x03', '\x02', '\x01', '\xEF', '\xCD', '\xAB', + + '\xAB', '\xCD', '\xEF', '\x01', + '\xAB', '\xCD', '\xEF', '\x01', '\x02', '\x03', '\x04', '\x05' + }; + TS_ASSERT_EQUALS(str.str(), expected); + + float f; + double d; + + read_little(str, f); + TS_ASSERT_EQUALS(f, fconst); + read_little(str, d); + TS_ASSERT_EQUALS(d, dconst); + + read_big(str, f); + TS_ASSERT_EQUALS(f, fconst); + read_big(str, d); + TS_ASSERT_EQUALS(d, dconst); + + TS_ASSERT(str); //Check if stream has failed + } +}; -- cgit 0.0.5-2-1-g0f52 From d92059d50c1b1fedc39dd60a580af8c61c9dea0d Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 20 Aug 2015 22:21:38 +0200 Subject: Make read_test use CxxTest --- test/CMakeLists.txt | 3 +- test/read_test.cpp | 228 ---------------------------------------------------- test/read_test.h | 216 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 217 insertions(+), 230 deletions(-) delete mode 100644 test/read_test.cpp create mode 100644 test/read_test.h diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4e74c0ab93..784a8ed815 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -8,12 +8,11 @@ target_link_libraries(nbttest nbt++) CXXTEST_ADD_TEST(endian_str_test endian_str_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/endian_str_test.h) target_link_libraries(endian_str_test nbt++) -add_executable(read_test read_test.cpp) +CXXTEST_ADD_TEST(read_test read_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/read_test.h) target_link_libraries(read_test nbt++) add_custom_command(TARGET read_test POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/testfiles ${CMAKE_CURRENT_BINARY_DIR}) -add_test(read_test read_test) add_executable(write_test write_test.cpp) target_link_libraries(write_test nbt++) diff --git a/test/read_test.cpp b/test/read_test.cpp deleted file mode 100644 index 90ee392451..0000000000 --- a/test/read_test.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/* - * libnbt++ - A library for the Minecraft Named Binary Tag format. - * Copyright (C) 2013, 2015 ljfa-ag - * - * This file is part of libnbt++. - * - * libnbt++ is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * libnbt++ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with libnbt++. If not, see . - */ -#include "microtest.h" -#include "io/stream_reader.h" -#include "nbt_tags.h" -#include -#include -#include - -using namespace nbt; - -void test_stream_reader_big() -{ - std::string input{ - 1, //tag_type::Byte - 0, //tag_type::End - 11, //tag_type::Int_Array - - 0x0a, 0x0b, 0x0c, 0x0d, //0x0a0b0c0d in Big Endian - - 0x00, 0x06, //String length in Big Endian - 'f', 'o', 'o', 'b', 'a', 'r', - - 0 //tag_type::End (invalid with allow_end = false) - }; - std::istringstream is(input); - nbt::io::stream_reader reader(is); - - ASSERT(&reader.get_istr() == &is); - ASSERT(reader.get_endian() == endian::big); - - ASSERT(reader.read_type() == tag_type::Byte); - ASSERT(reader.read_type(true) == tag_type::End); - ASSERT(reader.read_type(false) == tag_type::Int_Array); - - int32_t i; - reader.read_num(i); - ASSERT(i == 0x0a0b0c0d); - - ASSERT(reader.read_string() == "foobar"); - - EXPECT_EXCEPTION(reader.read_type(false), io::input_error); - ASSERT(!is); - is.clear(); - - //Test for invalid tag type 12 - is.str("\x0c"); - EXPECT_EXCEPTION(reader.read_type(), io::input_error); - ASSERT(!is); - is.clear(); - - //Test for unexpcted EOF on numbers (input too short for int32_t) - is.str("\x03\x04"); - reader.read_num(i); - ASSERT(!is); - std::clog << "test_stream_reader_big passed" << std::endl; -} - -void test_stream_reader_little() -{ - std::string input{ - 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, //0x0d0c0b0a09080706 in Little Endian - - 0x06, 0x00, //String length in Little Endian - 'f', 'o', 'o', 'b', 'a', 'r', - - 0x10, 0x00, //String length (intentionally too large) - 'a', 'b', 'c', 'd' //unexpected EOF - }; - std::istringstream is(input); - nbt::io::stream_reader reader(is, endian::little); - - ASSERT(reader.get_endian() == endian::little); - - int64_t i; - reader.read_num(i); - ASSERT(i == 0x0d0c0b0a09080706); - - ASSERT(reader.read_string() == "foobar"); - - EXPECT_EXCEPTION(reader.read_string(), io::input_error); - ASSERT(!is); - std::clog << "test_stream_reader_little passed" << std::endl; -} - -//Tests if comp equals an extended variant of Notch's bigtest NBT -void verify_bigtest_structure(const tag_compound& comp) -{ - ASSERT(comp.size() == 13); - - ASSERT(comp.at("byteTest") == tag_byte(127)); - ASSERT(comp.at("shortTest") == tag_short(32767)); - ASSERT(comp.at("intTest") == tag_int(2147483647)); - ASSERT(comp.at("longTest") == tag_long(9223372036854775807)); - ASSERT(comp.at("floatTest") == tag_float(std::stof("0xff1832p-25"))); //0.4982315 - ASSERT(comp.at("doubleTest") == tag_double(std::stod("0x1f8f6bbbff6a5ep-54"))); //0.493128713218231 - - //From bigtest.nbt: "the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...)" - tag_byte_array byteArrayTest; - for(int n = 0; n < 1000; ++n) - byteArrayTest.push_back((n*n*255 + n*7) % 100); - ASSERT(comp.at("byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...))") == byteArrayTest); - - ASSERT(comp.at("stringTest") == tag_string("HELLO WORLD THIS IS A TEST STRING \u00C5\u00C4\u00D6!")); - - ASSERT(comp.at("listTest (compound)") == tag_list::of({ - {{"created-on", tag_long(1264099775885)}, {"name", "Compound tag #0"}}, - {{"created-on", tag_long(1264099775885)}, {"name", "Compound tag #1"}} - })); - ASSERT(comp.at("listTest (long)") == tag_list::of({11, 12, 13, 14, 15})); - ASSERT(comp.at("listTest (end)") == tag_list()); - - ASSERT((comp.at("nested compound test") == tag_compound{ - {"egg", tag_compound{{"value", 0.5f}, {"name", "Eggbert"}}}, - {"ham", tag_compound{{"value", 0.75f}, {"name", "Hampus"}}} - })); - - ASSERT(comp.at("intArrayTest") == tag_int_array( - {0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f})); -} - -void test_read_bigtest() -{ - //Uses an extended variant of Notch's original bigtest file - std::ifstream file("bigtest_uncompr", std::ios::binary); - ASSERT(file); - - auto pair = nbt::io::read_compound(file); - ASSERT(pair.first == "Level"); - verify_bigtest_structure(*pair.second); - std::clog << "test_read_bigtest passed" << std::endl; -} - -void test_read_littletest() -{ - //Same as bigtest, but little endian - std::ifstream file("littletest_uncompr", std::ios::binary); - ASSERT(file); - - auto pair = nbt::io::read_compound(file, endian::little); - ASSERT(pair.first == "Level"); - ASSERT(pair.second->get_type() == tag_type::Compound); - verify_bigtest_structure(*pair.second); - std::clog << "test_read_littletest passed" << std::endl; -} - -void test_read_errors() -{ - std::ifstream file; - nbt::io::stream_reader reader(file); - - //EOF within a tag_double payload - file.open("errortest_eof1", std::ios::binary); - ASSERT(file); - EXPECT_EXCEPTION(reader.read_tag(), io::input_error); - ASSERT(!file); - - //EOF within a key in a compound - file.close(); - file.open("errortest_eof2", std::ios::binary); - ASSERT(file); - EXPECT_EXCEPTION(reader.read_tag(), io::input_error); - ASSERT(!file); - - //Missing tag_end - file.close(); - file.open("errortest_noend", std::ios::binary); - ASSERT(file); - EXPECT_EXCEPTION(reader.read_tag(), io::input_error); - ASSERT(!file); - - //Negative list length - file.close(); - file.open("errortest_neg_length", std::ios::binary); - ASSERT(file); - EXPECT_EXCEPTION(reader.read_tag(), io::input_error); - ASSERT(!file); - std::clog << "test_read_errors passed" << std::endl; -} - -void test_read_misc() -{ - std::ifstream file; - nbt::io::stream_reader reader(file); - - //Toplevel tag other than compound - file.open("toplevel_string", std::ios::binary); - ASSERT(file); - EXPECT_EXCEPTION(reader.read_compound(), io::input_error); - ASSERT(!file); - - //Rewind and try again with read_tag - file.clear(); - ASSERT(file.seekg(0)); - auto pair = reader.read_tag(); - ASSERT(pair.first == "Test (toplevel tag_string)"); - ASSERT(*pair.second == tag_string( - "Even though unprovided for by NBT, the library should also handle " - "the case where the file consists of something else than tag_compound")); - std::clog << "test_read_misc passed" << std::endl; -} - -int main() -{ - test_stream_reader_big(); - test_stream_reader_little(); - test_read_bigtest(); - test_read_littletest(); - test_read_errors(); - test_read_misc(); -} diff --git a/test/read_test.h b/test/read_test.h new file mode 100644 index 0000000000..bb5618b2b4 --- /dev/null +++ b/test/read_test.h @@ -0,0 +1,216 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include +#include "io/stream_reader.h" +#include "nbt_tags.h" +#include +#include +#include + +using namespace nbt; + +class read_test : public CxxTest::TestSuite +{ +public: + void test_stream_reader_big() + { + std::string input{ + 1, //tag_type::Byte + 0, //tag_type::End + 11, //tag_type::Int_Array + + 0x0a, 0x0b, 0x0c, 0x0d, //0x0a0b0c0d in Big Endian + + 0x00, 0x06, //String length in Big Endian + 'f', 'o', 'o', 'b', 'a', 'r', + + 0 //tag_type::End (invalid with allow_end = false) + }; + std::istringstream is(input); + nbt::io::stream_reader reader(is); + + TS_ASSERT_EQUALS(&reader.get_istr(), &is); + TS_ASSERT_EQUALS(reader.get_endian(), endian::big); + + TS_ASSERT_EQUALS(reader.read_type(), tag_type::Byte); + TS_ASSERT_EQUALS(reader.read_type(true), tag_type::End); + TS_ASSERT_EQUALS(reader.read_type(false), tag_type::Int_Array); + + int32_t i; + reader.read_num(i); + TS_ASSERT_EQUALS(i, 0x0a0b0c0d); + + TS_ASSERT_EQUALS(reader.read_string(), "foobar"); + + TS_ASSERT_THROWS(reader.read_type(false), io::input_error); + TS_ASSERT(!is); + is.clear(); + + //Test for invalid tag type 12 + is.str("\x0c"); + TS_ASSERT_THROWS(reader.read_type(), io::input_error); + TS_ASSERT(!is); + is.clear(); + + //Test for unexpcted EOF on numbers (input too short for int32_t) + is.str("\x03\x04"); + reader.read_num(i); + TS_ASSERT(!is); + } + + void test_stream_reader_little() + { + std::string input{ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, //0x0d0c0b0a09080706 in Little Endian + + 0x06, 0x00, //String length in Little Endian + 'f', 'o', 'o', 'b', 'a', 'r', + + 0x10, 0x00, //String length (intentionally too large) + 'a', 'b', 'c', 'd' //unexpected EOF + }; + std::istringstream is(input); + nbt::io::stream_reader reader(is, endian::little); + + TS_ASSERT_EQUALS(reader.get_endian(), endian::little); + + int64_t i; + reader.read_num(i); + TS_ASSERT_EQUALS(i, 0x0d0c0b0a09080706); + + TS_ASSERT_EQUALS(reader.read_string(), "foobar"); + + TS_ASSERT_THROWS(reader.read_string(), io::input_error); + TS_ASSERT(!is); + } + + //Tests if comp equals an extended variant of Notch's bigtest NBT + void verify_bigtest_structure(const tag_compound& comp) + { + TS_ASSERT_EQUALS(comp.size(), 13); + + TS_ASSERT(comp.at("byteTest") == tag_byte(127)); + TS_ASSERT(comp.at("shortTest") == tag_short(32767)); + TS_ASSERT(comp.at("intTest") == tag_int(2147483647)); + TS_ASSERT(comp.at("longTest") == tag_long(9223372036854775807)); + TS_ASSERT(comp.at("floatTest") == tag_float(std::stof("0xff1832p-25"))); //0.4982315 + TS_ASSERT(comp.at("doubleTest") == tag_double(std::stod("0x1f8f6bbbff6a5ep-54"))); //0.493128713218231 + + //From bigtest.nbt: "the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...)" + tag_byte_array byteArrayTest; + for(int n = 0; n < 1000; ++n) + byteArrayTest.push_back((n*n*255 + n*7) % 100); + TS_ASSERT(comp.at("byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...))") == byteArrayTest); + + TS_ASSERT(comp.at("stringTest") == tag_string("HELLO WORLD THIS IS A TEST STRING \u00C5\u00C4\u00D6!")); + + TS_ASSERT(comp.at("listTest (compound)") == tag_list::of({ + {{"created-on", tag_long(1264099775885)}, {"name", "Compound tag #0"}}, + {{"created-on", tag_long(1264099775885)}, {"name", "Compound tag #1"}} + })); + TS_ASSERT(comp.at("listTest (long)") == tag_list::of({11, 12, 13, 14, 15})); + TS_ASSERT(comp.at("listTest (end)") == tag_list()); + + TS_ASSERT((comp.at("nested compound test") == tag_compound{ + {"egg", tag_compound{{"value", 0.5f}, {"name", "Eggbert"}}}, + {"ham", tag_compound{{"value", 0.75f}, {"name", "Hampus"}}} + })); + + TS_ASSERT(comp.at("intArrayTest") == tag_int_array( + {0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f})); + } + + void test_read_bigtest() + { + //Uses an extended variant of Notch's original bigtest file + std::ifstream file("bigtest_uncompr", std::ios::binary); + TS_ASSERT(file); + + auto pair = nbt::io::read_compound(file); + TS_ASSERT_EQUALS(pair.first, "Level"); + verify_bigtest_structure(*pair.second); + } + + void test_read_littletest() + { + //Same as bigtest, but little endian + std::ifstream file("littletest_uncompr", std::ios::binary); + TS_ASSERT(file); + + auto pair = nbt::io::read_compound(file, endian::little); + TS_ASSERT_EQUALS(pair.first, "Level"); + TS_ASSERT_EQUALS(pair.second->get_type(), tag_type::Compound); + verify_bigtest_structure(*pair.second); + } + + void test_read_errors() + { + std::ifstream file; + nbt::io::stream_reader reader(file); + + //EOF within a tag_double payload + file.open("errortest_eof1", std::ios::binary); + TS_ASSERT(file); + TS_ASSERT_THROWS(reader.read_tag(), io::input_error); + TS_ASSERT(!file); + + //EOF within a key in a compound + file.close(); + file.open("errortest_eof2", std::ios::binary); + TS_ASSERT(file); + TS_ASSERT_THROWS(reader.read_tag(), io::input_error); + TS_ASSERT(!file); + + //Missing tag_end + file.close(); + file.open("errortest_noend", std::ios::binary); + TS_ASSERT(file); + TS_ASSERT_THROWS(reader.read_tag(), io::input_error); + TS_ASSERT(!file); + + //Negative list length + file.close(); + file.open("errortest_neg_length", std::ios::binary); + TS_ASSERT(file); + TS_ASSERT_THROWS(reader.read_tag(), io::input_error); + TS_ASSERT(!file); + } + + void test_read_misc() + { + std::ifstream file; + nbt::io::stream_reader reader(file); + + //Toplevel tag other than compound + file.open("toplevel_string", std::ios::binary); + TS_ASSERT(file); + TS_ASSERT_THROWS(reader.read_compound(), io::input_error); + TS_ASSERT(!file); + + //Rewind and try again with read_tag + file.clear(); + TS_ASSERT(file.seekg(0)); + auto pair = reader.read_tag(); + TS_ASSERT_EQUALS(pair.first, "Test (toplevel tag_string)"); + TS_ASSERT(*pair.second == tag_string( + "Even though unprovided for by NBT, the library should also handle " + "the case where the file consists of something else than tag_compound")); + } +}; -- cgit 0.0.5-2-1-g0f52 From 71176e1f0700d8567d436a9321ca02ad2a553b26 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 20 Aug 2015 22:50:38 +0200 Subject: Make write_test use CxxTest --- test/CMakeLists.txt | 3 +- test/write_test.cpp | 256 ---------------------------------------------------- test/write_test.h | 248 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 249 insertions(+), 258 deletions(-) delete mode 100644 test/write_test.cpp create mode 100644 test/write_test.h diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 784a8ed815..c13f09f2da 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -14,9 +14,8 @@ add_custom_command(TARGET read_test POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/testfiles ${CMAKE_CURRENT_BINARY_DIR}) -add_executable(write_test write_test.cpp) +CXXTEST_ADD_TEST(write_test write_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/write_test.h) target_link_libraries(write_test nbt++) -add_test(write_test write_test) add_executable(format_test format_test.cpp) target_link_libraries(format_test nbt++) diff --git a/test/write_test.cpp b/test/write_test.cpp deleted file mode 100644 index 9445e4ab70..0000000000 --- a/test/write_test.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/* - * libnbt++ - A library for the Minecraft Named Binary Tag format. - * Copyright (C) 2013, 2015 ljfa-ag - * - * This file is part of libnbt++. - * - * libnbt++ is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * libnbt++ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with libnbt++. If not, see . - */ -#include "microtest.h" -#include "io/stream_writer.h" -#include "io/stream_reader.h" -#include "nbt_tags.h" -#include -#include -#include - -using namespace nbt; - -void test_stream_writer_big() -{ - std::ostringstream os; - nbt::io::stream_writer writer(os); - - ASSERT(&writer.get_ostr() == &os); - ASSERT(writer.get_endian() == endian::big); - - writer.write_type(tag_type::End); - writer.write_type(tag_type::Long); - writer.write_type(tag_type::Int_Array); - - writer.write_num(int64_t(0x0102030405060708)); - - writer.write_string("foobar"); - - ASSERT(os); - std::string expected{ - 0, //tag_type::End - 4, //tag_type::Long - 11, //tag_type::Int_Array - - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, //0x0102030405060708 in Big Endian - - 0x00, 0x06, //string length in Big Endian - 'f', 'o', 'o', 'b', 'a', 'r' - }; - ASSERT(os.str() == expected); - - //too long for NBT - EXPECT_EXCEPTION(writer.write_string(std::string(65536, '.')), std::length_error); - ASSERT(!os); - std::clog << "test_stream_writer_big passed" << std::endl; -} - -void test_stream_writer_little() -{ - std::ostringstream os; - nbt::io::stream_writer writer(os, endian::little); - - ASSERT(writer.get_endian() == endian::little); - - writer.write_num(int32_t(0x0a0b0c0d)); - - writer.write_string("foobar"); - - ASSERT(os); - std::string expected{ - 0x0d, 0x0c, 0x0b, 0x0a, //0x0a0b0c0d in Little Endian - - 0x06, 0x00, //string length in Little Endian - 'f', 'o', 'o', 'b', 'a', 'r' - }; - ASSERT(os.str() == expected); - - EXPECT_EXCEPTION(writer.write_string(std::string(65536, '.')), std::length_error); - ASSERT(!os); - std::clog << "test_stream_writer_little passed" << std::endl; -} - -void test_write_payload_big() -{ - std::ostringstream os; - nbt::io::stream_writer writer(os); - - //tag_primitive - writer.write_payload(tag_byte(127)); - writer.write_payload(tag_short(32767)); - writer.write_payload(tag_int(2147483647)); - writer.write_payload(tag_long(9223372036854775807)); - - //Same values as in endian_str_test - writer.write_payload(tag_float(std::stof("-0xCDEF01p-63"))); - writer.write_payload(tag_double(std::stod("-0x1DEF0102030405p-375"))); - - ASSERT((os.str() == std::string{ - '\x7F', - '\x7F', '\xFF', - '\x7F', '\xFF', '\xFF', '\xFF', - '\x7F', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', - - '\xAB', '\xCD', '\xEF', '\x01', - '\xAB', '\xCD', '\xEF', '\x01', '\x02', '\x03', '\x04', '\x05' - })); - os.str(""); //clear and reuse the stream - - //tag_string - writer.write_payload(tag_string("barbaz")); - ASSERT((os.str() == std::string{ - 0x00, 0x06, //string length in Big Endian - 'b', 'a', 'r', 'b', 'a', 'z' - })); - EXPECT_EXCEPTION(writer.write_payload(tag_string(std::string(65536, '.'))), std::length_error); - ASSERT(!os); - os.clear(); - - //tag_byte_array - os.str(""); - writer.write_payload(tag_byte_array{0, 1, 127, -128, -127}); - ASSERT((os.str() == std::string{ - 0x00, 0x00, 0x00, 0x05, //length in Big Endian - 0, 1, 127, -128, -127 - })); - os.str(""); - - //tag_int_array - writer.write_payload(tag_int_array{0x01020304, 0x05060708, 0x090a0b0c}); - ASSERT((os.str() == std::string{ - 0x00, 0x00, 0x00, 0x03, //length in Big Endian - 0x01, 0x02, 0x03, 0x04, - 0x05, 0x06, 0x07, 0x08, - 0x09, 0x0a, 0x0b, 0x0c - })); - os.str(""); - - //tag_list - writer.write_payload(tag_list()); //empty list with undetermined type, should be written as list of tag_end - writer.write_payload(tag_list(tag_type::Int)); //empty list of tag_int - writer.write_payload(tag_list{ //nested list - tag_list::of({0x3456, 0x789a}), - tag_list::of({0x0a, 0x0b, 0x0c, 0x0d}) - }); - ASSERT((os.str() == std::string{ - 0, //tag_type::End - 0x00, 0x00, 0x00, 0x00, //length - - 3, //tag_type::Int - 0x00, 0x00, 0x00, 0x00, //length - - 9, //tag_type::List - 0x00, 0x00, 0x00, 0x02, //length - //list 0 - 2, //tag_type::Short - 0x00, 0x00, 0x00, 0x02, //length - '\x34', '\x56', - '\x78', '\x9a', - //list 1 - 1, //tag_type::Byte - 0x00, 0x00, 0x00, 0x04, //length - 0x0a, - 0x0b, - 0x0c, - 0x0d - })); - os.str(""); - - //tag_compound - /* Testing if writing compounds works properly is problematic because the - order of the tags is not guaranteed. However with only two tags in a - compound we only have two possible orderings. - See below for a more thorough test that uses writing and re-reading. */ - writer.write_payload(tag_compound{}); - writer.write_payload(tag_compound{ - {"foo", "quux"}, - {"bar", tag_int(0x789abcde)} - }); - - std::string endtag{0x00}; - std::string subtag1{ - 8, //tag_type::String - 0x00, 0x03, //key length - 'f', 'o', 'o', - 0x00, 0x04, //string length - 'q', 'u', 'u', 'x' - }; - std::string subtag2{ - 3, //tag_type::Int - 0x00, 0x03, //key length - 'b', 'a', 'r', - '\x78', '\x9A', '\xBC', '\xDE' - }; - ASSERT(os.str() == endtag + subtag1 + subtag2 + endtag - || os.str() == endtag + subtag2 + subtag1 + endtag); - - //Now for write_tag: - os.str(""); - writer.write_tag("foo", tag_string("quux")); - ASSERT(os.str() == subtag1); - ASSERT(os); - - //too long key for NBT - EXPECT_EXCEPTION(writer.write_tag(std::string(65536, '.'), tag_long(-1)), std::length_error); - ASSERT(!os); - std::clog << "test_write_payload_big passed" << std::endl; -} - -void test_write_bigtest() -{ - /* Like already stated above, because no order is guaranteed for - tag_compound, we cannot simply test it by writing into a stream and directly - comparing the output to a reference value. - Instead, we assume that reading already works correctly and re-read the - written tag. - Smaller-grained tests are already done above. */ - std::ifstream file("bigtest_uncompr", std::ios::binary); - const auto orig_pair = io::read_compound(file); - std::stringstream sstr; - - //Write into stream in Big Endian - io::write_tag(orig_pair.first, *orig_pair.second, sstr); - ASSERT(sstr); - - //Read from stream in Big Endian and compare - auto written_pair = io::read_compound(sstr); - ASSERT(orig_pair.first == written_pair.first); - ASSERT(*orig_pair.second == *written_pair.second); - - sstr.str(""); //Reset and reuse stream - //Write into stream in Little Endian - io::write_tag(orig_pair.first, *orig_pair.second, sstr, endian::little); - ASSERT(sstr); - - //Read from stream in Little Endian and compare - written_pair = io::read_compound(sstr, endian::little); - ASSERT(orig_pair.first == written_pair.first); - ASSERT(*orig_pair.second == *written_pair.second); - - std::clog << "test_write_bigtest passed" << std::endl; -} - -int main() -{ - test_stream_writer_big(); - test_stream_writer_little(); - test_write_payload_big(); - test_write_bigtest(); -} diff --git a/test/write_test.h b/test/write_test.h new file mode 100644 index 0000000000..424e6344a2 --- /dev/null +++ b/test/write_test.h @@ -0,0 +1,248 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include +#include "io/stream_writer.h" +#include "io/stream_reader.h" +#include "nbt_tags.h" +#include +#include +#include + +using namespace nbt; + +class read_test : public CxxTest::TestSuite +{ +public: + void test_stream_writer_big() + { + std::ostringstream os; + nbt::io::stream_writer writer(os); + + TS_ASSERT_EQUALS(&writer.get_ostr(), &os); + TS_ASSERT_EQUALS(writer.get_endian(), endian::big); + + writer.write_type(tag_type::End); + writer.write_type(tag_type::Long); + writer.write_type(tag_type::Int_Array); + + writer.write_num(int64_t(0x0102030405060708)); + + writer.write_string("foobar"); + + TS_ASSERT(os); + std::string expected{ + 0, //tag_type::End + 4, //tag_type::Long + 11, //tag_type::Int_Array + + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, //0x0102030405060708 in Big Endian + + 0x00, 0x06, //string length in Big Endian + 'f', 'o', 'o', 'b', 'a', 'r' + }; + TS_ASSERT_EQUALS(os.str(), expected); + + //too long for NBT + TS_ASSERT_THROWS(writer.write_string(std::string(65536, '.')), std::length_error); + TS_ASSERT(!os); + } + + void test_stream_writer_little() + { + std::ostringstream os; + nbt::io::stream_writer writer(os, endian::little); + + TS_ASSERT_EQUALS(writer.get_endian(), endian::little); + + writer.write_num(int32_t(0x0a0b0c0d)); + + writer.write_string("foobar"); + + TS_ASSERT(os); + std::string expected{ + 0x0d, 0x0c, 0x0b, 0x0a, //0x0a0b0c0d in Little Endian + + 0x06, 0x00, //string length in Little Endian + 'f', 'o', 'o', 'b', 'a', 'r' + }; + TS_ASSERT_EQUALS(os.str(), expected); + + TS_ASSERT_THROWS(writer.write_string(std::string(65536, '.')), std::length_error); + TS_ASSERT(!os); + } + + void test_write_payload_big() + { + std::ostringstream os; + nbt::io::stream_writer writer(os); + + //tag_primitive + writer.write_payload(tag_byte(127)); + writer.write_payload(tag_short(32767)); + writer.write_payload(tag_int(2147483647)); + writer.write_payload(tag_long(9223372036854775807)); + + //Same values as in endian_str_test + writer.write_payload(tag_float(std::stof("-0xCDEF01p-63"))); + writer.write_payload(tag_double(std::stod("-0x1DEF0102030405p-375"))); + + TS_ASSERT_EQUALS(os.str(), (std::string{ + '\x7F', + '\x7F', '\xFF', + '\x7F', '\xFF', '\xFF', '\xFF', + '\x7F', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', + + '\xAB', '\xCD', '\xEF', '\x01', + '\xAB', '\xCD', '\xEF', '\x01', '\x02', '\x03', '\x04', '\x05' + })); + os.str(""); //clear and reuse the stream + + //tag_string + writer.write_payload(tag_string("barbaz")); + TS_ASSERT_EQUALS(os.str(), (std::string{ + 0x00, 0x06, //string length in Big Endian + 'b', 'a', 'r', 'b', 'a', 'z' + })); + TS_ASSERT_THROWS(writer.write_payload(tag_string(std::string(65536, '.'))), std::length_error); + TS_ASSERT(!os); + os.clear(); + + //tag_byte_array + os.str(""); + writer.write_payload(tag_byte_array{0, 1, 127, -128, -127}); + TS_ASSERT_EQUALS(os.str(), (std::string{ + 0x00, 0x00, 0x00, 0x05, //length in Big Endian + 0, 1, 127, -128, -127 + })); + os.str(""); + + //tag_int_array + writer.write_payload(tag_int_array{0x01020304, 0x05060708, 0x090a0b0c}); + TS_ASSERT_EQUALS(os.str(), (std::string{ + 0x00, 0x00, 0x00, 0x03, //length in Big Endian + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c + })); + os.str(""); + + //tag_list + writer.write_payload(tag_list()); //empty list with undetermined type, should be written as list of tag_end + writer.write_payload(tag_list(tag_type::Int)); //empty list of tag_int + writer.write_payload(tag_list{ //nested list + tag_list::of({0x3456, 0x789a}), + tag_list::of({0x0a, 0x0b, 0x0c, 0x0d}) + }); + TS_ASSERT_EQUALS(os.str(), (std::string{ + 0, //tag_type::End + 0x00, 0x00, 0x00, 0x00, //length + + 3, //tag_type::Int + 0x00, 0x00, 0x00, 0x00, //length + + 9, //tag_type::List + 0x00, 0x00, 0x00, 0x02, //length + //list 0 + 2, //tag_type::Short + 0x00, 0x00, 0x00, 0x02, //length + '\x34', '\x56', + '\x78', '\x9a', + //list 1 + 1, //tag_type::Byte + 0x00, 0x00, 0x00, 0x04, //length + 0x0a, + 0x0b, + 0x0c, + 0x0d + })); + os.str(""); + + //tag_compound + /* Testing if writing compounds works properly is problematic because the + order of the tags is not guaranteed. However with only two tags in a + compound we only have two possible orderings. + See below for a more thorough test that uses writing and re-reading. */ + writer.write_payload(tag_compound{}); + writer.write_payload(tag_compound{ + {"foo", "quux"}, + {"bar", tag_int(0x789abcde)} + }); + + std::string endtag{0x00}; + std::string subtag1{ + 8, //tag_type::String + 0x00, 0x03, //key length + 'f', 'o', 'o', + 0x00, 0x04, //string length + 'q', 'u', 'u', 'x' + }; + std::string subtag2{ + 3, //tag_type::Int + 0x00, 0x03, //key length + 'b', 'a', 'r', + '\x78', '\x9A', '\xBC', '\xDE' + }; + + TS_ASSERT(os.str() == endtag + subtag1 + subtag2 + endtag + || os.str() == endtag + subtag2 + subtag1 + endtag); + + //Now for write_tag: + os.str(""); + writer.write_tag("foo", tag_string("quux")); + TS_ASSERT_EQUALS(os.str(), subtag1); + TS_ASSERT(os); + + //too long key for NBT + TS_ASSERT_THROWS(writer.write_tag(std::string(65536, '.'), tag_long(-1)), std::length_error); + TS_ASSERT(!os); + } + + void test_write_bigtest() + { + /* Like already stated above, because no order is guaranteed for + tag_compound, we cannot simply test it by writing into a stream and directly + comparing the output to a reference value. + Instead, we assume that reading already works correctly and re-read the + written tag. + Smaller-grained tests are already done above. */ + std::ifstream file("bigtest_uncompr", std::ios::binary); + const auto orig_pair = io::read_compound(file); + std::stringstream sstr; + + //Write into stream in Big Endian + io::write_tag(orig_pair.first, *orig_pair.second, sstr); + TS_ASSERT(sstr); + + //Read from stream in Big Endian and compare + auto written_pair = io::read_compound(sstr); + TS_ASSERT_EQUALS(orig_pair.first, written_pair.first); + TS_ASSERT(*orig_pair.second == *written_pair.second); + + sstr.str(""); //Reset and reuse stream + //Write into stream in Little Endian + io::write_tag(orig_pair.first, *orig_pair.second, sstr, endian::little); + TS_ASSERT(sstr); + + //Read from stream in Little Endian and compare + written_pair = io::read_compound(sstr, endian::little); + TS_ASSERT_EQUALS(orig_pair.first, written_pair.first); + TS_ASSERT(*orig_pair.second == *written_pair.second); + } +}; -- cgit 0.0.5-2-1-g0f52 From 936b2390d89f4e20380c060b39a670dc9fae873f Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 23 Aug 2015 10:24:00 +0200 Subject: Inline some methods --- .gitignore | 1 + include/tag_array.h | 26 ++++++++-------- include/tag_compound.h | 10 +++--- include/tag_list.h | 16 +++++----- include/tag_string.h | 18 ++++++----- include/value.h | 18 +++++------ include/value_initializer.h | 8 ++--- src/tag_array.cpp | 74 --------------------------------------------- src/tag_compound.cpp | 20 ------------ src/tag_list.cpp | 38 ----------------------- src/tag_string.cpp | 37 ----------------------- src/value.cpp | 49 ------------------------------ src/value_initializer.cpp | 5 --- 13 files changed, 52 insertions(+), 268 deletions(-) diff --git a/.gitignore b/.gitignore index 05dee84b0f..b1ef936ea3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.layout *.cbtemp *.bak +*.swp /bin /lib /obj diff --git a/include/tag_array.h b/include/tag_array.h index 14dd4d4480..342d3a4ceb 100644 --- a/include/tag_array.h +++ b/include/tag_array.h @@ -62,12 +62,12 @@ public: tag_array() {} ///Constructs an array with the given values - tag_array(std::initializer_list init); - tag_array(std::vector&& vec) noexcept; + tag_array(std::initializer_list init): data(init) {} + tag_array(std::vector&& vec) noexcept: data(std::move(vec)) {} ///Returns a reference to the vector that contains the values - std::vector& get(); - const std::vector& get() const; + std::vector& get() { return data; } + const std::vector& get() const { return data; } /** * @brief Accesses a value by index with bounds checking @@ -81,20 +81,20 @@ public: * * No bounds checking is performed. */ - T& operator[](size_t i); - T operator[](size_t i) const; + 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); + void push_back(T val) { data.push_back(val); } ///Removes the last element from the array - void pop_back(); + void pop_back() { data.pop_back(); } ///Returns the number of values in the array - size_t size() const; + size_t size() const { return data.size(); } ///Erases all values from the array. - void clear(); + void clear() { data.clear(); } //Iterators iterator begin(); @@ -115,8 +115,10 @@ private: std::vector data; }; -template bool operator==(const tag_array& lhs, const tag_array& rhs); -template bool operator!=(const tag_array& lhs, const tag_array& rhs); +template bool operator==(const tag_array& lhs, const tag_array& rhs) +{ return lhs.get() == rhs.get(); } +template bool operator!=(const tag_array& lhs, const tag_array& rhs) +{ return !(lhs == rhs); } //Typedefs that should be used instead of the template tag_array. typedef tag_array tag_byte_array; diff --git a/include/tag_compound.h b/include/tag_compound.h index bcb0a6cec2..2948059ce9 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -106,10 +106,10 @@ public: bool has_key(const std::string& key, tag_type type) const; ///Returns the number of tags in the compound - size_t size() const; + size_t size() const { return tags.size(); } ///Erases all tags from the compound - void clear(); + void clear() { tags.clear(); } //Iterators iterator begin(); @@ -122,8 +122,10 @@ public: 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); - friend bool operator!=(const tag_compound& lhs, const tag_compound& rhs); + 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; diff --git a/include/tag_list.h b/include/tag_list.h index 66f63e6ed9..05150323b9 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -66,10 +66,10 @@ public: * * The content type is determined when the first tag is added. */ - tag_list(); + tag_list(): tag_list(tag_type::Null) {} ///Constructs an empty list with the given content type - explicit tag_list(tag_type type); + explicit tag_list(tag_type type): el_type_(type) {} ///Constructs a list with the given contents tag_list(std::initializer_list init); @@ -106,8 +106,8 @@ public: * Returns a value to the tag at the specified index. No bounds checking * is performed. */ - value& operator[](size_t i); - const value& operator[](size_t i) const; + 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 @@ -133,16 +133,16 @@ public: void emplace_back(Args&&... args); ///Removes the last element of the list - void pop_back(); + void pop_back() { tags.pop_back(); } ///Returns the content type of the list, or tag_type::Null if undetermined - tag_type el_type() const; + tag_type el_type() const { return el_type_; } ///Returns the number of tags in the list - size_t size() const; + size_t size() const { return tags.size(); } ///Erases all tags from the list. Preserves the content type. - void clear(); + void clear() { tags.clear(); } /** * @brief Erases all tags from the list and changes the content type. diff --git a/include/tag_string.h b/include/tag_string.h index ab084c44aa..48e7adf497 100644 --- a/include/tag_string.h +++ b/include/tag_string.h @@ -35,14 +35,14 @@ public: //Constructors tag_string() {} - tag_string(const std::string& str); - tag_string(std::string&& str) noexcept; - tag_string(const char* str); + 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&(); - operator const std::string&() const; - const std::string& get() const; + 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); @@ -62,8 +62,10 @@ private: std::string value; }; -bool operator==(const tag_string& lhs, const tag_string& rhs); -bool operator!=(const tag_string& lhs, const tag_string& 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); } } diff --git a/include/value.h b/include/value.h index 4865c8fd1c..c51a862b4d 100644 --- a/include/value.h +++ b/include/value.h @@ -62,7 +62,7 @@ class value public: //Constructors value() noexcept {} - explicit value(std::unique_ptr&& t) noexcept; + explicit value(std::unique_ptr&& t) noexcept: tag_(std::move(t)) {} explicit value(tag&& t); //Moving @@ -87,10 +87,10 @@ public: * * If the value is uninitialized, the behavior is undefined. */ - operator tag&(); - operator const tag&() const; - tag& get(); - const tag& get() const; + 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 @@ -143,7 +143,7 @@ public: explicit operator const std::string&() const; ///Returns true if the value is not uninitialized - explicit operator bool() const; + explicit operator bool() const { return tag_ != nullptr; } /** * @brief In case of a tag_compound, accesses a tag by key with bounds checking @@ -189,10 +189,10 @@ public: const value& operator[](size_t i) const; ///Returns a reference to the underlying std::unique_ptr - std::unique_ptr& get_ptr(); - const std::unique_ptr& get_ptr() const; + std::unique_ptr& get_ptr() { return tag_; } + const std::unique_ptr& get_ptr() const { return tag_; } ///Resets the underlying std::unique_ptr to a different value - void set_ptr(std::unique_ptr&& t); + void set_ptr(std::unique_ptr&& t) { tag_ = std::move(t); } ///@sa tag::get_type tag_type get_type() const; diff --git a/include/value_initializer.h b/include/value_initializer.h index 843ee16885..4ec0215740 100644 --- a/include/value_initializer.h +++ b/include/value_initializer.h @@ -44,10 +44,10 @@ namespace nbt class value_initializer : public value { public: - value_initializer(std::unique_ptr&& t) noexcept; - value_initializer(std::nullptr_t) noexcept; - value_initializer(value&& val) noexcept; - value_initializer(tag&& t); + value_initializer(std::unique_ptr&& 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); diff --git a/src/tag_array.cpp b/src/tag_array.cpp index 41f00eedc3..6c12b9a2cd 100644 --- a/src/tag_array.cpp +++ b/src/tag_array.cpp @@ -25,28 +25,6 @@ namespace nbt { -template -tag_array::tag_array(std::initializer_list init): - data(init) -{} - -template -tag_array::tag_array(std::vector&& vec) noexcept: - data(std::move(vec)) -{} - -template -std::vector& tag_array::get() -{ - return data; -} - -template -const std::vector& tag_array::get() const -{ - return data; -} - template T& tag_array::at(size_t i) { @@ -59,42 +37,6 @@ T tag_array::at(size_t i) const return data.at(i); } -template -T& tag_array::operator[](size_t i) -{ - return data[i]; -} - -template -T tag_array::operator[](size_t i) const -{ - return data[i]; -} - -template -void tag_array::push_back(T val) -{ - data.push_back(val); -} - -template -void tag_array::pop_back() -{ - data.pop_back(); -} - -template -size_t tag_array::size() const -{ - return data.size(); -} - -template -void tag_array::clear() -{ - data.clear(); -} - template auto tag_array::begin() -> iterator { return data.begin(); } template auto tag_array::end() -> iterator { return data.end(); } template auto tag_array::begin() const -> const_iterator { return data.begin(); } @@ -168,24 +110,8 @@ void tag_array::write_payload(io::stream_writer& writer) const writer.write_num(i); } -template -bool operator==(const tag_array& lhs, const tag_array& rhs) -{ - return lhs.get() == rhs.get(); -} - -template -bool operator!=(const tag_array& lhs, const tag_array& rhs) -{ - return !(lhs == rhs); -} - //Enforce template instantiations template class tag_array; template class tag_array; -template bool operator== (const tag_array& , const tag_array&); -template bool operator==(const tag_array&, const tag_array&); -template bool operator!= (const tag_array& , const tag_array&); -template bool operator!=(const tag_array&, const tag_array&); } diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index d0f20512ae..746a07955e 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -82,16 +82,6 @@ bool tag_compound::has_key(const std::string& key, tag_type type) const return it != tags.end() && it->second.get_type() == type; } -size_t tag_compound::size() const -{ - return tags.size(); -} - -void tag_compound::clear() -{ - tags.clear(); -} - auto tag_compound::begin() -> iterator { return tags.begin(); } auto tag_compound::end() -> iterator { return tags.end(); } auto tag_compound::begin() const -> const_iterator { return tags.begin(); } @@ -128,14 +118,4 @@ void tag_compound::write_payload(io::stream_writer& writer) const writer.write_type(tag_type::End); } -bool operator==(const tag_compound& lhs, const tag_compound& rhs) -{ - return lhs.tags == rhs.tags; -} - -bool operator!=(const tag_compound& lhs, const tag_compound& rhs) -{ - return !(lhs == rhs); -} - } diff --git a/src/tag_list.cpp b/src/tag_list.cpp index d2aa01d314..0e7dda4ae6 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -26,14 +26,6 @@ namespace nbt { -tag_list::tag_list(): - tag_list(tag_type::Null) -{} - -tag_list::tag_list(tag_type type): - el_type_(type) -{} - tag_list::tag_list(std::initializer_list il) { init(il); } tag_list::tag_list(std::initializer_list il) { init(il); } tag_list::tag_list(std::initializer_list il) { init(il); } @@ -72,16 +64,6 @@ const value& tag_list::at(size_t i) const return tags.at(i); } -value& tag_list::operator[](size_t i) -{ - return tags[i]; -} - -const value& tag_list::operator[](size_t i) const -{ - return tags[i]; -} - void tag_list::set(size_t i, value&& val) { if(val.get_type() != el_type_) @@ -100,26 +82,6 @@ void tag_list::push_back(value_initializer&& val) tags.push_back(std::move(val)); } -void tag_list::pop_back() -{ - tags.pop_back(); -} - -tag_type tag_list::el_type() const -{ - return el_type_; -} - -size_t tag_list::size() const -{ - return tags.size(); -} - -void tag_list::clear() -{ - tags.clear(); -} - void tag_list::reset(tag_type type) { clear(); diff --git a/src/tag_string.cpp b/src/tag_string.cpp index 1a8f1cd5d2..2d04c25fdc 100644 --- a/src/tag_string.cpp +++ b/src/tag_string.cpp @@ -24,33 +24,6 @@ namespace nbt { -tag_string::tag_string(const std::string& str): - value(str) -{} - -tag_string::tag_string(std::string&& str) noexcept: - value(std::move(str)) -{} - -tag_string::tag_string(const char* str): - value(str) -{} - -tag_string::operator std::string&() -{ - return value; -} - -tag_string::operator const std::string&() const -{ - return value; - -} -const std::string& tag_string::get() const -{ - return value; -} - tag_string& tag_string::operator=(const std::string& str) { value = str; @@ -96,14 +69,4 @@ void tag_string::write_payload(io::stream_writer& writer) const writer.write_string(value); } -bool operator==(const tag_string& lhs, const tag_string& rhs) -{ - return lhs.get() == rhs.get(); -} - -bool operator!=(const tag_string& lhs, const tag_string& rhs) -{ - return !(lhs == rhs); -} - } diff --git a/src/value.cpp b/src/value.cpp index 4b3fa47678..8376dc9b9e 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -24,10 +24,6 @@ namespace nbt { -value::value(std::unique_ptr&& t) noexcept: - tag_(std::move(t)) -{} - value::value(tag&& t): tag_(std::move(t).move_clone()) {} @@ -59,26 +55,6 @@ void value::set(tag&& t) tag_ = std::move(t).move_clone(); } -value::operator tag&() -{ - return get(); -} - -value::operator const tag&() const -{ - return get(); -} - -tag& value::get() -{ - return *tag_; -} - -const tag& value::get() const -{ - return *tag_; -} - //Primitive assignment //FIXME: Make this less copypaste! value& value::operator=(int8_t val) @@ -325,11 +301,6 @@ value::operator double() const } } -value& value::operator=(const std::string& str) -{ - return *this = std::move(std::string(str)); -} - value& value::operator=(std::string&& str) { if(!tag_) @@ -344,11 +315,6 @@ value::operator const std::string&() const return dynamic_cast(*tag_).get(); } -value::operator bool() const -{ - return tag_ != nullptr; -} - value& value::at(const std::string& key) { return dynamic_cast(*tag_).at(key); @@ -389,21 +355,6 @@ const value& value::operator[](size_t i) const return dynamic_cast(*tag_)[i]; } -std::unique_ptr& value::get_ptr() -{ - return tag_; -} - -const std::unique_ptr& value::get_ptr() const -{ - return tag_; -} - -void value::set_ptr(std::unique_ptr&& t) -{ - tag_ = std::move(t); -} - tag_type value::get_type() const { return tag_ ? tag_->get_type() : tag_type::Null; diff --git a/src/value_initializer.cpp b/src/value_initializer.cpp index 0431bfaef2..3735bfdf09 100644 --- a/src/value_initializer.cpp +++ b/src/value_initializer.cpp @@ -23,11 +23,6 @@ namespace nbt { -value_initializer::value_initializer(std::unique_ptr&& t) noexcept: value(std::move(t)) {} -value_initializer::value_initializer(std::nullptr_t) noexcept: value(nullptr) {} -value_initializer::value_initializer(value&& val) noexcept : value(std::move(val)) {} -value_initializer::value_initializer(tag&& t) : value(std::move(t)) {} - value_initializer::value_initializer(int8_t val) : value(tag_byte(val)) {} value_initializer::value_initializer(int16_t val) : value(tag_short(val)) {} value_initializer::value_initializer(int32_t val) : value(tag_int(val)) {} -- cgit 0.0.5-2-1-g0f52 From 6ba18bfdd87053540515bb9edffcd4c0e1e2c542 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 23 Aug 2015 10:30:44 +0200 Subject: Remove some unused parameter names --- include/nbt_visitor.h | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/include/nbt_visitor.h b/include/nbt_visitor.h index edecc8edfb..fd24ae22d7 100644 --- a/include/nbt_visitor.h +++ b/include/nbt_visitor.h @@ -35,17 +35,17 @@ class nbt_visitor public: virtual ~nbt_visitor() noexcept = 0; //Abstract class - virtual void visit(tag_byte& tag) {} - virtual void visit(tag_short& tag) {} - virtual void visit(tag_int& tag) {} - virtual void visit(tag_long& tag) {} - virtual void visit(tag_float& tag) {} - virtual void visit(tag_double& tag) {} - virtual void visit(tag_byte_array& tag) {} - virtual void visit(tag_string& tag) {} - virtual void visit(tag_list& tag) {} - virtual void visit(tag_compound& tag) {} - virtual void visit(tag_int_array& tag) {} + 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&) {} }; /** @@ -58,17 +58,17 @@ class const_nbt_visitor public: virtual ~const_nbt_visitor() noexcept = 0; //Abstract class - virtual void visit(const tag_byte& tag) {} - virtual void visit(const tag_short& tag) {} - virtual void visit(const tag_int& tag) {} - virtual void visit(const tag_long& tag) {} - virtual void visit(const tag_float& tag) {} - virtual void visit(const tag_double& tag) {} - virtual void visit(const tag_byte_array& tag) {} - virtual void visit(const tag_string& tag) {} - virtual void visit(const tag_list& tag) {} - virtual void visit(const tag_compound& tag) {} - virtual void visit(const tag_int_array& tag) {} + 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&) {} }; inline nbt_visitor::~nbt_visitor() noexcept {} -- cgit 0.0.5-2-1-g0f52 From 2f78701bf83f3954d73020c011c6ffdcf2408f74 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 30 Aug 2015 14:55:51 +0200 Subject: Remove unneeded semicolon --- include/value.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/value.h b/include/value.h index c51a862b4d..d741c1f46a 100644 --- a/include/value.h +++ b/include/value.h @@ -213,7 +213,7 @@ T& value::as() template const T& value::as() const { - return tag_->as();; + return tag_->as(); } } -- cgit 0.0.5-2-1-g0f52 From f309f5c9665387128c1673e76d39944248bded2a Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 2 Sep 2015 21:02:22 +0200 Subject: Remove microtest.h --- test/format_test.cpp | 1 - test/microtest.h | 33 --------------------------------- 2 files changed, 34 deletions(-) delete mode 100644 test/microtest.h diff --git a/test/format_test.cpp b/test/format_test.cpp index b1638efd4c..8ea4095c90 100644 --- a/test/format_test.cpp +++ b/test/format_test.cpp @@ -17,7 +17,6 @@ * You should have received a copy of the GNU Lesser General Public License * along with libnbt++. If not, see . */ -#include "microtest.h" //#include "text/json_formatter.h" //#include "io/stream_reader.h" #include diff --git a/test/microtest.h b/test/microtest.h deleted file mode 100644 index 8806775f12..0000000000 --- a/test/microtest.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * libnbt++ - A library for the Minecraft Named Binary Tag format. - * Copyright (C) 2013, 2015 ljfa-ag - * - * This file is part of libnbt++. - * - * libnbt++ is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * libnbt++ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with libnbt++. If not, see . - */ -#ifndef MICROTEST_H -#define MICROTEST_H - -#include - -#define FAIL_TEST { std::cerr << "Assertion failed at " __FILE__ ":" << __LINE__ << std::endl; \ - exit(EXIT_FAILURE); } -#define ASSERT(expr) { if(!(expr)) FAIL_TEST } -#define EXPECT_EXCEPTION(expr, type) { \ - try { (expr); std::cerr << "Expected " #type " to be thrown, got no exception instead" << std::endl; FAIL_TEST } \ - catch(type&) {} \ - catch(...) { std::cerr << "Expected " #type " to be thrown, got something else instead" << std::endl; FAIL_TEST } } - -#endif -- cgit 0.0.5-2-1-g0f52 From 7b3e7818d0dd104f4a8da6e98b73d6def0f1407b Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 2 Sep 2015 21:12:37 +0200 Subject: Fix sign-compare warnings in tests --- test/endian_str_test.h | 8 ++++---- test/nbttest.h | 18 +++++++++--------- test/read_test.h | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/test/endian_str_test.h b/test/endian_str_test.h index e0b960ee54..5c7897521f 100644 --- a/test/endian_str_test.h +++ b/test/endian_str_test.h @@ -65,18 +65,18 @@ public: read_little(str, u16); TS_ASSERT_EQUALS(u16, 0x0102); read_little(str, u32); - TS_ASSERT_EQUALS(u32, 0x01020304); + TS_ASSERT_EQUALS(u32, 0x01020304u); read(str, u64, endian::little); - TS_ASSERT_EQUALS(u64, 0x0102030405060708); + TS_ASSERT_EQUALS(u64, 0x0102030405060708u); read_big(str, u8); TS_ASSERT_EQUALS(u8, 0x09); read_big(str, u16); TS_ASSERT_EQUALS(u16, 0x090A); read(str, u32, endian::big); - TS_ASSERT_EQUALS(u32, 0x090A0B0C); + TS_ASSERT_EQUALS(u32, 0x090A0B0Cu); read_big(str, u64); - TS_ASSERT_EQUALS(u64, 0x090A0B0C0D0E0F10); + TS_ASSERT_EQUALS(u64, 0x090A0B0C0D0E0F10u); TS_ASSERT(str); //Check if stream has failed } diff --git a/test/nbttest.h b/test/nbttest.h index 8255d4f6dc..1eb53acc4f 100644 --- a/test/nbttest.h +++ b/test/nbttest.h @@ -318,7 +318,7 @@ public: TS_ASSERT(list[0] == tag_string("foo")); TS_ASSERT_EQUALS(static_cast(list.at(1)), "bar"); - TS_ASSERT_EQUALS(list.size(), 2); + TS_ASSERT_EQUALS(list.size(), 2u); TS_ASSERT_THROWS(list.at(2), std::out_of_range); TS_ASSERT_THROWS(list.at(-1), std::out_of_range); @@ -327,7 +327,7 @@ public: TS_ASSERT_THROWS(list.set(1, value(tag_int(-42))), std::bad_cast); TS_ASSERT_EQUALS(static_cast(list[1]), "baz"); - TS_ASSERT_EQUALS(list.size(), 2); + TS_ASSERT_EQUALS(list.size(), 2u); tag_string values[] = {"foo", "baz"}; TS_ASSERT_EQUALS(list.end() - list.begin(), int(list.size())); TS_ASSERT(std::equal(list.begin(), list.end(), values)); @@ -339,7 +339,7 @@ public: TS_ASSERT((list != tag_list{2, 3, 5, 7})); list.clear(); - TS_ASSERT_EQUALS(list.size(), 0); + TS_ASSERT_EQUALS(list.size(), 0u); TS_ASSERT_EQUALS(list.el_type(), tag_type::String) TS_ASSERT_THROWS(list.push_back(tag_short(25)), std::bad_cast); TS_ASSERT_THROWS(list.push_back(value(nullptr)), std::bad_cast); @@ -376,7 +376,7 @@ public: { std::vector vec{1, 2, 127, -128}; tag_byte_array arr{1, 2, 127, -128}; - TS_ASSERT_EQUALS(arr.size(), 4); + TS_ASSERT_EQUALS(arr.size(), 4u); TS_ASSERT(arr.at(0) == 1 && arr[1] == 2 && arr[2] == 127 && arr.at(3) == -128); TS_ASSERT_THROWS(arr.at(-1), std::out_of_range); TS_ASSERT_THROWS(arr.at(4), std::out_of_range); @@ -387,13 +387,13 @@ public: arr.push_back(42); vec.push_back(42); - TS_ASSERT_EQUALS(arr.size(), 5); + TS_ASSERT_EQUALS(arr.size(), 5u); TS_ASSERT_EQUALS(arr.end() - arr.begin(), int(arr.size())); TS_ASSERT(std::equal(arr.begin(), arr.end(), vec.begin())); arr.pop_back(); arr.pop_back(); - TS_ASSERT_EQUALS(arr.size(), 3); + TS_ASSERT_EQUALS(arr.size(), 3u); TS_ASSERT((arr == tag_byte_array{1, 2, 127})); TS_ASSERT((arr != tag_int_array{1, 2, 127})); TS_ASSERT((arr != tag_byte_array{1, 2, -1})); @@ -406,7 +406,7 @@ public: { std::vector vec{100, 200, INT32_MAX, INT32_MIN}; tag_int_array arr{100, 200, INT32_MAX, INT32_MIN}; - TS_ASSERT_EQUALS(arr.size(), 4); + TS_ASSERT_EQUALS(arr.size(), 4u); TS_ASSERT(arr.at(0) == 100 && arr[1] == 200 && arr[2] == INT32_MAX && arr.at(3) == INT32_MIN); TS_ASSERT_THROWS(arr.at(-1), std::out_of_range); TS_ASSERT_THROWS(arr.at(4), std::out_of_range); @@ -417,13 +417,13 @@ public: arr.push_back(42); vec.push_back(42); - TS_ASSERT_EQUALS(arr.size(), 5); + TS_ASSERT_EQUALS(arr.size(), 5u); TS_ASSERT_EQUALS(arr.end() - arr.begin(), int(arr.size())); TS_ASSERT(std::equal(arr.begin(), arr.end(), vec.begin())); arr.pop_back(); arr.pop_back(); - TS_ASSERT_EQUALS(arr.size(), 3); + TS_ASSERT_EQUALS(arr.size(), 3u); TS_ASSERT((arr == tag_int_array{100, 200, INT32_MAX})); TS_ASSERT((arr != tag_int_array{100, -56, -1})); diff --git a/test/read_test.h b/test/read_test.h index bb5618b2b4..891c2933d2 100644 --- a/test/read_test.h +++ b/test/read_test.h @@ -104,7 +104,7 @@ public: //Tests if comp equals an extended variant of Notch's bigtest NBT void verify_bigtest_structure(const tag_compound& comp) { - TS_ASSERT_EQUALS(comp.size(), 13); + TS_ASSERT_EQUALS(comp.size(), 13u); TS_ASSERT(comp.at("byteTest") == tag_byte(127)); TS_ASSERT(comp.at("shortTest") == tag_short(32767)); -- cgit 0.0.5-2-1-g0f52 From 5c6cb9eca80a7bb18beb0fae42426b97a9f99fe0 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 2 Sep 2015 21:31:36 +0200 Subject: More inlining. Closes #3 --- include/tag_array.h | 12 ++++++------ include/tag_compound.h | 14 +++++++------- include/tag_list.h | 12 ++++++------ include/tag_string.h | 10 +++++----- src/tag_array.cpp | 7 ------- src/tag_compound.cpp | 12 ------------ src/tag_list.cpp | 7 ------- src/tag_string.cpp | 28 ---------------------------- 8 files changed, 24 insertions(+), 78 deletions(-) diff --git a/include/tag_array.h b/include/tag_array.h index 342d3a4ceb..c59249b571 100644 --- a/include/tag_array.h +++ b/include/tag_array.h @@ -97,12 +97,12 @@ public: void clear() { data.clear(); } //Iterators - iterator begin(); - iterator end(); - const_iterator begin() const; - const_iterator end() const; - const_iterator cbegin() const; - const_iterator cend() const; + 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; /** diff --git a/include/tag_compound.h b/include/tag_compound.h index 2948059ce9..b1c4c996dc 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -63,7 +63,7 @@ public: * 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); + value& operator[](const std::string& key) { return tags[key]; } /** * @brief Inserts or assigns a tag @@ -112,12 +112,12 @@ public: void clear() { tags.clear(); } //Iterators - iterator begin(); - iterator end(); - const_iterator begin() const; - const_iterator end() const; - const_iterator cbegin() const; - const_iterator cend() const; + 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; diff --git a/include/tag_list.h b/include/tag_list.h index 05150323b9..c27da8697d 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -151,12 +151,12 @@ public: void reset(tag_type type = tag_type::Null); //Iterators - iterator begin(); - iterator end(); - const_iterator begin() const; - const_iterator end() const; - const_iterator cbegin() const; - const_iterator cend() const; + 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 diff --git a/include/tag_string.h b/include/tag_string.h index 48e7adf497..dee47f5f97 100644 --- a/include/tag_string.h +++ b/include/tag_string.h @@ -45,11 +45,11 @@ public: const std::string& get() const { return value; } //Setters - tag_string& operator=(const std::string& str); - tag_string& operator=(std::string&& str); - tag_string& operator=(const char* str); - void set(const std::string& str); - void set(std::string&& str); + 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; /** diff --git a/src/tag_array.cpp b/src/tag_array.cpp index 6c12b9a2cd..8bb8bf72e4 100644 --- a/src/tag_array.cpp +++ b/src/tag_array.cpp @@ -37,13 +37,6 @@ T tag_array::at(size_t i) const return data.at(i); } -template auto tag_array::begin() -> iterator { return data.begin(); } -template auto tag_array::end() -> iterator { return data.end(); } -template auto tag_array::begin() const -> const_iterator { return data.begin(); } -template auto tag_array::end() const -> const_iterator { return data.end(); } -template auto tag_array::cbegin() const -> const_iterator { return data.cbegin(); } -template auto tag_array::cend() const -> const_iterator { return data.cend(); } - //Slightly different between byte_array and int_array //Reading template<> diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index 746a07955e..4085bb4e0c 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -42,11 +42,6 @@ const value& tag_compound::at(const std::string& key) const return tags.at(key); } -value& tag_compound::operator[](const std::string& key) -{ - return tags[key]; -} - std::pair tag_compound::put(const std::string& key, value_initializer&& val) { auto it = tags.find(key); @@ -82,13 +77,6 @@ bool tag_compound::has_key(const std::string& key, tag_type type) const return it != tags.end() && it->second.get_type() == type; } -auto tag_compound::begin() -> iterator { return tags.begin(); } -auto tag_compound::end() -> iterator { return tags.end(); } -auto tag_compound::begin() const -> const_iterator { return tags.begin(); } -auto tag_compound::end() const -> const_iterator { return tags.end(); } -auto tag_compound::cbegin() const -> const_iterator { return tags.cbegin(); } -auto tag_compound::cend() const -> const_iterator { return tags.cend(); } - void tag_compound::read_payload(io::stream_reader& reader) { clear(); diff --git a/src/tag_list.cpp b/src/tag_list.cpp index 0e7dda4ae6..a1a708a6a6 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -88,13 +88,6 @@ void tag_list::reset(tag_type type) el_type_ = type; } -auto tag_list::begin() -> iterator { return tags.begin(); } -auto tag_list::end() -> iterator { return tags.end(); } -auto tag_list::begin() const -> const_iterator { return tags.begin(); } -auto tag_list::end() const -> const_iterator { return tags.end(); } -auto tag_list::cbegin() const -> const_iterator { return tags.cbegin(); } -auto tag_list::cend() const -> const_iterator { return tags.cend(); } - void tag_list::read_payload(io::stream_reader& reader) { tag_type lt = reader.read_type(true); diff --git a/src/tag_string.cpp b/src/tag_string.cpp index 2d04c25fdc..30347818bd 100644 --- a/src/tag_string.cpp +++ b/src/tag_string.cpp @@ -24,34 +24,6 @@ namespace nbt { -tag_string& tag_string::operator=(const std::string& str) -{ - value = str; - return *this; -} - -tag_string& tag_string::operator=(std::string&& str) -{ - value = std::move(str); - return *this; -} - -tag_string& tag_string::operator=(const char* str) -{ - value = std::string(str); - return *this; -} - -void tag_string::set(const std::string& str) -{ - value = str; -} - -void tag_string::set(std::string&& str) -{ - value = std::move(str); -} - void tag_string::read_payload(io::stream_reader& reader) { try -- cgit 0.0.5-2-1-g0f52 From fd32c7c7d05bc7e823d7e129d16193be4635b0c7 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sat, 5 Sep 2015 13:00:31 +0200 Subject: Change some exception types thrown by tag_list --- include/tag_list.h | 18 ++++++++---------- src/tag_list.cpp | 10 +++++----- test/nbttest.h | 20 ++++++++++---------- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/include/tag_list.h b/include/tag_list.h index c27da8697d..5ae505d4fc 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -23,7 +23,7 @@ #include "crtp_tag.h" #include "tagfwd.h" #include "value_initializer.h" -#include +#include #include namespace nbt @@ -33,14 +33,12 @@ 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(). + * 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. - * - * The list's behavior is undefined if the contained values are changed in a - * way that their type differs from the list's content type. */ class tag_list final : public detail::crtp_tag { @@ -86,7 +84,7 @@ public: /** * @brief Constructs a list with the given contents - * @throw std::bad_cast if the tags are not all of the same type + * @throw std::invalid_argument if the tags are not all of the same type */ tag_list(std::initializer_list init); @@ -111,7 +109,7 @@ public: /** * @brief Assigns a value at the given index - * @throw std::bad_cast if the type of the value does not match the list's + * @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 */ @@ -119,14 +117,14 @@ public: /** * @brief Appends the tag to the end of the list - * @throw std::bad_cast if the type of the tag does not match the list's + * @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::bad_cast if the type of the tag does not match the list's + * @throw std::invalid_argument if the type of the tag does not match the list's * content type */ template @@ -206,7 +204,7 @@ 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::bad_cast(); + throw std::invalid_argument("The tag type does not match the list's content type"); tags.emplace_back(make_unique(std::forward(args)...)); } diff --git a/src/tag_list.cpp b/src/tag_list.cpp index a1a708a6a6..039bd9b77a 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -48,7 +48,7 @@ tag_list::tag_list(std::initializer_list init) for(const value& val: init) { if(!val || val.get_type() != el_type_) - throw std::bad_cast(); + throw std::invalid_argument("The values are not all the same type"); } tags.assign(init.begin(), init.end()); } @@ -67,18 +67,18 @@ const value& tag_list::at(size_t i) const void tag_list::set(size_t i, value&& val) { if(val.get_type() != el_type_) - throw std::bad_cast(); + throw std::invalid_argument("The tag type does not match the list's content type"); tags.at(i) = std::move(val); } void tag_list::push_back(value_initializer&& val) { if(!val) //don't allow null values - throw std::bad_cast(); + throw std::invalid_argument("The value must not be null"); if(el_type_ == tag_type::Null) //set content type if undetermined el_type_ = val.get_type(); else if(el_type_ != val.get_type()) - throw std::bad_cast(); + throw std::invalid_argument("The tag type does not match the list's content type"); tags.push_back(std::move(val)); } @@ -131,7 +131,7 @@ void tag_list::write_payload(io::stream_writer& writer) const if(val.get_type() != el_type_) { writer.get_ostr().setstate(std::ios::failbit); - throw std::bad_cast(); + throw std::logic_error("The tags in the list do not all match the content type"); } writer.write_payload(val); } diff --git a/test/nbttest.h b/test/nbttest.h index 1eb53acc4f..53327af983 100644 --- a/test/nbttest.h +++ b/test/nbttest.h @@ -306,13 +306,13 @@ public: { tag_list list; TS_ASSERT_EQUALS(list.el_type(), tag_type::Null); - TS_ASSERT_THROWS(list.push_back(value(nullptr)), std::bad_cast); + TS_ASSERT_THROWS(list.push_back(value(nullptr)), std::invalid_argument); list.emplace_back("foo"); TS_ASSERT_EQUALS(list.el_type(), tag_type::String); list.push_back(tag_string("bar")); - TS_ASSERT_THROWS(list.push_back(tag_int(42)), std::bad_cast); - TS_ASSERT_THROWS(list.emplace_back(), std::bad_cast); + TS_ASSERT_THROWS(list.push_back(tag_int(42)), std::invalid_argument); + TS_ASSERT_THROWS(list.emplace_back(), std::invalid_argument); TS_ASSERT((list == tag_list{"foo", "bar"})); TS_ASSERT(list[0] == tag_string("foo")); @@ -323,8 +323,8 @@ public: TS_ASSERT_THROWS(list.at(-1), std::out_of_range); list.set(1, value(tag_string("baz"))); - TS_ASSERT_THROWS(list.set(1, value(nullptr)), std::bad_cast); - TS_ASSERT_THROWS(list.set(1, value(tag_int(-42))), std::bad_cast); + TS_ASSERT_THROWS(list.set(1, value(nullptr)), std::invalid_argument); + TS_ASSERT_THROWS(list.set(1, value(tag_int(-42))), std::invalid_argument); TS_ASSERT_EQUALS(static_cast(list[1]), "baz"); TS_ASSERT_EQUALS(list.size(), 2u); @@ -341,8 +341,8 @@ public: list.clear(); TS_ASSERT_EQUALS(list.size(), 0u); TS_ASSERT_EQUALS(list.el_type(), tag_type::String) - TS_ASSERT_THROWS(list.push_back(tag_short(25)), std::bad_cast); - TS_ASSERT_THROWS(list.push_back(value(nullptr)), std::bad_cast); + TS_ASSERT_THROWS(list.push_back(tag_short(25)), std::invalid_argument); + TS_ASSERT_THROWS(list.push_back(value(nullptr)), std::invalid_argument); list.reset(); TS_ASSERT_EQUALS(list.el_type(), tag_type::Null); @@ -365,9 +365,9 @@ public: TS_ASSERT((short_list != tag_list{25, 36})); TS_ASSERT((short_list == tag_list{value(tag_short(25)), value(tag_short(36))})); - TS_ASSERT_THROWS((tag_list{value(tag_byte(4)), value(tag_int(5))}), std::bad_cast); - TS_ASSERT_THROWS((tag_list{value(nullptr), value(tag_int(6))}), std::bad_cast); - TS_ASSERT_THROWS((tag_list{value(tag_int(7)), value(tag_int(8)), value(nullptr)}), std::bad_cast); + TS_ASSERT_THROWS((tag_list{value(tag_byte(4)), value(tag_int(5))}), std::invalid_argument); + TS_ASSERT_THROWS((tag_list{value(nullptr), value(tag_int(6))}), std::invalid_argument); + TS_ASSERT_THROWS((tag_list{value(tag_int(7)), value(tag_int(8)), value(nullptr)}), std::invalid_argument); TS_ASSERT_EQUALS((tag_list(std::initializer_list{})).el_type(), tag_type::Null); TS_ASSERT_EQUALS((tag_list{2, 3, 5, 7}).el_type(), tag_type::Int); } -- cgit 0.0.5-2-1-g0f52 From 3b7d44aa0b84f9c208d906f4d10517e36220ee80 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sat, 5 Sep 2015 13:17:56 +0200 Subject: Create constant for maximum list and array length --- include/io/stream_writer.h | 4 +++- src/tag_array.cpp | 4 ++-- src/tag_list.cpp | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/include/io/stream_writer.h b/include/io/stream_writer.h index c771426bb3..959202e1ad 100644 --- a/include/io/stream_writer.h +++ b/include/io/stream_writer.h @@ -54,8 +54,10 @@ void write_tag(const std::string& key, const tag& t, std::ostream& os, endian::e class stream_writer { public: - ///Maximum length of an NBT string (65535) + ///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 diff --git a/src/tag_array.cpp b/src/tag_array.cpp index 8bb8bf72e4..99e32549b7 100644 --- a/src/tag_array.cpp +++ b/src/tag_array.cpp @@ -81,7 +81,7 @@ void tag_array::read_payload(io::stream_reader& reader) template<> void tag_array::write_payload(io::stream_writer& writer) const { - if(size() > INT32_MAX) + 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"); @@ -93,7 +93,7 @@ void tag_array::write_payload(io::stream_writer& writer) const template<> void tag_array::write_payload(io::stream_writer& writer) const { - if(size() > INT32_MAX) + if(size() > io::stream_writer::max_array_len) { writer.get_ostr().setstate(std::ios::failbit); throw std::length_error("Int array is too large for NBT"); diff --git a/src/tag_list.cpp b/src/tag_list.cpp index 039bd9b77a..67a3d4c15f 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -116,7 +116,7 @@ void tag_list::read_payload(io::stream_reader& reader) void tag_list::write_payload(io::stream_writer& writer) const { - if(size() > INT32_MAX) + if(size() > io::stream_writer::max_array_len) { writer.get_ostr().setstate(std::ios::failbit); throw std::length_error("List is too large for NBT"); -- cgit 0.0.5-2-1-g0f52 From 68cb9bb85731b361745703f28a2bf2f0cc76da7a Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 10 Sep 2015 15:33:07 +0200 Subject: Remove include guard from nbt_tags.h --- include/nbt_tags.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/nbt_tags.h b/include/nbt_tags.h index 810bf0d661..7f557fc769 100644 --- a/include/nbt_tags.h +++ b/include/nbt_tags.h @@ -17,13 +17,8 @@ * You should have received a copy of the GNU Lesser General Public License * along with libnbt++. If not, see . */ -#ifndef NBT_TAGS_H_INCLUDED -#define NBT_TAGS_H_INCLUDED - #include "tag_primitive.h" #include "tag_string.h" #include "tag_array.h" #include "tag_list.h" #include "tag_compound.h" - -#endif // NBT_TAGS_H_INCLUDED -- cgit 0.0.5-2-1-g0f52 From 1cf163c9181708a06ab8ac207dc976e066ae91e9 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Thu, 10 Sep 2015 20:24:13 +0200 Subject: Preliminary header for ozlibstream --- include/io/ozlibstream.h | 111 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 include/io/ozlibstream.h diff --git a/include/io/ozlibstream.h b/include/io/ozlibstream.h new file mode 100644 index 0000000000..c427281763 --- /dev/null +++ b/include/io/ozlibstream.h @@ -0,0 +1,111 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#ifndef OZLIBSTREAM_H_INCLUDED +#define OZLIBSTREAM_H_INCLUDED + +#include +#include +#include + +namespace zlib +{ + +///Exception thrown in case zlib encounters a problem +class zlib_error : public std::runtime_error +{ +public: + const int errcode; + + explicit zlib_error(const char* what_arg, int errcode = Z_ERRNO): + std::runtime_error(what_arg), errcode(errcode) + {} +}; + +/** + * @brief Stream buffer used by zlib::ozlibstream + * @see ozlibstream + */ +class deflate_streambuf : public std::streambuf +{ +public: + explicit deflate_streambuf(std::ostream& output, int level = -1, int window_bits = 15, int mem_level = 8, int strategy = Z_DEFAULT_STRATEGY); + ~deflate_streambuf() noexcept; + + //Moving + deflate_streambuf(deflate_streambuf&&) noexcept = default; + deflate_streambuf& operator=(deflate_streambuf&&) noexcept = default; + + //No copying + deflate_streambuf(const deflate_streambuf&) = delete; + deflate_streambuf& operator=(const deflate_streambuf&) = delete; + + std::ostream& get_ostr() const { return os; } + +private: + std::ostream& os; + std::vector in; + std::vector out; + z_stream zstr; + + void deflate_chunk(int flush = Z_NO_FLUSH); + + 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. + */ +class ozlibstream : public std::ostream +{ +public: + /** + * @param output the ostream to wrap + * @param level the compression level. Ranges from 0 to 9, or -1 for the default + * @param gzip_header whether to write a gzip header rather than a zlib header + */ + explicit ozlibstream(std::ostream& output, int level, bool gzip): + ozlibstream(output, level, 15 + (gzip ? 16 : 0)) + {} + + /** + * @param output the ostream to wrap + * @param level the compression level. Ranges from 0 to 9, or -1 for the default + * + * Refer to the zlib documentation of deflateInit2 for a detailed explanation of the arguments. + */ + explicit ozlibstream(std::ostream& output, int level = -1, int window_bits = 15, int mem_level = 8, int strategy = Z_DEFAULT_STRATEGY): + buf(output, level, window_bits, mem_level, strategy) + { + init(&buf); + } + ///@return the wrapped ostream + std::ostream& get_ostr() const { return buf.get_ostr(); } + +private: + deflate_streambuf buf; +}; + +} + +#endif // OZLIBSTREAM_H_INCLUDED -- cgit 0.0.5-2-1-g0f52 From ec792d93a73e49738d4ded361f23acfae3a00e94 Mon Sep 17 00:00:00 2001 From: ljfa Date: Sun, 13 Sep 2015 19:47:30 +0200 Subject: Fix ambiguities and missing default constructor Compiler errors that g++ didn't report --- include/text/json_formatter.h | 1 + test/endian_str_test.h | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/include/text/json_formatter.h b/include/text/json_formatter.h index c0d1bfc396..4f3e434177 100644 --- a/include/text/json_formatter.h +++ b/include/text/json_formatter.h @@ -36,6 +36,7 @@ namespace text class json_formatter { public: + json_formatter() {} void print(std::ostream& os, const tag& t) const; }; diff --git a/test/endian_str_test.h b/test/endian_str_test.h index 5c7897521f..6dfba9fc27 100644 --- a/test/endian_str_test.h +++ b/test/endian_str_test.h @@ -34,13 +34,13 @@ public: write_little(str, uint8_t (0x01)); write_little(str, uint16_t(0x0102)); - write (str, uint32_t(0x01020304), endian::little); + write (str, uint32_t(0x01020304), little); write_little(str, uint64_t(0x0102030405060708)); write_big (str, uint8_t (0x09)); write_big (str, uint16_t(0x090A)); write_big (str, uint32_t(0x090A0B0C)); - write (str, uint64_t(0x090A0B0C0D0E0F10), endian::big); + write (str, uint64_t(0x090A0B0C0D0E0F10), big); std::string expected{ 1, @@ -66,14 +66,14 @@ public: TS_ASSERT_EQUALS(u16, 0x0102); read_little(str, u32); TS_ASSERT_EQUALS(u32, 0x01020304u); - read(str, u64, endian::little); + read(str, u64, little); TS_ASSERT_EQUALS(u64, 0x0102030405060708u); read_big(str, u8); TS_ASSERT_EQUALS(u8, 0x09); read_big(str, u16); TS_ASSERT_EQUALS(u16, 0x090A); - read(str, u32, endian::big); + read(str, u32, big); TS_ASSERT_EQUALS(u32, 0x090A0B0Cu); read_big(str, u64); TS_ASSERT_EQUALS(u64, 0x090A0B0C0D0E0F10u); @@ -88,11 +88,11 @@ public: write_little(str, int8_t (-0x01)); write_little(str, int16_t(-0x0102)); write_little(str, int32_t(-0x01020304)); - write (str, int64_t(-0x0102030405060708), endian::little); + write (str, int64_t(-0x0102030405060708), little); write_big (str, int8_t (-0x09)); write_big (str, int16_t(-0x090A)); - write (str, int32_t(-0x090A0B0C), endian::big); + write (str, int32_t(-0x090A0B0C), big); write_big (str, int64_t(-0x090A0B0C0D0E0F10)); std::string expected{ //meh, stupid narrowing conversions @@ -117,7 +117,7 @@ public: TS_ASSERT_EQUALS(i8, -0x01); read_little(str, i16); TS_ASSERT_EQUALS(i16, -0x0102); - read(str, i32, endian::little); + read(str, i32, little); TS_ASSERT_EQUALS(i32, -0x01020304); read_little(str, i64); TS_ASSERT_EQUALS(i64, -0x0102030405060708); @@ -128,7 +128,7 @@ public: TS_ASSERT_EQUALS(i16, -0x090A); read_big(str, i32); TS_ASSERT_EQUALS(i32, -0x090A0B0C); - read(str, i64, endian::big); + read(str, i64, big); TS_ASSERT_EQUALS(i64, -0x090A0B0C0D0E0F10); TS_ASSERT(str); //Check if stream has failed -- cgit 0.0.5-2-1-g0f52 From ec4d2df1f946fdf109d13c6d49f13869f11ae95e Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 14 Sep 2015 19:50:30 +0200 Subject: Put zlib_error into its own file --- include/io/ozlibstream.h | 12 +----------- include/io/zlib_error.h | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 11 deletions(-) create mode 100644 include/io/zlib_error.h diff --git a/include/io/ozlibstream.h b/include/io/ozlibstream.h index c427281763..6ebd3c0dd0 100644 --- a/include/io/ozlibstream.h +++ b/include/io/ozlibstream.h @@ -20,6 +20,7 @@ #ifndef OZLIBSTREAM_H_INCLUDED #define OZLIBSTREAM_H_INCLUDED +#include "io/zlib_error.h" #include #include #include @@ -27,17 +28,6 @@ namespace zlib { -///Exception thrown in case zlib encounters a problem -class zlib_error : public std::runtime_error -{ -public: - const int errcode; - - explicit zlib_error(const char* what_arg, int errcode = Z_ERRNO): - std::runtime_error(what_arg), errcode(errcode) - {} -}; - /** * @brief Stream buffer used by zlib::ozlibstream * @see ozlibstream diff --git a/include/io/zlib_error.h b/include/io/zlib_error.h new file mode 100644 index 0000000000..12eaf20a3f --- /dev/null +++ b/include/io/zlib_error.h @@ -0,0 +1,17 @@ +#ifndef ZLIB_ERROR_H_INCLUDED +#define ZLIB_ERROR_H_INCLUDED + +#include + +///Exception thrown in case zlib encounters a problem +class zlib_error : public std::runtime_error +{ +public: + const int errcode; + + explicit zlib_error(const char* what_arg, int errcode = -1): + std::runtime_error(what_arg), errcode(errcode) + {} +}; + +#endif // ZLIB_ERROR_H_INCLUDED -- cgit 0.0.5-2-1-g0f52 From 7668b2e16894ef6cfe13c30a33823986d02fa5b1 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 14 Sep 2015 20:23:29 +0200 Subject: Add preliminary header for izlibstream --- include/io/izlibstream.h | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 include/io/izlibstream.h diff --git a/include/io/izlibstream.h b/include/io/izlibstream.h new file mode 100644 index 0000000000..99b56ac5cf --- /dev/null +++ b/include/io/izlibstream.h @@ -0,0 +1,80 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#ifndef IZLIBSTREAM_H_INCLUDED +#define IZLIBSTREAM_H_INCLUDED + +#include +#include +#include + +namespace zlib +{ + +/** + * @brief Stream buffer used by zlib::izlibstream + * @see izlibstream + */ +class inflate_streambuf : public std::streambuf +{ +public: + explicit inflate_streambuf(std::istream& input, size_t bufsize = 32768, int window_bits = 32); + ~inflate_streambuf() noexcept; + + //No copying or moving + inflate_streambuf(const inflate_streambuf&) = delete; + inflate_streambuf& operator=(const inflate_streambuf&) = delete; + + std::istream& get_istr() const { return is; } + +private: + std::istream& is; + std::vector in; + std::vector out; + z_stream zstr; +}; + +/** + * @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. + */ +class izlibstream : public std::istream +{ +public: + /** + * @param input the istream to wrap + * @param bufsize the size of the internal buffer + */ + explicit izlibstream(std::istream& input, size_t bufsize = 32768): + buf(input, bufsize) + { + init(&buf); + } + ///@return the wrapped istream + std::istream& get_istr() const { return buf.get_istr(); } + +private: + inflate_streambuf buf; +}; + +} + +#endif // IZLIBSTREAM_H_INCLUDED -- cgit 0.0.5-2-1-g0f52 From 0f7e89cfce630788e01a9a58467a0ab8bf252681 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 14 Sep 2015 20:51:38 +0200 Subject: Add test case for izlibstream --- test/CMakeLists.txt | 3 +++ test/zlibstream_test.h | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 test/zlibstream_test.h diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c13f09f2da..2c6c09ef61 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,6 +17,9 @@ add_custom_command(TARGET read_test POST_BUILD CXXTEST_ADD_TEST(write_test write_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/write_test.h) target_link_libraries(write_test nbt++) +CXXTEST_ADD_TEST(zlibstream_test zlibstream_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/zlibstream_test.h) +target_link_libraries(zlibstream_test nbt++) + add_executable(format_test format_test.cpp) target_link_libraries(format_test nbt++) add_test(format_test format_test) diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h new file mode 100644 index 0000000000..65f83b89dd --- /dev/null +++ b/test/zlibstream_test.h @@ -0,0 +1,49 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include +#include "io/izlibstream.h" +#include "io/ozlibstream.h" +#include +#include + +using namespace zlib; + +class zlibstream_test : public CxxTest::TestSuite +{ +public: + void test_izlibstream() + { + std::ifstream gzip_in("bigtest.nbt", std::ios::binary); + std::ifstream expected_in("bigtest_uncompr", std::ios::binary); + std::stringbuf expected; + expected_in >> &expected; //Dump uncompressed file contents into streambuf + TS_ASSERT(gzip_in && expected_in); + TS_ASSERT_DIFFERS(expected.str().size(), 0u); + expected_in.close(); + + izlibstream igzs(gzip_in, 512); //Small buffer so not all fits at once (the compressed file is 561 bytes) + TS_ASSERT(igzs); + + std::stringbuf data; + igzs >> &data; + TS_ASSERT(igzs); + TS_ASSERT_EQUALS(data.str(), expected.str()); + } +}; -- cgit 0.0.5-2-1-g0f52 From ab346593cab00bc000cb34819f3784580d21e6d7 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 14 Sep 2015 21:03:37 +0200 Subject: Add zlib dependency Todo: make it optional --- CMakeLists.txt | 3 ++- test/CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a2eb71cb8..cd073e0e56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,8 @@ add_library(nbt++ STATIC src/text/json_formatter.cpp) -find_package(CxxTest) +find_package(zlib REQUIRED) +find_package(CxxTest) option(NBT_BUILD_TESTS "Build the unit tests. Requires CxxTest." ${CXXTEST_FOUND}) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2c6c09ef61..c640ef054e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -18,7 +18,7 @@ CXXTEST_ADD_TEST(write_test write_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/write_tes target_link_libraries(write_test nbt++) CXXTEST_ADD_TEST(zlibstream_test zlibstream_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/zlibstream_test.h) -target_link_libraries(zlibstream_test nbt++) +target_link_libraries(zlibstream_test nbt++ z) add_executable(format_test format_test.cpp) target_link_libraries(format_test nbt++) -- cgit 0.0.5-2-1-g0f52 From a1804179aaa98b570afd117ec977d7db1e56cc90 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 15 Sep 2015 12:02:56 +0200 Subject: Change test a bit --- test/zlibstream_test.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h index 65f83b89dd..4bcf140fd8 100644 --- a/test/zlibstream_test.h +++ b/test/zlibstream_test.h @@ -38,12 +38,14 @@ public: TS_ASSERT_DIFFERS(expected.str().size(), 0u); expected_in.close(); - izlibstream igzs(gzip_in, 512); //Small buffer so not all fits at once (the compressed file is 561 bytes) - TS_ASSERT(igzs); + izlibstream igzs(gzip_in, 256); //Small buffer so not all fits at once (the compressed file is 561 bytes) + TS_ASSERT(igzs.good()); + TS_ASSERT(!igzs.eof()); std::stringbuf data; igzs >> &data; TS_ASSERT(igzs); + TS_ASSERT(igzs.eof()); TS_ASSERT_EQUALS(data.str(), expected.str()); } }; -- cgit 0.0.5-2-1-g0f52 From f3cd19f1fa88b2bc33bfb96fb6c877a4d62c5c97 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Mon, 14 Sep 2015 23:03:31 +0200 Subject: Add implementation of inflate_streambuf (still broken though) --- CMakeLists.txt | 1 + include/io/izlibstream.h | 2 ++ src/io/izlibstream.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 src/io/izlibstream.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index cd073e0e56..c16860a8ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ add_library(nbt++ STATIC src/value.cpp src/value_initializer.cpp + src/io/izlibstream.cpp src/io/stream_reader.cpp src/io/stream_writer.cpp diff --git a/include/io/izlibstream.h b/include/io/izlibstream.h index 99b56ac5cf..16c82d9f9d 100644 --- a/include/io/izlibstream.h +++ b/include/io/izlibstream.h @@ -48,6 +48,8 @@ private: std::vector in; std::vector out; z_stream zstr; + + int_type underflow() override; }; /** diff --git a/src/io/izlibstream.cpp b/src/io/izlibstream.cpp new file mode 100644 index 0000000000..632f0cf7c6 --- /dev/null +++ b/src/io/izlibstream.cpp @@ -0,0 +1,86 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include "io/izlibstream.h" +#include "io/zlib_error.h" + +namespace zlib +{ + +inflate_streambuf::inflate_streambuf(std::istream& input, size_t bufsize, int window_bits): + is(input), in(bufsize), out(bufsize) +{ + zstr.zalloc = Z_NULL; + zstr.zfree = Z_NULL; + zstr.opaque = Z_NULL; + zstr.next_in = Z_NULL; + zstr.avail_in = 0; + int ret = inflateInit2(&zstr, window_bits); + if(ret != Z_OK) + throw zlib_error("inflateInit failed", ret); + + char* end = out.data() + out.size(); + setg(end, end, end); +} + +inflate_streambuf::~inflate_streambuf() +{ + inflateEnd(&zstr); +} + +inflate_streambuf::int_type inflate_streambuf::underflow() +{ + if(gptr() < egptr()) + return traits_type::to_int_type(*gptr()); + + size_t have; + do + { + //Read if input buffer is empty + if(zstr.avail_in <= 0) + { + is.read(in.data(), in.size()); + size_t count = is.gcount(); + zstr.next_in = reinterpret_cast(in.data()); + zstr.avail_in = count; + } + + zstr.next_out = reinterpret_cast(out.data()); + zstr.avail_out = out.size(); + + int ret = inflate(&zstr, Z_NO_FLUSH); + switch(ret) + { + case Z_NEED_DICT: + case Z_DATA_ERROR: + throw zlib_error("Error decompressing data", ret); + case Z_MEM_ERROR: + throw std::bad_alloc(); + case Z_STREAM_END: + return traits_type::eof(); + } + + have = out.size() - zstr.avail_out; + } while(have == 0); + + setg(out.data(), out.data(), out.data() + have); + return traits_type::to_int_type(*gptr()); +} + +} -- cgit 0.0.5-2-1-g0f52 From c61cb502c1bb06b5ff93a6766c500c17f1e7dcc0 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 15 Sep 2015 08:17:01 +0200 Subject: No output doesn't necessarily mean EOF --- src/io/izlibstream.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/io/izlibstream.cpp b/src/io/izlibstream.cpp index 632f0cf7c6..9079489507 100644 --- a/src/io/izlibstream.cpp +++ b/src/io/izlibstream.cpp @@ -65,6 +65,7 @@ inflate_streambuf::int_type inflate_streambuf::underflow() zstr.avail_out = out.size(); int ret = inflate(&zstr, Z_NO_FLUSH); + have = out.size() - zstr.avail_out; switch(ret) { case Z_NEED_DICT: @@ -73,10 +74,11 @@ inflate_streambuf::int_type inflate_streambuf::underflow() case Z_MEM_ERROR: throw std::bad_alloc(); case Z_STREAM_END: - return traits_type::eof(); + if(have == 0) + return traits_type::eof(); + break; } - have = out.size() - zstr.avail_out; } while(have == 0); setg(out.data(), out.data(), out.data() + have); -- cgit 0.0.5-2-1-g0f52 From 0c6f7fcccc1a9e20bbd68b5bc09bc8853d15134c Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 15 Sep 2015 10:37:52 +0200 Subject: Use different windowBits value to avoid zlib bug --- include/io/izlibstream.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/io/izlibstream.h b/include/io/izlibstream.h index 16c82d9f9d..b88839ea99 100644 --- a/include/io/izlibstream.h +++ b/include/io/izlibstream.h @@ -34,7 +34,7 @@ namespace zlib class inflate_streambuf : public std::streambuf { public: - explicit inflate_streambuf(std::istream& input, size_t bufsize = 32768, int window_bits = 32); + explicit inflate_streambuf(std::istream& input, size_t bufsize = 32768, int window_bits = 32 + 15); ~inflate_streambuf() noexcept; //No copying or moving -- cgit 0.0.5-2-1-g0f52 From 4f96ef6b0d4aede214d208090e86cb7b8091f49c Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 15 Sep 2015 10:58:49 +0200 Subject: Change zlib_error to take message and code --- include/io/zlib_error.h | 6 ++++-- src/io/izlibstream.cpp | 7 ++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/include/io/zlib_error.h b/include/io/zlib_error.h index 12eaf20a3f..ce375a6ccd 100644 --- a/include/io/zlib_error.h +++ b/include/io/zlib_error.h @@ -2,6 +2,7 @@ #define ZLIB_ERROR_H_INCLUDED #include +#include ///Exception thrown in case zlib encounters a problem class zlib_error : public std::runtime_error @@ -9,8 +10,9 @@ class zlib_error : public std::runtime_error public: const int errcode; - explicit zlib_error(const char* what_arg, int errcode = -1): - std::runtime_error(what_arg), errcode(errcode) + explicit zlib_error(const char* msg, int errcode): + std::runtime_error(std::string(zError(errcode)) + ": " + msg), + errcode(errcode) {} }; diff --git a/src/io/izlibstream.cpp b/src/io/izlibstream.cpp index 9079489507..f9d9463d95 100644 --- a/src/io/izlibstream.cpp +++ b/src/io/izlibstream.cpp @@ -33,7 +33,7 @@ inflate_streambuf::inflate_streambuf(std::istream& input, size_t bufsize, int wi zstr.avail_in = 0; int ret = inflateInit2(&zstr, window_bits); if(ret != Z_OK) - throw zlib_error("inflateInit failed", ret); + throw zlib_error(zstr.msg, ret); char* end = out.data() + out.size(); setg(end, end, end); @@ -70,15 +70,16 @@ inflate_streambuf::int_type inflate_streambuf::underflow() { case Z_NEED_DICT: case Z_DATA_ERROR: - throw zlib_error("Error decompressing data", ret); + throw zlib_error(zstr.msg, ret); + case Z_MEM_ERROR: throw std::bad_alloc(); + case Z_STREAM_END: if(have == 0) return traits_type::eof(); break; } - } while(have == 0); setg(out.data(), out.data(), out.data() + have); -- cgit 0.0.5-2-1-g0f52 From 089a804a12c0592b0d2a396df1d35dad4fd134c0 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 15 Sep 2015 12:04:03 +0200 Subject: Enable exceptions flag in test --- test/zlibstream_test.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h index 4bcf140fd8..caa62a87c8 100644 --- a/test/zlibstream_test.h +++ b/test/zlibstream_test.h @@ -39,11 +39,12 @@ public: expected_in.close(); izlibstream igzs(gzip_in, 256); //Small buffer so not all fits at once (the compressed file is 561 bytes) + igzs.exceptions(std::ios::failbit); TS_ASSERT(igzs.good()); TS_ASSERT(!igzs.eof()); std::stringbuf data; - igzs >> &data; + TS_ASSERT_THROWS_NOTHING(igzs >> &data); TS_ASSERT(igzs); TS_ASSERT(igzs.eof()); TS_ASSERT_EQUALS(data.str(), expected.str()); -- cgit 0.0.5-2-1-g0f52 From a692b3be10084aec02a5b6483c878e1dc20081f2 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 15 Sep 2015 11:34:44 +0200 Subject: Check for errors in input stream --- include/io/izlibstream.h | 1 + src/io/izlibstream.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/io/izlibstream.h b/include/io/izlibstream.h index b88839ea99..2b67003232 100644 --- a/include/io/izlibstream.h +++ b/include/io/izlibstream.h @@ -48,6 +48,7 @@ private: std::vector in; std::vector out; z_stream zstr; + bool stream_end; int_type underflow() override; }; diff --git a/src/io/izlibstream.cpp b/src/io/izlibstream.cpp index f9d9463d95..db69e0e626 100644 --- a/src/io/izlibstream.cpp +++ b/src/io/izlibstream.cpp @@ -24,7 +24,7 @@ namespace zlib { inflate_streambuf::inflate_streambuf(std::istream& input, size_t bufsize, int window_bits): - is(input), in(bufsize), out(bufsize) + is(input), in(bufsize), out(bufsize), stream_end(false) { zstr.zalloc = Z_NULL; zstr.zfree = Z_NULL; @@ -56,7 +56,12 @@ inflate_streambuf::int_type inflate_streambuf::underflow() if(zstr.avail_in <= 0) { is.read(in.data(), in.size()); + if(is.bad()) + throw std::ios_base::failure("Input stream is bad"); size_t count = is.gcount(); + if(count == 0 && !stream_end) + throw zlib_error("Unexpected end of stream", Z_DATA_ERROR); + zstr.next_in = reinterpret_cast(in.data()); zstr.avail_in = count; } @@ -76,6 +81,7 @@ inflate_streambuf::int_type inflate_streambuf::underflow() throw std::bad_alloc(); case Z_STREAM_END: + stream_end = true; if(have == 0) return traits_type::eof(); break; -- cgit 0.0.5-2-1-g0f52 From 82efd65efe324e0a78425b57bff255653db2c92b Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 15 Sep 2015 11:43:22 +0200 Subject: Add more doxygen to inflate_streambuf --- include/io/izlibstream.h | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/include/io/izlibstream.h b/include/io/izlibstream.h index 2b67003232..edf632eddf 100644 --- a/include/io/izlibstream.h +++ b/include/io/izlibstream.h @@ -29,11 +29,21 @@ namespace zlib /** * @brief Stream buffer used by zlib::izlibstream - * @see izlibstream + * @sa izlibstream */ class inflate_streambuf : public std::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; @@ -41,6 +51,7 @@ public: inflate_streambuf(const inflate_streambuf&) = delete; inflate_streambuf& operator=(const inflate_streambuf&) = delete; + ///@return the wrapped istream std::istream& get_istr() const { return is; } private: @@ -58,13 +69,15 @@ private: * * This istream wraps another istream. The izlibstream will read compressed * data from the wrapped istream and inflate (decompress) it with zlib. + * + * @sa inflate_streambuf */ class izlibstream : public std::istream { public: /** * @param input the istream to wrap - * @param bufsize the size of the internal buffer + * @param bufsize the size of the internal buffers */ explicit izlibstream(std::istream& input, size_t bufsize = 32768): buf(input, bufsize) -- cgit 0.0.5-2-1-g0f52 From 0f76512c7a8d4aaf3928c9c9bb4bc35dda1ac171 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 15 Sep 2015 12:50:06 +0200 Subject: Add tests for different buffer sizes --- test/zlibstream_test.h | 74 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 16 deletions(-) diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h index caa62a87c8..7620246d74 100644 --- a/test/zlibstream_test.h +++ b/test/zlibstream_test.h @@ -27,26 +27,68 @@ using namespace zlib; class zlibstream_test : public CxxTest::TestSuite { +private: + std::stringbuf bigtest; + public: - void test_izlibstream() + zlibstream_test() + { + std::ifstream bigtest_f("bigtest_uncompr", std::ios::binary); + bigtest_f >> &bigtest; + if(!bigtest_f || bigtest.str().size() == 0) + throw std::runtime_error("Could not read bigtest_uncompr file"); + } + + void test_izlibstream_gzip() { std::ifstream gzip_in("bigtest.nbt", std::ios::binary); - std::ifstream expected_in("bigtest_uncompr", std::ios::binary); - std::stringbuf expected; - expected_in >> &expected; //Dump uncompressed file contents into streambuf - TS_ASSERT(gzip_in && expected_in); - TS_ASSERT_DIFFERS(expected.str().size(), 0u); - expected_in.close(); - - izlibstream igzs(gzip_in, 256); //Small buffer so not all fits at once (the compressed file is 561 bytes) - igzs.exceptions(std::ios::failbit); - TS_ASSERT(igzs.good()); - TS_ASSERT(!igzs.eof()); + TS_ASSERT(gzip_in); std::stringbuf data; - TS_ASSERT_THROWS_NOTHING(igzs >> &data); - TS_ASSERT(igzs); - TS_ASSERT(igzs.eof()); - TS_ASSERT_EQUALS(data.str(), expected.str()); + //Small buffer so not all fits at once (the compressed file is 561 bytes) + { + izlibstream igzs(gzip_in, 256); + igzs.exceptions(std::ios::failbit); + TS_ASSERT(igzs.good()); + TS_ASSERT(!igzs.eof()); + + TS_ASSERT_THROWS_NOTHING(igzs >> &data); + TS_ASSERT(igzs); + TS_ASSERT(igzs.eof()); + TS_ASSERT_EQUALS(data.str(), bigtest.str()); + } + + //Clear and reuse buffers + data.str(""); + gzip_in.clear(); + gzip_in.seekg(0); + //Now try the same with larger buffer (but not large enough for all output, uncompressed size 1561 bytes) + { + izlibstream igzs(gzip_in, 1000); + igzs.exceptions(std::ios::failbit); + TS_ASSERT(igzs.good()); + TS_ASSERT(!igzs.eof()); + + TS_ASSERT_THROWS_NOTHING(igzs >> &data); + TS_ASSERT(igzs); + TS_ASSERT(igzs.eof()); + TS_ASSERT_EQUALS(data.str(), bigtest.str()); + } + + data.str(""); + gzip_in.clear(); + gzip_in.seekg(0); + //Now with large buffer + { + izlibstream igzs(gzip_in, 4000); + igzs.exceptions(std::ios::failbit); + TS_ASSERT(igzs.good()); + TS_ASSERT(!igzs.eof()); + + TS_ASSERT_THROWS_NOTHING(igzs >> &data); + TS_ASSERT(igzs); + TS_ASSERT(igzs.eof()); + TS_ASSERT_EQUALS(data.str(), bigtest.str()); + } } }; -- cgit 0.0.5-2-1-g0f52 From 7a8a1833dc766abc1626a1e590e3f39a7d503062 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 15 Sep 2015 13:25:36 +0200 Subject: Add tests for corrupted input data --- test/testfiles/bigtest_corrupt.nbt | Bin 0 -> 561 bytes test/testfiles/bigtest_eof.nbt | Bin 0 -> 426 bytes test/zlibstream_test.h | 29 +++++++++++++++++++++++++---- 3 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 test/testfiles/bigtest_corrupt.nbt create mode 100644 test/testfiles/bigtest_eof.nbt diff --git a/test/testfiles/bigtest_corrupt.nbt b/test/testfiles/bigtest_corrupt.nbt new file mode 100644 index 0000000000..71eba42a7b Binary files /dev/null and b/test/testfiles/bigtest_corrupt.nbt differ diff --git a/test/testfiles/bigtest_eof.nbt b/test/testfiles/bigtest_eof.nbt new file mode 100644 index 0000000000..eeedb9d26d Binary files /dev/null and b/test/testfiles/bigtest_eof.nbt differ diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h index 7620246d74..f37dcdbd91 100644 --- a/test/zlibstream_test.h +++ b/test/zlibstream_test.h @@ -39,7 +39,7 @@ public: throw std::runtime_error("Could not read bigtest_uncompr file"); } - void test_izlibstream_gzip() + void test_inflate_gzip() { std::ifstream gzip_in("bigtest.nbt", std::ios::binary); TS_ASSERT(gzip_in); @@ -50,7 +50,6 @@ public: izlibstream igzs(gzip_in, 256); igzs.exceptions(std::ios::failbit); TS_ASSERT(igzs.good()); - TS_ASSERT(!igzs.eof()); TS_ASSERT_THROWS_NOTHING(igzs >> &data); TS_ASSERT(igzs); @@ -67,7 +66,6 @@ public: izlibstream igzs(gzip_in, 1000); igzs.exceptions(std::ios::failbit); TS_ASSERT(igzs.good()); - TS_ASSERT(!igzs.eof()); TS_ASSERT_THROWS_NOTHING(igzs >> &data); TS_ASSERT(igzs); @@ -83,7 +81,6 @@ public: izlibstream igzs(gzip_in, 4000); igzs.exceptions(std::ios::failbit); TS_ASSERT(igzs.good()); - TS_ASSERT(!igzs.eof()); TS_ASSERT_THROWS_NOTHING(igzs >> &data); TS_ASSERT(igzs); @@ -91,4 +88,28 @@ public: TS_ASSERT_EQUALS(data.str(), bigtest.str()); } } + + void test_inflate_corrupt() + { + std::ifstream gzip_in("bigtest_corrupt.nbt", std::ios::binary); + TS_ASSERT(gzip_in); + + std::stringbuf data; + { + izlibstream igzs(gzip_in); + igzs.exceptions(std::ios::failbit); + TS_ASSERT_THROWS(igzs >> &data, zlib_error); + } + + gzip_in.close(); + gzip_in.open("bigtest_eof.nbt", std::ios::binary); + TS_ASSERT(gzip_in); + + data.str(""); + { + izlibstream igzs(gzip_in); + igzs.exceptions(std::ios::failbit); + TS_ASSERT_THROWS(igzs >> &data, zlib_error); + } + } }; -- cgit 0.0.5-2-1-g0f52 From 5d0b98fb46b1aba137677d45b3c74726143eb6fa Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 15 Sep 2015 14:25:23 +0200 Subject: Copying and moving is implicitly disabled anyway --- include/io/izlibstream.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/io/izlibstream.h b/include/io/izlibstream.h index edf632eddf..4e9dc9c1ee 100644 --- a/include/io/izlibstream.h +++ b/include/io/izlibstream.h @@ -47,10 +47,6 @@ public: explicit inflate_streambuf(std::istream& input, size_t bufsize = 32768, int window_bits = 32 + 15); ~inflate_streambuf() noexcept; - //No copying or moving - inflate_streambuf(const inflate_streambuf&) = delete; - inflate_streambuf& operator=(const inflate_streambuf&) = delete; - ///@return the wrapped istream std::istream& get_istr() const { return is; } -- cgit 0.0.5-2-1-g0f52 From 2d8cc72760cfd26e56b70786b30c4f6dafe04aa0 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 15 Sep 2015 14:59:02 +0200 Subject: Handle nullptr message in zlib_error --- include/io/zlib_error.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/io/zlib_error.h b/include/io/zlib_error.h index ce375a6ccd..1ecb50e553 100644 --- a/include/io/zlib_error.h +++ b/include/io/zlib_error.h @@ -11,7 +11,9 @@ public: const int errcode; explicit zlib_error(const char* msg, int errcode): - std::runtime_error(std::string(zError(errcode)) + ": " + msg), + std::runtime_error(msg + ? std::string(zError(errcode)) + ": " + msg + : zError(errcode)), errcode(errcode) {} }; -- cgit 0.0.5-2-1-g0f52 From ef10a4f34a3f10bc1fc7ee0b7b1be6f37d286741 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 15 Sep 2015 20:41:11 +0200 Subject: Add test for inflating zlib data --- test/testfiles/bigtest.zlib | Bin 0 -> 528 bytes test/zlibstream_test.h | 16 ++++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 test/testfiles/bigtest.zlib diff --git a/test/testfiles/bigtest.zlib b/test/testfiles/bigtest.zlib new file mode 100644 index 0000000000..36aeee57fb Binary files /dev/null and b/test/testfiles/bigtest.zlib differ diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h index f37dcdbd91..d2e6b6ee82 100644 --- a/test/zlibstream_test.h +++ b/test/zlibstream_test.h @@ -89,6 +89,22 @@ public: } } + void test_inflate_zlib() + { + std::ifstream zlib_in("bigtest.zlib", std::ios::binary); + TS_ASSERT(zlib_in); + + std::stringbuf data; + izlibstream izls(zlib_in, 256); + izls.exceptions(std::ios::failbit); + TS_ASSERT(izls.good()); + + TS_ASSERT_THROWS_NOTHING(izls >> &data); + TS_ASSERT(izls); + TS_ASSERT(izls.eof()); + TS_ASSERT_EQUALS(data.str(), bigtest.str()); + } + void test_inflate_corrupt() { std::ifstream gzip_in("bigtest_corrupt.nbt", std::ios::binary); -- cgit 0.0.5-2-1-g0f52 From 84cf824153d7a6bc8e7240b3683cf6f6e83be65e Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 15 Sep 2015 21:02:03 +0200 Subject: Make sure testfiles are copied for all tests that need them --- test/CMakeLists.txt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c640ef054e..bd4ac0295d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,6 +2,13 @@ enable_testing() include_directories(${libnbt++_SOURCE_DIR}/include) include_directories(${CXXTEST_INCLUDE_DIR}) +#Specifies that the directory containing the testfiles get copied when the target is built +function(use_testfiles target) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E + copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/testfiles ${CMAKE_CURRENT_BINARY_DIR}) +endfunction() + CXXTEST_ADD_TEST(nbttest nbttest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/nbttest.h) target_link_libraries(nbttest nbt++) @@ -10,15 +17,15 @@ target_link_libraries(endian_str_test nbt++) CXXTEST_ADD_TEST(read_test read_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/read_test.h) target_link_libraries(read_test nbt++) -add_custom_command(TARGET read_test POST_BUILD - COMMAND ${CMAKE_COMMAND} -E - copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/testfiles ${CMAKE_CURRENT_BINARY_DIR}) +use_testfiles(read_test) CXXTEST_ADD_TEST(write_test write_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/write_test.h) target_link_libraries(write_test nbt++) +use_testfiles(write_test) CXXTEST_ADD_TEST(zlibstream_test zlibstream_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/zlibstream_test.h) target_link_libraries(zlibstream_test nbt++ z) +use_testfiles(zlibstream_test) add_executable(format_test format_test.cpp) target_link_libraries(format_test nbt++) -- cgit 0.0.5-2-1-g0f52 From cfbde1f32cc505adf9822339692331c54e0f3b2f Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 15 Sep 2015 21:12:07 +0200 Subject: Make test_visitor more accurate --- test/nbttest.h | 59 ++++++++++++++++++++++++---------------------------------- 1 file changed, 24 insertions(+), 35 deletions(-) diff --git a/test/nbttest.h b/test/nbttest.h index 53327af983..0ebce0c306 100644 --- a/test/nbttest.h +++ b/test/nbttest.h @@ -435,42 +435,31 @@ public: { struct : public nbt_visitor { - tag_type visited = tag_type::Null; - - void visit(tag_byte& tag) { visited = tag_type::Byte; } - void visit(tag_short& tag) { visited = tag_type::Short; } - void visit(tag_int& tag) { visited = tag_type::Int; } - void visit(tag_long& tag) { visited = tag_type::Long; } - void visit(tag_float& tag) { visited = tag_type::Float; } - void visit(tag_double& tag) { visited = tag_type::Double; } - void visit(tag_byte_array& tag) { visited = tag_type::Byte_Array; } - void visit(tag_string& tag) { visited = tag_type::String; } - void visit(tag_list& tag) { visited = tag_type::List; } - void visit(tag_compound& tag) { visited = tag_type::Compound; } - void visit(tag_int_array& tag) { visited = tag_type::Int_Array; } + tag* visited = nullptr; + + void visit(tag_byte& tag) { visited = &tag; } + void visit(tag_short& tag) { visited = &tag; } + void visit(tag_int& tag) { visited = &tag; } + void visit(tag_long& tag) { visited = &tag; } + void visit(tag_float& tag) { visited = &tag; } + void visit(tag_double& tag) { visited = &tag; } + void visit(tag_byte_array& tag) { visited = &tag; } + void visit(tag_string& tag) { visited = &tag; } + void visit(tag_list& tag) { visited = &tag; } + void visit(tag_compound& tag) { visited = &tag; } + void visit(tag_int_array& tag) { visited = &tag; } } v; - tag_byte().accept(v); - TS_ASSERT_EQUALS(v.visited, tag_type::Byte); - tag_short().accept(v); - TS_ASSERT_EQUALS(v.visited, tag_type::Short); - tag_int().accept(v); - TS_ASSERT_EQUALS(v.visited, tag_type::Int); - tag_long().accept(v); - TS_ASSERT_EQUALS(v.visited, tag_type::Long); - tag_float().accept(v); - TS_ASSERT_EQUALS(v.visited, tag_type::Float); - tag_double().accept(v); - TS_ASSERT_EQUALS(v.visited, tag_type::Double); - tag_byte_array().accept(v); - TS_ASSERT_EQUALS(v.visited, tag_type::Byte_Array); - tag_string().accept(v); - TS_ASSERT_EQUALS(v.visited, tag_type::String); - tag_list().accept(v); - TS_ASSERT_EQUALS(v.visited, tag_type::List); - tag_compound().accept(v); - TS_ASSERT_EQUALS(v.visited, tag_type::Compound); - tag_int_array().accept(v); - TS_ASSERT_EQUALS(v.visited, tag_type::Int_Array); + tag_byte b; b.accept(v); TS_ASSERT_EQUALS(v.visited, &b); + tag_short s; s.accept(v); TS_ASSERT_EQUALS(v.visited, &s); + tag_int i; i.accept(v); TS_ASSERT_EQUALS(v.visited, &i); + tag_long l; l.accept(v); TS_ASSERT_EQUALS(v.visited, &l); + tag_float f; f.accept(v); TS_ASSERT_EQUALS(v.visited, &f); + tag_double d; d.accept(v); TS_ASSERT_EQUALS(v.visited, &d); + tag_byte_array ba; ba.accept(v); TS_ASSERT_EQUALS(v.visited, &ba); + tag_string st; st.accept(v); TS_ASSERT_EQUALS(v.visited, &st); + tag_list ls; ls.accept(v); TS_ASSERT_EQUALS(v.visited, &ls); + tag_compound c; c.accept(v); TS_ASSERT_EQUALS(v.visited, &c); + tag_int_array ia; ia.accept(v); TS_ASSERT_EQUALS(v.visited, &ia); } }; -- cgit 0.0.5-2-1-g0f52 From 93a4f406d0b827026d26703c4623b1b846f65ca4 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 15 Sep 2015 23:37:49 +0200 Subject: Fix base constructor calls of i/ostream Add missing noexcept --- include/io/izlibstream.h | 6 ++---- include/io/ozlibstream.h | 6 ++---- src/io/izlibstream.cpp | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/include/io/izlibstream.h b/include/io/izlibstream.h index 4e9dc9c1ee..6d58d56b86 100644 --- a/include/io/izlibstream.h +++ b/include/io/izlibstream.h @@ -76,10 +76,8 @@ public: * @param bufsize the size of the internal buffers */ explicit izlibstream(std::istream& input, size_t bufsize = 32768): - buf(input, bufsize) - { - init(&buf); - } + std::istream(&buf), buf(input, bufsize) + {} ///@return the wrapped istream std::istream& get_istr() const { return buf.get_istr(); } diff --git a/include/io/ozlibstream.h b/include/io/ozlibstream.h index 6ebd3c0dd0..954bcc182d 100644 --- a/include/io/ozlibstream.h +++ b/include/io/ozlibstream.h @@ -85,10 +85,8 @@ public: * Refer to the zlib documentation of deflateInit2 for a detailed explanation of the arguments. */ explicit ozlibstream(std::ostream& output, int level = -1, int window_bits = 15, int mem_level = 8, int strategy = Z_DEFAULT_STRATEGY): - buf(output, level, window_bits, mem_level, strategy) - { - init(&buf); - } + std::ostream(&buf), buf(output, level, window_bits, mem_level, strategy) + {} ///@return the wrapped ostream std::ostream& get_ostr() const { return buf.get_ostr(); } diff --git a/src/io/izlibstream.cpp b/src/io/izlibstream.cpp index db69e0e626..6b7a75189a 100644 --- a/src/io/izlibstream.cpp +++ b/src/io/izlibstream.cpp @@ -39,7 +39,7 @@ inflate_streambuf::inflate_streambuf(std::istream& input, size_t bufsize, int wi setg(end, end, end); } -inflate_streambuf::~inflate_streambuf() +inflate_streambuf::~inflate_streambuf() noexcept { inflateEnd(&zstr); } -- cgit 0.0.5-2-1-g0f52 From edaaac5a6c7d20eb5baa6de27aa79cb50878a10b Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 15 Sep 2015 23:40:49 +0200 Subject: Moving and copying is disabled for ozlibstream too --- include/io/ozlibstream.h | 9 --------- test/zlibstream_test.h | 1 + 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/include/io/ozlibstream.h b/include/io/ozlibstream.h index 954bcc182d..0e19aaaa2b 100644 --- a/include/io/ozlibstream.h +++ b/include/io/ozlibstream.h @@ -20,7 +20,6 @@ #ifndef OZLIBSTREAM_H_INCLUDED #define OZLIBSTREAM_H_INCLUDED -#include "io/zlib_error.h" #include #include #include @@ -38,14 +37,6 @@ public: explicit deflate_streambuf(std::ostream& output, int level = -1, int window_bits = 15, int mem_level = 8, int strategy = Z_DEFAULT_STRATEGY); ~deflate_streambuf() noexcept; - //Moving - deflate_streambuf(deflate_streambuf&&) noexcept = default; - deflate_streambuf& operator=(deflate_streambuf&&) noexcept = default; - - //No copying - deflate_streambuf(const deflate_streambuf&) = delete; - deflate_streambuf& operator=(const deflate_streambuf&) = delete; - std::ostream& get_ostr() const { return os; } private: diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h index d2e6b6ee82..8e9ca6b0f2 100644 --- a/test/zlibstream_test.h +++ b/test/zlibstream_test.h @@ -20,6 +20,7 @@ #include #include "io/izlibstream.h" #include "io/ozlibstream.h" +#include "io/zlib_error.h" #include #include -- cgit 0.0.5-2-1-g0f52 From 7cb16a28b6bba36e125d680c86e182033aa17666 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 16 Sep 2015 12:42:58 +0200 Subject: Fix CMake not finding zlib --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c16860a8ed..bed0ca668a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ add_library(nbt++ STATIC src/text/json_formatter.cpp) -find_package(zlib REQUIRED) +find_package(ZLIB REQUIRED) find_package(CxxTest) option(NBT_BUILD_TESTS "Build the unit tests. Requires CxxTest." ${CXXTEST_FOUND}) -- cgit 0.0.5-2-1-g0f52 From 837f04f57e32838ee6b2450db5ff2a140de5f818 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 16 Sep 2015 14:20:29 +0200 Subject: Change stringbuf to string for bigtest Enable exceptions for badbit as well --- test/zlibstream_test.h | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h index 8e9ca6b0f2..6af1e433f4 100644 --- a/test/zlibstream_test.h +++ b/test/zlibstream_test.h @@ -29,14 +29,16 @@ using namespace zlib; class zlibstream_test : public CxxTest::TestSuite { private: - std::stringbuf bigtest; + std::string bigtest; public: zlibstream_test() { std::ifstream bigtest_f("bigtest_uncompr", std::ios::binary); - bigtest_f >> &bigtest; - if(!bigtest_f || bigtest.str().size() == 0) + std::stringbuf bigtest_b; + bigtest_f >> &bigtest_b; + bigtest = bigtest_b.str(); + if(!bigtest_f || bigtest.size() == 0) throw std::runtime_error("Could not read bigtest_uncompr file"); } @@ -49,13 +51,13 @@ public: //Small buffer so not all fits at once (the compressed file is 561 bytes) { izlibstream igzs(gzip_in, 256); - igzs.exceptions(std::ios::failbit); + igzs.exceptions(std::ios::failbit | std::ios::badbit); TS_ASSERT(igzs.good()); TS_ASSERT_THROWS_NOTHING(igzs >> &data); TS_ASSERT(igzs); TS_ASSERT(igzs.eof()); - TS_ASSERT_EQUALS(data.str(), bigtest.str()); + TS_ASSERT_EQUALS(data.str(), bigtest); } //Clear and reuse buffers @@ -65,13 +67,13 @@ public: //Now try the same with larger buffer (but not large enough for all output, uncompressed size 1561 bytes) { izlibstream igzs(gzip_in, 1000); - igzs.exceptions(std::ios::failbit); + igzs.exceptions(std::ios::failbit | std::ios::badbit); TS_ASSERT(igzs.good()); TS_ASSERT_THROWS_NOTHING(igzs >> &data); TS_ASSERT(igzs); TS_ASSERT(igzs.eof()); - TS_ASSERT_EQUALS(data.str(), bigtest.str()); + TS_ASSERT_EQUALS(data.str(), bigtest); } data.str(""); @@ -80,13 +82,13 @@ public: //Now with large buffer { izlibstream igzs(gzip_in, 4000); - igzs.exceptions(std::ios::failbit); + igzs.exceptions(std::ios::failbit | std::ios::badbit); TS_ASSERT(igzs.good()); TS_ASSERT_THROWS_NOTHING(igzs >> &data); TS_ASSERT(igzs); TS_ASSERT(igzs.eof()); - TS_ASSERT_EQUALS(data.str(), bigtest.str()); + TS_ASSERT_EQUALS(data.str(), bigtest); } } @@ -97,13 +99,13 @@ public: std::stringbuf data; izlibstream izls(zlib_in, 256); - izls.exceptions(std::ios::failbit); + izls.exceptions(std::ios::failbit | std::ios::badbit); TS_ASSERT(izls.good()); TS_ASSERT_THROWS_NOTHING(izls >> &data); TS_ASSERT(izls); TS_ASSERT(izls.eof()); - TS_ASSERT_EQUALS(data.str(), bigtest.str()); + TS_ASSERT_EQUALS(data.str(), bigtest); } void test_inflate_corrupt() @@ -114,7 +116,7 @@ public: std::stringbuf data; { izlibstream igzs(gzip_in); - igzs.exceptions(std::ios::failbit); + igzs.exceptions(std::ios::failbit | std::ios::badbit); TS_ASSERT_THROWS(igzs >> &data, zlib_error); } @@ -125,7 +127,7 @@ public: data.str(""); { izlibstream igzs(gzip_in); - igzs.exceptions(std::ios::failbit); + igzs.exceptions(std::ios::failbit | std::ios::badbit); TS_ASSERT_THROWS(igzs >> &data, zlib_error); } } -- cgit 0.0.5-2-1-g0f52 From 694855d5f59e8b0a5bc4419b9a409f9ed7609daa Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 16 Sep 2015 14:25:35 +0200 Subject: Change operator>> to read for test_inflate_corrupt operator>>(streambuf*) seems to be weird when it comes to exceptions, and it behaves differently on g++ and clang --- test/zlibstream_test.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h index 6af1e433f4..2a94ab7ccb 100644 --- a/test/zlibstream_test.h +++ b/test/zlibstream_test.h @@ -113,22 +113,24 @@ public: std::ifstream gzip_in("bigtest_corrupt.nbt", std::ios::binary); TS_ASSERT(gzip_in); - std::stringbuf data; + std::vector buf(bigtest.size()); { izlibstream igzs(gzip_in); igzs.exceptions(std::ios::failbit | std::ios::badbit); - TS_ASSERT_THROWS(igzs >> &data, zlib_error); + TS_ASSERT_THROWS(igzs.read(buf.data(), buf.size()), zlib_error); + TS_ASSERT(igzs.bad()); } gzip_in.close(); + gzip_in.clear(); gzip_in.open("bigtest_eof.nbt", std::ios::binary); TS_ASSERT(gzip_in); - data.str(""); { izlibstream igzs(gzip_in); igzs.exceptions(std::ios::failbit | std::ios::badbit); - TS_ASSERT_THROWS(igzs >> &data, zlib_error); + TS_ASSERT_THROWS(igzs.read(buf.data(), buf.size()), zlib_error); + TS_ASSERT(igzs.bad()); } } }; -- cgit 0.0.5-2-1-g0f52 From 264ec99e7bae08710449262e609c20ad76383615 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Wed, 16 Sep 2015 18:41:45 +0200 Subject: Changes to deflate_streambuf and ozlibstream constructors --- include/io/izlibstream.h | 3 ++- include/io/ozlibstream.h | 33 ++++++++++++++++++--------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/include/io/izlibstream.h b/include/io/izlibstream.h index 6d58d56b86..43d2d2c02e 100644 --- a/include/io/izlibstream.h +++ b/include/io/izlibstream.h @@ -38,7 +38,8 @@ 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. + * 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. * diff --git a/include/io/ozlibstream.h b/include/io/ozlibstream.h index 0e19aaaa2b..0be792d482 100644 --- a/include/io/ozlibstream.h +++ b/include/io/ozlibstream.h @@ -29,12 +29,21 @@ namespace zlib /** * @brief Stream buffer used by zlib::ozlibstream - * @see ozlibstream + * @sa ozlibstream */ class deflate_streambuf : public std::streambuf { public: - explicit deflate_streambuf(std::ostream& output, int level = -1, int window_bits = 15, int mem_level = 8, int strategy = Z_DEFAULT_STRATEGY); + /** + * @param output the ostream to wrap + * @param bufsize the size of the internal buffers + * @param level the compression level, ranges from 0 to 9 + * + * 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; std::ostream& get_ostr() const { return os; } @@ -56,28 +65,22 @@ private: * * 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 ozlibstream : public std::ostream { public: /** * @param output the ostream to wrap - * @param level the compression level. Ranges from 0 to 9, or -1 for the default - * @param gzip_header whether to write a gzip header rather than a zlib header + * @param level the compression level, ranges from 0 to 9 + * @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, bool gzip): - ozlibstream(output, level, 15 + (gzip ? 16 : 0)) + explicit ozlibstream(std::ostream& output, int level = Z_DEFAULT_COMPRESSION, bool gzip = false, size_t bufsize = 32768): + std::ostream(&buf), buf(output, level, bufsize, 15 + (gzip ? 16 : 0)) {} - /** - * @param output the ostream to wrap - * @param level the compression level. Ranges from 0 to 9, or -1 for the default - * - * Refer to the zlib documentation of deflateInit2 for a detailed explanation of the arguments. - */ - explicit ozlibstream(std::ostream& output, int level = -1, int window_bits = 15, int mem_level = 8, int strategy = Z_DEFAULT_STRATEGY): - std::ostream(&buf), buf(output, level, window_bits, mem_level, strategy) - {} ///@return the wrapped ostream std::ostream& get_ostr() const { return buf.get_ostr(); } -- cgit 0.0.5-2-1-g0f52 From 84d2896da1c3f3ef0be80476585422d8b1230289 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 20 Sep 2015 19:15:27 +0200 Subject: Implement ozlibstream --- CMakeLists.txt | 1 + include/io/ozlibstream.h | 2 +- src/io/ozlibstream.cpp | 87 ++++++++++++++++++++++++++++++++++++++++++++++++ test/zlibstream_test.h | 20 +++++++++++ 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 src/io/ozlibstream.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bed0ca668a..39a3a5759c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,7 @@ add_library(nbt++ STATIC src/value_initializer.cpp src/io/izlibstream.cpp + src/io/ozlibstream.cpp src/io/stream_reader.cpp src/io/stream_writer.cpp diff --git a/include/io/ozlibstream.h b/include/io/ozlibstream.h index 0be792d482..d4ea8c2840 100644 --- a/include/io/ozlibstream.h +++ b/include/io/ozlibstream.h @@ -78,7 +78,7 @@ public: * @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, level, bufsize, 15 + (gzip ? 16 : 0)) + std::ostream(&buf), buf(output, bufsize, level, 15 + (gzip ? 16 : 0)) {} ///@return the wrapped ostream diff --git a/src/io/ozlibstream.cpp b/src/io/ozlibstream.cpp new file mode 100644 index 0000000000..97702a37e1 --- /dev/null +++ b/src/io/ozlibstream.cpp @@ -0,0 +1,87 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see . + */ +#include "io/ozlibstream.h" +#include "io/zlib_error.h" + +namespace zlib +{ + +deflate_streambuf::deflate_streambuf(std::ostream& output, size_t bufsize, int level, int window_bits, int mem_level, int strategy): + os(output), in(bufsize), out(bufsize) +{ + zstr.zalloc = Z_NULL; + zstr.zfree = Z_NULL; + zstr.opaque = Z_NULL; + int ret = deflateInit2(&zstr, level, Z_DEFLATED, window_bits, mem_level, strategy); + if(ret != Z_OK) + throw zlib_error(zstr.msg, ret); + + setp(in.data(), in.data() + in.size()); +} + +deflate_streambuf::~deflate_streambuf() noexcept +{ + try + { + deflate_chunk(Z_FINISH); + } + catch(...) + { + //ignore as we can't do anything about it + } + deflateEnd(&zstr); +} + +void deflate_streambuf::deflate_chunk(int flush) +{ + zstr.next_in = reinterpret_cast(pbase()); + zstr.avail_in = pptr() - pbase(); + do + { + zstr.next_out = reinterpret_cast(out.data()); + zstr.avail_out = out.size(); + int ret = deflate(&zstr, flush); + if(ret != Z_OK && ret != Z_STREAM_END) + throw zlib_error(zstr.msg, ret); + int have = out.size() - zstr.avail_out; + if(!os.write(out.data(), have)) + throw std::ios_base::failure("Could not write to the output stream"); + } while(zstr.avail_out == 0); + setp(in.data(), in.data() + in.size()); +} + +deflate_streambuf::int_type deflate_streambuf::overflow(int_type ch) +{ + deflate_chunk(); + if(ch != traits_type::eof()) + { + *pptr() = ch; + pbump(1); + } + return ch; +} + +int deflate_streambuf::sync() +{ + deflate_chunk(); + return 0; +} + +} diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h index 2a94ab7ccb..e0b22c1356 100644 --- a/test/zlibstream_test.h +++ b/test/zlibstream_test.h @@ -133,4 +133,24 @@ public: TS_ASSERT(igzs.bad()); } } + + void test_deflate_zlib() + { + //Here we assume that inflating works and has already been tested + std::stringstream str; + std::stringbuf output; + //Small buffer + { + ozlibstream ozls(str, -1, false, 256); + ozls.exceptions(std::ios::failbit | std::ios::badbit); + ozls << bigtest; + TS_ASSERT(ozls.good()); + } + { + izlibstream izls(str); + izls >> &output; + TS_ASSERT(izls); + } + TS_ASSERT_EQUALS(output.str(), bigtest); + } }; -- cgit 0.0.5-2-1-g0f52 From 70309e97831b2e3be48eee83356e5e9241b317b0 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 20 Sep 2015 19:31:14 +0200 Subject: Add more tests for deflating --- test/zlibstream_test.h | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h index e0b22c1356..754f4e5d58 100644 --- a/test/zlibstream_test.h +++ b/test/zlibstream_test.h @@ -146,6 +146,64 @@ public: ozls << bigtest; TS_ASSERT(ozls.good()); } + TS_ASSERT(str.good()); + { + izlibstream izls(str); + TS_ASSERT_THROWS_NOTHING(izls >> &output); + TS_ASSERT(izls); + } + TS_ASSERT_EQUALS(output.str(), bigtest); + + str.clear(); str.str(""); + output.str(""); + //Medium sized buffer + //Write first half, then flush and write second half + { + ozlibstream ozls(str, 9, false, 512); + ozls.exceptions(std::ios::failbit | std::ios::badbit); + + std::string half1 = bigtest.substr(0, bigtest.size()/2); + std::string half2 = bigtest.substr(bigtest.size()/2); + TS_ASSERT_THROWS_NOTHING(ozls << half1 << std::flush << half2); + TS_ASSERT(ozls.good()); + } + TS_ASSERT(str.good()); + { + izlibstream izls(str); + izls >> &output; + TS_ASSERT(izls); + } + TS_ASSERT_EQUALS(output.str(), bigtest); + + str.clear(); str.str(""); + output.str(""); + //Large buffer + { + ozlibstream ozls(str, 1, false, 4000); + ozls.exceptions(std::ios::failbit | std::ios::badbit); + TS_ASSERT_THROWS_NOTHING(ozls << bigtest); + TS_ASSERT(ozls.good()); + } + TS_ASSERT(str.good()); + { + izlibstream izls(str); + izls >> &output; + TS_ASSERT(izls); + } + TS_ASSERT_EQUALS(output.str(), bigtest); + } + + void test_deflate_gzip() + { + std::stringstream str; + std::stringbuf output; + { + ozlibstream ozls(str, -1, true); + ozls.exceptions(std::ios::failbit | std::ios::badbit); + TS_ASSERT_THROWS_NOTHING(ozls << bigtest); + TS_ASSERT(ozls.good()); + } + TS_ASSERT(str.good()); { izlibstream izls(str); izls >> &output; -- cgit 0.0.5-2-1-g0f52 From 8de25170806ebc2e72d037f9bd81c193c9f13138 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 20 Sep 2015 20:08:22 +0200 Subject: Fix indentation in comments --- include/io/stream_reader.h | 24 ++++++++++++------------ include/io/stream_writer.h | 15 +++++++-------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/include/io/stream_reader.h b/include/io/stream_reader.h index 877f6498ed..ba9708fe1c 100644 --- a/include/io/stream_reader.h +++ b/include/io/stream_reader.h @@ -40,21 +40,21 @@ class input_error : public std::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 -*/ + * @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 + */ std::pair> 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 -*/ + * @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 + */ std::pair> read_tag(std::istream& is, endian::endian e = endian::big); /** diff --git a/include/io/stream_writer.h b/include/io/stream_writer.h index 959202e1ad..b9c478b933 100644 --- a/include/io/stream_writer.h +++ b/include/io/stream_writer.h @@ -37,14 +37,13 @@ class output_error : public std::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 -*/ -void write_tag(const std::string& key, const tag& t, std::ostream& os, endian::endian e = endian::big); + * @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 + */ /** * @brief Helper class for writing NBT tags to output streams -- cgit 0.0.5-2-1-g0f52 From 07a9300246630705d5f4e6978cdc5cf8a8b4afd7 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Sun, 20 Sep 2015 20:13:59 +0200 Subject: Fix derp... --- include/io/stream_writer.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/io/stream_writer.h b/include/io/stream_writer.h index b9c478b933..cd3317849b 100644 --- a/include/io/stream_writer.h +++ b/include/io/stream_writer.h @@ -44,6 +44,7 @@ class output_error : public std::runtime_error * @param e the byte order of the written data. The Java edition * of Minecraft uses Big Endian, the Pocket edition uses Little Endian */ +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 -- cgit 0.0.5-2-1-g0f52 From 64e6d2abc719869e386caa760bfbe1e65bd7559b Mon Sep 17 00:00:00 2001 From: ljfa Date: Mon, 21 Sep 2015 08:30:25 +0200 Subject: Create ozlibstream::close method Set failbit on output stream on failure --- include/io/ozlibstream.h | 4 ++++ src/io/ozlibstream.cpp | 10 +++++++++- test/zlibstream_test.h | 7 ++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/include/io/ozlibstream.h b/include/io/ozlibstream.h index d4ea8c2840..770db0029d 100644 --- a/include/io/ozlibstream.h +++ b/include/io/ozlibstream.h @@ -48,6 +48,8 @@ public: std::ostream& get_ostr() const { return os; } + ///Finishes compression and writes all pending data to the output + void close(); private: std::ostream& os; std::vector in; @@ -84,6 +86,8 @@ public: ///@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() { buf.close(); } private: deflate_streambuf buf; }; diff --git a/src/io/ozlibstream.cpp b/src/io/ozlibstream.cpp index 97702a37e1..605d1397d0 100644 --- a/src/io/ozlibstream.cpp +++ b/src/io/ozlibstream.cpp @@ -40,7 +40,7 @@ deflate_streambuf::~deflate_streambuf() noexcept { try { - deflate_chunk(Z_FINISH); + close(); } catch(...) { @@ -49,6 +49,11 @@ deflate_streambuf::~deflate_streambuf() noexcept deflateEnd(&zstr); } +void deflate_streambuf::close() +{ + deflate_chunk(Z_FINISH); +} + void deflate_streambuf::deflate_chunk(int flush) { zstr.next_in = reinterpret_cast(pbase()); @@ -59,7 +64,10 @@ void deflate_streambuf::deflate_chunk(int flush) zstr.avail_out = out.size(); int ret = deflate(&zstr, flush); if(ret != Z_OK && ret != Z_STREAM_END) + { + os.setstate(std::ios_base::failbit); throw zlib_error(zstr.msg, ret); + } int have = out.size() - zstr.avail_out; if(!os.write(out.data(), have)) throw std::ios_base::failure("Could not write to the output stream"); diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h index 754f4e5d58..a8357e2692 100644 --- a/test/zlibstream_test.h +++ b/test/zlibstream_test.h @@ -143,8 +143,9 @@ public: { ozlibstream ozls(str, -1, false, 256); ozls.exceptions(std::ios::failbit | std::ios::badbit); - ozls << bigtest; + TS_ASSERT_THROWS_NOTHING(ozls << bigtest); TS_ASSERT(ozls.good()); + TS_ASSERT_THROWS_NOTHING(ozls.close()); } TS_ASSERT(str.good()); { @@ -166,6 +167,7 @@ public: std::string half2 = bigtest.substr(bigtest.size()/2); TS_ASSERT_THROWS_NOTHING(ozls << half1 << std::flush << half2); TS_ASSERT(ozls.good()); + TS_ASSERT_THROWS_NOTHING(ozls.close()); } TS_ASSERT(str.good()); { @@ -183,6 +185,8 @@ public: ozls.exceptions(std::ios::failbit | std::ios::badbit); TS_ASSERT_THROWS_NOTHING(ozls << bigtest); TS_ASSERT(ozls.good()); + TS_ASSERT_THROWS_NOTHING(ozls.close()); + TS_ASSERT_THROWS_NOTHING(ozls.close()); //closing twice shouldn't be a problem } TS_ASSERT(str.good()); { @@ -202,6 +206,7 @@ public: ozls.exceptions(std::ios::failbit | std::ios::badbit); TS_ASSERT_THROWS_NOTHING(ozls << bigtest); TS_ASSERT(ozls.good()); + TS_ASSERT_THROWS_NOTHING(ozls.close()); } TS_ASSERT(str.good()); { -- cgit 0.0.5-2-1-g0f52 From c7ca7fc947527a831ab21a6449a7b5f71bf1e382 Mon Sep 17 00:00:00 2001 From: ljfa Date: Mon, 21 Sep 2015 08:47:25 +0200 Subject: More assertions --- test/zlibstream_test.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h index a8357e2692..9ce411b6c1 100644 --- a/test/zlibstream_test.h +++ b/test/zlibstream_test.h @@ -146,6 +146,7 @@ public: TS_ASSERT_THROWS_NOTHING(ozls << bigtest); TS_ASSERT(ozls.good()); TS_ASSERT_THROWS_NOTHING(ozls.close()); + TS_ASSERT(ozls.good()); } TS_ASSERT(str.good()); { @@ -168,6 +169,7 @@ public: TS_ASSERT_THROWS_NOTHING(ozls << half1 << std::flush << half2); TS_ASSERT(ozls.good()); TS_ASSERT_THROWS_NOTHING(ozls.close()); + TS_ASSERT(ozls.good()); } TS_ASSERT(str.good()); { @@ -187,6 +189,7 @@ public: TS_ASSERT(ozls.good()); TS_ASSERT_THROWS_NOTHING(ozls.close()); TS_ASSERT_THROWS_NOTHING(ozls.close()); //closing twice shouldn't be a problem + TS_ASSERT(ozls.good()); } TS_ASSERT(str.good()); { @@ -207,6 +210,7 @@ public: TS_ASSERT_THROWS_NOTHING(ozls << bigtest); TS_ASSERT(ozls.good()); TS_ASSERT_THROWS_NOTHING(ozls.close()); + TS_ASSERT(ozls.good()); } TS_ASSERT(str.good()); { -- cgit 0.0.5-2-1-g0f52 From 28d735a3261e831f0c65cfe035e0aa53e369b964 Mon Sep 17 00:00:00 2001 From: ljfa Date: Mon, 21 Sep 2015 08:56:39 +0200 Subject: Add test for writing to a closed ozlibstream --- test/zlibstream_test.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h index 9ce411b6c1..204764e370 100644 --- a/test/zlibstream_test.h +++ b/test/zlibstream_test.h @@ -220,4 +220,16 @@ public: } TS_ASSERT_EQUALS(output.str(), bigtest); } + + void test_deflate_closed() + { + std::stringstream str; + ozlibstream ozls(str); + ozls.exceptions(std::ios::failbit | std::ios::badbit); + TS_ASSERT_THROWS_NOTHING(ozls << bigtest); + TS_ASSERT_THROWS_NOTHING(ozls.close()); + TS_ASSERT_THROWS(ozls << "foo" << std::flush, zlib_error); + TS_ASSERT(ozls.bad()); + TS_ASSERT(!str); + } }; -- cgit 0.0.5-2-1-g0f52 From f76d4d228b32e72c7de250cb37af264d38ee7e79 Mon Sep 17 00:00:00 2001 From: ljfa Date: Mon, 21 Sep 2015 10:21:17 +0200 Subject: Make close honor the exceptions mask --- include/io/ozlibstream.h | 2 +- src/io/ozlibstream.cpp | 14 ++++++++++++++ test/zlibstream_test.h | 30 +++++++++++++++++++++++------- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/include/io/ozlibstream.h b/include/io/ozlibstream.h index 770db0029d..bd9faea381 100644 --- a/include/io/ozlibstream.h +++ b/include/io/ozlibstream.h @@ -87,7 +87,7 @@ public: std::ostream& get_ostr() const { return buf.get_ostr(); } ///Finishes compression and writes all pending data to the output - void close() { buf.close(); } + void close(); private: deflate_streambuf buf; }; diff --git a/src/io/ozlibstream.cpp b/src/io/ozlibstream.cpp index 605d1397d0..36c6b42d61 100644 --- a/src/io/ozlibstream.cpp +++ b/src/io/ozlibstream.cpp @@ -92,4 +92,18 @@ int deflate_streambuf::sync() return 0; } +void ozlibstream::close() +{ + try + { + buf.close(); + } + catch(...) + { + setstate(badbit); //FIXME: This will throw the wrong type of exception + //but there's no good way of setting the badbit + //without causing an exception when exceptions is set + } +} + } diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h index 204764e370..5f2ad64c55 100644 --- a/test/zlibstream_test.h +++ b/test/zlibstream_test.h @@ -224,12 +224,28 @@ public: void test_deflate_closed() { std::stringstream str; - ozlibstream ozls(str); - ozls.exceptions(std::ios::failbit | std::ios::badbit); - TS_ASSERT_THROWS_NOTHING(ozls << bigtest); - TS_ASSERT_THROWS_NOTHING(ozls.close()); - TS_ASSERT_THROWS(ozls << "foo" << std::flush, zlib_error); - TS_ASSERT(ozls.bad()); - TS_ASSERT(!str); + { + ozlibstream ozls(str); + ozls.exceptions(std::ios::failbit | std::ios::badbit); + TS_ASSERT_THROWS_NOTHING(ozls << bigtest); + TS_ASSERT_THROWS_NOTHING(ozls.close()); + TS_ASSERT_THROWS_NOTHING(ozls << "foo"); + TS_ASSERT_THROWS_ANYTHING(ozls.close()); + TS_ASSERT(ozls.bad()); + TS_ASSERT(!str); + } + str.clear(); + str.seekp(0); + { + ozlibstream ozls(str); + //this time without exceptions + TS_ASSERT_THROWS_NOTHING(ozls << bigtest); + TS_ASSERT_THROWS_NOTHING(ozls.close()); + TS_ASSERT_THROWS_NOTHING(ozls << "foo" << std::flush); + TS_ASSERT(ozls.bad()); + TS_ASSERT_THROWS_NOTHING(ozls.close()); + TS_ASSERT(ozls.bad()); + TS_ASSERT(!str); + } } }; -- cgit 0.0.5-2-1-g0f52 From 76dcc4a9d1c44f7078780dea2756dff3a75d234f Mon Sep 17 00:00:00 2001 From: ljfa Date: Mon, 21 Sep 2015 13:26:04 +0200 Subject: Create zlib_streambuf base class --- include/io/izlibstream.h | 7 ++----- include/io/ozlibstream.h | 8 +++----- include/io/zlib_error.h | 21 --------------------- include/io/zlib_streambuf.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/io/izlibstream.cpp | 7 ++----- src/io/ozlibstream.cpp | 7 ++----- test/zlibstream_test.h | 1 - 7 files changed, 54 insertions(+), 42 deletions(-) delete mode 100644 include/io/zlib_error.h create mode 100644 include/io/zlib_streambuf.h diff --git a/include/io/izlibstream.h b/include/io/izlibstream.h index 43d2d2c02e..d3c4faa14e 100644 --- a/include/io/izlibstream.h +++ b/include/io/izlibstream.h @@ -20,8 +20,8 @@ #ifndef IZLIBSTREAM_H_INCLUDED #define IZLIBSTREAM_H_INCLUDED +#include "io/zlib_streambuf.h" #include -#include #include namespace zlib @@ -31,7 +31,7 @@ namespace zlib * @brief Stream buffer used by zlib::izlibstream * @sa izlibstream */ -class inflate_streambuf : public std::streambuf +class inflate_streambuf : public zlib_streambuf { public: /** @@ -53,9 +53,6 @@ public: private: std::istream& is; - std::vector in; - std::vector out; - z_stream zstr; bool stream_end; int_type underflow() override; diff --git a/include/io/ozlibstream.h b/include/io/ozlibstream.h index bd9faea381..ef93944c94 100644 --- a/include/io/ozlibstream.h +++ b/include/io/ozlibstream.h @@ -20,8 +20,8 @@ #ifndef OZLIBSTREAM_H_INCLUDED #define OZLIBSTREAM_H_INCLUDED +#include "io/zlib_streambuf.h" #include -#include #include namespace zlib @@ -31,7 +31,7 @@ namespace zlib * @brief Stream buffer used by zlib::ozlibstream * @sa ozlibstream */ -class deflate_streambuf : public std::streambuf +class deflate_streambuf : public zlib_streambuf { public: /** @@ -46,15 +46,13 @@ public: 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; } ///Finishes compression and writes all pending data to the output void close(); private: std::ostream& os; - std::vector in; - std::vector out; - z_stream zstr; void deflate_chunk(int flush = Z_NO_FLUSH); diff --git a/include/io/zlib_error.h b/include/io/zlib_error.h deleted file mode 100644 index 1ecb50e553..0000000000 --- a/include/io/zlib_error.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef ZLIB_ERROR_H_INCLUDED -#define ZLIB_ERROR_H_INCLUDED - -#include -#include - -///Exception thrown in case zlib encounters a problem -class zlib_error : public std::runtime_error -{ -public: - const int errcode; - - explicit zlib_error(const char* msg, int errcode): - std::runtime_error(msg - ? std::string(zError(errcode)) + ": " + msg - : zError(errcode)), - errcode(errcode) - {} -}; - -#endif // ZLIB_ERROR_H_INCLUDED diff --git a/include/io/zlib_streambuf.h b/include/io/zlib_streambuf.h new file mode 100644 index 0000000000..36062192b7 --- /dev/null +++ b/include/io/zlib_streambuf.h @@ -0,0 +1,45 @@ +#ifndef ZLIB_STREAMBUF_H_INCLUDED +#define ZLIB_STREAMBUF_H_INCLUDED + +#include +#include +#include +#include + +namespace zlib +{ + +///Exception thrown in case zlib encounters a problem +class zlib_error : public std::runtime_error +{ +public: + const int errcode; + + explicit 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 in; + std::vector 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; + } +}; + +} + +#endif // ZLIB_STREAMBUF_H_INCLUDED diff --git a/src/io/izlibstream.cpp b/src/io/izlibstream.cpp index 6b7a75189a..85e04a189a 100644 --- a/src/io/izlibstream.cpp +++ b/src/io/izlibstream.cpp @@ -18,17 +18,14 @@ * along with libnbt++. If not, see . */ #include "io/izlibstream.h" -#include "io/zlib_error.h" +#include "io/zlib_streambuf.h" namespace zlib { inflate_streambuf::inflate_streambuf(std::istream& input, size_t bufsize, int window_bits): - is(input), in(bufsize), out(bufsize), stream_end(false) + zlib_streambuf(bufsize), is(input), stream_end(false) { - zstr.zalloc = Z_NULL; - zstr.zfree = Z_NULL; - zstr.opaque = Z_NULL; zstr.next_in = Z_NULL; zstr.avail_in = 0; int ret = inflateInit2(&zstr, window_bits); diff --git a/src/io/ozlibstream.cpp b/src/io/ozlibstream.cpp index 36c6b42d61..73f1057c67 100644 --- a/src/io/ozlibstream.cpp +++ b/src/io/ozlibstream.cpp @@ -18,17 +18,14 @@ * along with libnbt++. If not, see . */ #include "io/ozlibstream.h" -#include "io/zlib_error.h" +#include "io/zlib_streambuf.h" namespace zlib { deflate_streambuf::deflate_streambuf(std::ostream& output, size_t bufsize, int level, int window_bits, int mem_level, int strategy): - os(output), in(bufsize), out(bufsize) + zlib_streambuf(bufsize), os(output) { - zstr.zalloc = Z_NULL; - zstr.zfree = Z_NULL; - zstr.opaque = Z_NULL; int ret = deflateInit2(&zstr, level, Z_DEFLATED, window_bits, mem_level, strategy); if(ret != Z_OK) throw zlib_error(zstr.msg, ret); diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h index 5f2ad64c55..f8308cf7c5 100644 --- a/test/zlibstream_test.h +++ b/test/zlibstream_test.h @@ -20,7 +20,6 @@ #include #include "io/izlibstream.h" #include "io/ozlibstream.h" -#include "io/zlib_error.h" #include #include -- cgit 0.0.5-2-1-g0f52 From dea9b34634c557823baa648a121d16514d3e8a36 Mon Sep 17 00:00:00 2001 From: ljfa Date: Mon, 21 Sep 2015 13:39:28 +0200 Subject: Remove redundant explicit --- include/io/zlib_streambuf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/io/zlib_streambuf.h b/include/io/zlib_streambuf.h index 36062192b7..e7bb780902 100644 --- a/include/io/zlib_streambuf.h +++ b/include/io/zlib_streambuf.h @@ -15,7 +15,7 @@ class zlib_error : public std::runtime_error public: const int errcode; - explicit zlib_error(const char* msg, int errcode): + zlib_error(const char* msg, int errcode): std::runtime_error(msg ? std::string(zError(errcode)) + ": " + msg : zError(errcode)), -- cgit 0.0.5-2-1-g0f52 From 083939ed646b069632f6c3d645fcd8074d3a9933 Mon Sep 17 00:00:00 2001 From: ljfa Date: Mon, 21 Sep 2015 14:48:21 +0200 Subject: Add tests for reading and writing gzipped tags --- test/CMakeLists.txt | 4 ++-- test/read_test.h | 13 +++++++++++++ test/write_test.h | 16 ++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bd4ac0295d..418f6b4b14 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -16,11 +16,11 @@ CXXTEST_ADD_TEST(endian_str_test endian_str_test.cpp ${CMAKE_CURRENT_SOURCE_DIR} target_link_libraries(endian_str_test nbt++) CXXTEST_ADD_TEST(read_test read_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/read_test.h) -target_link_libraries(read_test nbt++) +target_link_libraries(read_test nbt++ z) use_testfiles(read_test) CXXTEST_ADD_TEST(write_test write_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/write_test.h) -target_link_libraries(write_test nbt++) +target_link_libraries(write_test nbt++ z) use_testfiles(write_test) CXXTEST_ADD_TEST(zlibstream_test zlibstream_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/zlibstream_test.h) diff --git a/test/read_test.h b/test/read_test.h index 891c2933d2..eab30cfaad 100644 --- a/test/read_test.h +++ b/test/read_test.h @@ -19,6 +19,7 @@ */ #include #include "io/stream_reader.h" +#include "io/izlibstream.h" #include "nbt_tags.h" #include #include @@ -213,4 +214,16 @@ public: "Even though unprovided for by NBT, the library should also handle " "the case where the file consists of something else than tag_compound")); } + + void test_read_gzip() + { + std::ifstream file("bigtest.nbt", std::ios::binary); + zlib::izlibstream igzs(file); + TS_ASSERT(file && igzs); + + auto pair = nbt::io::read_compound(igzs); + TS_ASSERT(igzs); + TS_ASSERT_EQUALS(pair.first, "Level"); + verify_bigtest_structure(*pair.second); + } }; diff --git a/test/write_test.h b/test/write_test.h index 424e6344a2..69c5dfdb97 100644 --- a/test/write_test.h +++ b/test/write_test.h @@ -20,6 +20,8 @@ #include #include "io/stream_writer.h" #include "io/stream_reader.h" +#include "io/ozlibstream.h" +#include "io/izlibstream.h" #include "nbt_tags.h" #include #include @@ -244,5 +246,19 @@ public: written_pair = io::read_compound(sstr, endian::little); TS_ASSERT_EQUALS(orig_pair.first, written_pair.first); TS_ASSERT(*orig_pair.second == *written_pair.second); + + //Now with gzip compression + sstr.str(""); + zlib::ozlibstream ogzs(sstr, -1, true); + io::write_tag(orig_pair.first, *orig_pair.second, ogzs); + ogzs.close(); + TS_ASSERT(ogzs); + TS_ASSERT(sstr); + //Read and compare + zlib::izlibstream igzs(sstr); + written_pair = io::read_compound(igzs); + TS_ASSERT(igzs); + TS_ASSERT_EQUALS(orig_pair.first, written_pair.first); + TS_ASSERT(*orig_pair.second == *written_pair.second); } }; -- cgit 0.0.5-2-1-g0f52 From 57136429177ed86e2152154d4ecc229d3afe6d94 Mon Sep 17 00:00:00 2001 From: ljfa Date: Mon, 21 Sep 2015 15:03:36 +0200 Subject: Add hint for default compression level --- include/io/ozlibstream.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/io/ozlibstream.h b/include/io/ozlibstream.h index ef93944c94..78dd2c1684 100644 --- a/include/io/ozlibstream.h +++ b/include/io/ozlibstream.h @@ -37,7 +37,7 @@ 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 + * @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. * @@ -73,7 +73,7 @@ class ozlibstream : public std::ostream public: /** * @param output the ostream to wrap - * @param level the compression level, ranges from 0 to 9 + * @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 */ -- cgit 0.0.5-2-1-g0f52 From 8773152a57a4e1524be66c7d6777c48de246040f Mon Sep 17 00:00:00 2001 From: ljfa Date: Mon, 21 Sep 2015 18:10:03 +0200 Subject: Make the inflate_streambuf rewind the input --- include/io/izlibstream.h | 5 +++++ src/io/izlibstream.cpp | 8 +++++++- test/testfiles/trailing_data.zlib | Bin 0 -> 20 bytes test/zlibstream_test.h | 21 +++++++++++++++++++++ 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 test/testfiles/trailing_data.zlib diff --git a/include/io/izlibstream.h b/include/io/izlibstream.h index d3c4faa14e..4c6d5def85 100644 --- a/include/io/izlibstream.h +++ b/include/io/izlibstream.h @@ -64,6 +64,11 @@ private: * 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 izlibstream : public std::istream diff --git a/src/io/izlibstream.cpp b/src/io/izlibstream.cpp index 85e04a189a..0a7512469e 100644 --- a/src/io/izlibstream.cpp +++ b/src/io/izlibstream.cpp @@ -78,7 +78,13 @@ inflate_streambuf::int_type inflate_streambuf::underflow() throw std::bad_alloc(); case Z_STREAM_END: - stream_end = true; + if(!stream_end) + { + stream_end = true; + //In case we consumed too much, we have to rewind the input stream + is.clear(); + is.seekg(-static_cast(zstr.avail_in), std::ios_base::cur); + } if(have == 0) return traits_type::eof(); break; diff --git a/test/testfiles/trailing_data.zlib b/test/testfiles/trailing_data.zlib new file mode 100644 index 0000000000..83848f3024 Binary files /dev/null and b/test/testfiles/trailing_data.zlib differ diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h index f8308cf7c5..e76745f80f 100644 --- a/test/zlibstream_test.h +++ b/test/zlibstream_test.h @@ -133,6 +133,27 @@ public: } } + void test_inflate_trailing() + { + //This file contains additional uncompressed data after the zlib-compressed data + std::ifstream file("trailing_data.zlib", std::ios::binary); + izlibstream izls(file, 32); + TS_ASSERT(file && izls); + + std::string str; + izls >> str; + TS_ASSERT(izls); + TS_ASSERT(izls.eof()); + TS_ASSERT_EQUALS(str, "foobar"); + + //Now read the uncompressed data + TS_ASSERT(file); + TS_ASSERT(!file.eof()); + file >> str; + TS_ASSERT(!file.bad()); + TS_ASSERT_EQUALS(str, "barbaz"); + } + void test_deflate_zlib() { //Here we assume that inflating works and has already been tested -- cgit 0.0.5-2-1-g0f52 From 5cc13f6a358b7d4e7b5141ca6ee4eba586bea851 Mon Sep 17 00:00:00 2001 From: ljfa Date: Mon, 21 Sep 2015 19:53:48 +0200 Subject: Version bump Remove PRD.md --- CMakeLists.txt | 2 +- PRD.md | 60 ---------------------------------------------------------- 2 files changed, 1 insertion(+), 61 deletions(-) delete mode 100644 PRD.md diff --git a/CMakeLists.txt b/CMakeLists.txt index 39a3a5759c..30441d77d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.0) project(libnbt++ - VERSION 2.1) + VERSION 2.3) add_definitions(-std=c++11) include_directories(include) diff --git a/PRD.md b/PRD.md deleted file mode 100644 index f7b46f1a8a..0000000000 --- a/PRD.md +++ /dev/null @@ -1,60 +0,0 @@ -# libnbt++2 Product Requirements Document - -### Purpose -Provide a C++ interface for working with NBT data, particularly originating -from Minecraft. - -### Scope -External Minecraft utilities that read or manipulate parts of savefiles, -such as: -- (Graphical) NBT editors -- Inventory editors -- Tools for reading or changing world metadata -- Map editors and visualizers - -### Definitions, Acronyms and Abbreviations -- **libnbt++1:** The predecessor of libnbt++2. -- **Minecraft:** A sandbox voxel world game written in Java, developed by - Mojang. -- **Mojang's implementation:** The NBT library written in Java that Mojang - uses in Minecraft. -- **NBT:** Named Binary Tag. A binary serialization format used by Minecraft. -- **Tag:** A data unit in NBT. Can be a number, string, array, list or - compound. - -### Product Functions -- /RF10/ Reading and writing NBT data files/streams with or without - compression. -- /RF20/ Representing NBT data in memory and allowing programs to read or - manipulate it in all the ways that Mojang's implementation and libnbt++1 - provide. -- /RF30/ A shorter syntax than in libnbt++1 and preferrably also Mojang's - implementation. -- /RF35/ Typesafe operations (no possibly unwanted implicit casts), in case - of incompatible types exceptions should be thrown. -- /RF40/ The need for insecure operations and manual memory management should - be minimized; references and `std::unique_ptr` should be preferred before - raw pointers. -- /RF55/ A wrapper for tags that provides syntactic sugar is preferred - before raw `std::unique_ptr` values. -- /RF50/ Move semantics are preferred before copy semantics. -- /RF55/ Copying tags should be possible, but only in an explicit manner. -- /RF60/ Checked conversions are preferred, unchecked conversions may be - possible but discouraged. - -### Product Performance -- /RP10/ All operations on (not too large) NBT data should not be slower - than their counterparts in Mojang's implementation. -- /RP20/ The library must be able to handle all possible NBT data that - Mojang's implementation can create and handle. -- /RP30/ Often used operations on large Lists, Compounds and Arrays must - be of at most O(log n) time complexity if reasonable. Other operations - should be at most O(n). - -### Quality Requirements -- Functionality: good -- Reliability: normal -- Usability: very good -- Efficiency: good -- Changeability: normal -- Transferability: normal -- cgit 0.0.5-2-1-g0f52 From fe4d1dca6a44e9a079806dc27b7ddb0a558ee36a Mon Sep 17 00:00:00 2001 From: ljfa Date: Mon, 21 Sep 2015 20:04:10 +0200 Subject: Maaaaaybe the WIP can be removed now --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 477ab72e15..6ec6b57e03 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# libnbt++ 2 (WIP) +# libnbt++ 2 libnbt++ is a free C++ library for Minecraft's file format Named Binary Tag (NBT). It can read and write compressed and uncompressed NBT files and -- cgit 0.0.5-2-1-g0f52 From c178c3055257d1e0ff63229568eb507486d8ad36 Mon Sep 17 00:00:00 2001 From: ljfa Date: Tue, 22 Sep 2015 11:12:25 +0200 Subject: Add option for building shared library (#7) No export headers yet though --- CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 30441d77d0..6a5c651d78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,8 @@ project(libnbt++ add_definitions(-std=c++11) include_directories(include) -add_library(nbt++ STATIC + +set(NBT_SOURCES src/endian_str.cpp src/tag.cpp src/tag_array.cpp @@ -21,9 +22,13 @@ add_library(nbt++ STATIC src/text/json_formatter.cpp) +add_library(nbt++ ${NBT_SOURCES}) +target_link_libraries(nbt++ z) + find_package(ZLIB REQUIRED) find_package(CxxTest) +option(BUILD_SHARED_LIBS "Build shared libraries" OFF) option(NBT_BUILD_TESTS "Build the unit tests. Requires CxxTest." ${CXXTEST_FOUND}) if(NBT_BUILD_TESTS) -- cgit 0.0.5-2-1-g0f52 From 9c63256baa6562c5e50431b555e1c3c8e867bdb7 Mon Sep 17 00:00:00 2001 From: ljfa Date: Tue, 22 Sep 2015 13:36:17 +0200 Subject: Add exports for shared libraries. Closes #7 --- CMakeLists.txt | 7 +++- include/endian_str.h | 81 ++++++++++++++++++++++--------------------- include/io/izlibstream.h | 4 +-- include/io/ozlibstream.h | 4 +-- include/io/stream_reader.h | 8 ++--- include/io/stream_writer.h | 4 +-- include/io/zlib_streambuf.h | 3 +- include/tag.h | 13 +++---- include/tag_compound.h | 2 +- include/tag_list.h | 6 ++-- include/tag_string.h | 2 +- include/text/json_formatter.h | 3 +- include/value.h | 6 ++-- include/value_initializer.h | 2 +- src/tag_array.cpp | 4 +-- 15 files changed, 79 insertions(+), 70 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a5c651d78..4ebd2d0c6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,12 @@ cmake_minimum_required(VERSION 3.0) project(libnbt++ VERSION 2.3) +include(GenerateExportHeader) +set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) + add_definitions(-std=c++11) -include_directories(include) +include_directories(include ${CMAKE_CURRENT_BINARY_DIR}) set(NBT_SOURCES src/endian_str.cpp @@ -24,6 +28,7 @@ set(NBT_SOURCES add_library(nbt++ ${NBT_SOURCES}) target_link_libraries(nbt++ z) +generate_export_header(nbt++ BASE_NAME nbt) find_package(ZLIB REQUIRED) find_package(CxxTest) diff --git a/include/endian_str.h b/include/endian_str.h index b1549bc27c..ca36835fd0 100644 --- a/include/endian_str.h +++ b/include/endian_str.h @@ -22,6 +22,7 @@ #include #include +#include "nbt_export.h" /** * @brief Reading and writing numbers from and to streams @@ -37,56 +38,56 @@ template void read(std::istream& is, T& x, endian e); ///Reads number from stream in little endian -void read_little(std::istream& is, uint8_t& x); -void read_little(std::istream& is, uint16_t& x); -void read_little(std::istream& is, uint32_t& x); -void read_little(std::istream& is, uint64_t& x); -void read_little(std::istream& is, int8_t& x); -void read_little(std::istream& is, int16_t& x); -void read_little(std::istream& is, int32_t& x); -void read_little(std::istream& is, int64_t& x); -void read_little(std::istream& is, float& x); -void read_little(std::istream& is, double& x); +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 -void read_big(std::istream& is, uint8_t& x); -void read_big(std::istream& is, uint16_t& x); -void read_big(std::istream& is, uint32_t& x); -void read_big(std::istream& is, uint64_t& x); -void read_big(std::istream& is, int8_t& x); -void read_big(std::istream& is, int16_t& x); -void read_big(std::istream& is, int32_t& x); -void read_big(std::istream& is, int64_t& x); -void read_big(std::istream& is, float& x); -void read_big(std::istream& is, double& x); +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 void write(std::ostream& os, T x, endian e); ///Writes number to stream in little endian -void write_little(std::ostream& os, uint8_t x); -void write_little(std::ostream& os, uint16_t x); -void write_little(std::ostream& os, uint32_t x); -void write_little(std::ostream& os, uint64_t x); -void write_little(std::ostream& os, int8_t x); -void write_little(std::ostream& os, int16_t x); -void write_little(std::ostream& os, int32_t x); -void write_little(std::ostream& os, int64_t x); -void write_little(std::ostream& os, float x); -void write_little(std::ostream& os, double x); +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 -void write_big(std::ostream& os, uint8_t x); -void write_big(std::ostream& os, uint16_t x); -void write_big(std::ostream& os, uint32_t x); -void write_big(std::ostream& os, uint64_t x); -void write_big(std::ostream& os, int8_t x); -void write_big(std::ostream& os, int16_t x); -void write_big(std::ostream& os, int32_t x); -void write_big(std::ostream& os, int64_t x); -void write_big(std::ostream& os, float x); -void write_big(std::ostream& os, double x); +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 void read(std::istream& is, T& x, endian e) diff --git a/include/io/izlibstream.h b/include/io/izlibstream.h index 4c6d5def85..2b9b91a74a 100644 --- a/include/io/izlibstream.h +++ b/include/io/izlibstream.h @@ -31,7 +31,7 @@ namespace zlib * @brief Stream buffer used by zlib::izlibstream * @sa izlibstream */ -class inflate_streambuf : public zlib_streambuf +class NBT_EXPORT inflate_streambuf : public zlib_streambuf { public: /** @@ -71,7 +71,7 @@ private: * after the end of the compressed data. * @sa inflate_streambuf */ -class izlibstream : public std::istream +class NBT_EXPORT izlibstream : public std::istream { public: /** diff --git a/include/io/ozlibstream.h b/include/io/ozlibstream.h index 78dd2c1684..65c97c7d69 100644 --- a/include/io/ozlibstream.h +++ b/include/io/ozlibstream.h @@ -31,7 +31,7 @@ namespace zlib * @brief Stream buffer used by zlib::ozlibstream * @sa ozlibstream */ -class deflate_streambuf : public zlib_streambuf +class NBT_EXPORT deflate_streambuf : public zlib_streambuf { public: /** @@ -68,7 +68,7 @@ private: * * @sa deflate_streambuf */ -class ozlibstream : public std::ostream +class NBT_EXPORT ozlibstream : public std::ostream { public: /** diff --git a/include/io/stream_reader.h b/include/io/stream_reader.h index ba9708fe1c..469e18ce87 100644 --- a/include/io/stream_reader.h +++ b/include/io/stream_reader.h @@ -34,7 +34,7 @@ namespace io { ///Exception that gets thrown when reading is not successful -class input_error : public std::runtime_error +class NBT_EXPORT input_error : public std::runtime_error { using std::runtime_error::runtime_error; }; @@ -46,7 +46,7 @@ class input_error : public std::runtime_error * 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 */ -std::pair> read_compound(std::istream& is, endian::endian e = endian::big); +NBT_EXPORT std::pair> read_compound(std::istream& is, endian::endian e = endian::big); /** * @brief Reads a named tag from the stream @@ -55,14 +55,14 @@ std::pair> read_compound(std::istream * of Minecraft uses Big Endian, the Pocket edition uses Little Endian * @throw input_error on failure */ -std::pair> read_tag(std::istream& is, endian::endian e = endian::big); +NBT_EXPORT std::pair> 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 stream_reader +class NBT_EXPORT stream_reader { public: /** diff --git a/include/io/stream_writer.h b/include/io/stream_writer.h index cd3317849b..b10f03adfa 100644 --- a/include/io/stream_writer.h +++ b/include/io/stream_writer.h @@ -44,14 +44,14 @@ class output_error : public std::runtime_error * @param e the byte order of the written data. The Java edition * of Minecraft uses Big Endian, the Pocket edition uses Little Endian */ -void write_tag(const std::string& key, const tag& t, std::ostream& os, endian::endian e = endian::big); +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 stream_writer +class NBT_EXPORT stream_writer { public: ///Maximum length of an NBT string (16 bit unsigned) diff --git a/include/io/zlib_streambuf.h b/include/io/zlib_streambuf.h index e7bb780902..4241769e4e 100644 --- a/include/io/zlib_streambuf.h +++ b/include/io/zlib_streambuf.h @@ -5,12 +5,13 @@ #include #include #include +#include "nbt_export.h" namespace zlib { ///Exception thrown in case zlib encounters a problem -class zlib_error : public std::runtime_error +class NBT_EXPORT zlib_error : public std::runtime_error { public: const int errcode; diff --git a/include/tag.h b/include/tag.h index a2ae49628e..35c71dfd30 100644 --- a/include/tag.h +++ b/include/tag.h @@ -23,6 +23,7 @@ #include #include #include +#include "nbt_export.h" namespace nbt { @@ -49,7 +50,7 @@ enum class tag_type : int8_t * @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 */ -bool is_valid_type(int type, bool allow_end = false); +NBT_EXPORT bool is_valid_type(int type, bool allow_end = false); //Forward declarations class nbt_visitor; @@ -61,7 +62,7 @@ namespace io } ///Base class for all NBT tag classes -class tag +class NBT_EXPORT tag { public: //Virtual destructor @@ -116,8 +117,8 @@ public: */ static std::unique_ptr create(tag_type type); - friend bool operator==(const tag& lhs, const tag& rhs); - friend bool operator!=(const tag& lhs, const tag& rhs); + friend NBT_EXPORT bool operator==(const tag& lhs, const tag& rhs); + friend NBT_EXPORT bool operator!=(const tag& lhs, const tag& rhs); private: /** @@ -128,7 +129,7 @@ private: }; ///Output operator for tag types -std::ostream& operator<<(std::ostream& os, tag_type tt); +NBT_EXPORT std::ostream& operator<<(std::ostream& os, tag_type tt); /** * @brief Output operator for tags @@ -136,7 +137,7 @@ std::ostream& operator<<(std::ostream& os, tag_type tt); * Uses @ref text::json_formatter * @relates tag */ -std::ostream& operator<<(std::ostream& os, const tag& t); +NBT_EXPORT std::ostream& operator<<(std::ostream& os, const tag& t); template T& tag::as() diff --git a/include/tag_compound.h b/include/tag_compound.h index b1c4c996dc..3bbc1f2fb0 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -29,7 +29,7 @@ namespace nbt { ///Tag that contains multiple unordered named tags of arbitrary types -class tag_compound final : public detail::crtp_tag +class NBT_EXPORT tag_compound final : public detail::crtp_tag { typedef std::map map_t_; diff --git a/include/tag_list.h b/include/tag_list.h index 5ae505d4fc..c6a568ec36 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -40,7 +40,7 @@ namespace nbt * will return tag_type::Null. The type will then be set when the first tag * is added to the list. */ -class tag_list final : public detail::crtp_tag +class NBT_EXPORT tag_list final : public detail::crtp_tag { public: //Iterator types @@ -174,8 +174,8 @@ public: * Lists are considered equal if their content types and the contained tags * are equal. */ - friend bool operator==(const tag_list& lhs, const tag_list& rhs); - friend bool operator!=(const tag_list& lhs, const tag_list& rhs); + 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 tags; diff --git a/include/tag_string.h b/include/tag_string.h index dee47f5f97..f6c49fd5ad 100644 --- a/include/tag_string.h +++ b/include/tag_string.h @@ -27,7 +27,7 @@ namespace nbt { ///Tag that contains a UTF-8 string -class tag_string final : public detail::crtp_tag +class NBT_EXPORT tag_string final : public detail::crtp_tag { public: ///The type of the tag diff --git a/include/text/json_formatter.h b/include/text/json_formatter.h index 4f3e434177..876caff086 100644 --- a/include/text/json_formatter.h +++ b/include/text/json_formatter.h @@ -22,6 +22,7 @@ #include "tagfwd.h" #include +#include "nbt_export.h" namespace nbt { @@ -33,7 +34,7 @@ namespace text * * @todo Make it configurable and able to produce actual standard-conformant JSON */ -class json_formatter +class NBT_EXPORT json_formatter { public: json_formatter() {} diff --git a/include/value.h b/include/value.h index d741c1f46a..fffe5cd3ec 100644 --- a/include/value.h +++ b/include/value.h @@ -57,7 +57,7 @@ namespace nbt * 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 value +class NBT_EXPORT value { public: //Constructors @@ -197,8 +197,8 @@ public: ///@sa tag::get_type tag_type get_type() const; - friend bool operator==(const value& lhs, const value& rhs); - friend bool operator!=(const value& lhs, const value& rhs); + 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_; diff --git a/include/value_initializer.h b/include/value_initializer.h index 4ec0215740..20fd436e67 100644 --- a/include/value_initializer.h +++ b/include/value_initializer.h @@ -41,7 +41,7 @@ namespace nbt * As value_initializer objects are in no way different than value objects, * they can just be converted to value after construction. */ -class value_initializer : public value +class NBT_EXPORT value_initializer : public value { public: value_initializer(std::unique_ptr&& t) noexcept: value(std::move(t)) {} diff --git a/src/tag_array.cpp b/src/tag_array.cpp index 99e32549b7..3f073232d1 100644 --- a/src/tag_array.cpp +++ b/src/tag_array.cpp @@ -104,7 +104,7 @@ void tag_array::write_payload(io::stream_writer& writer) const } //Enforce template instantiations -template class tag_array; -template class tag_array; +template class NBT_EXPORT tag_array; +template class NBT_EXPORT tag_array; } -- cgit 0.0.5-2-1-g0f52 From 3f965312d61fac3970f1000ee6433cc154abcb6c Mon Sep 17 00:00:00 2001 From: ljfa Date: Tue, 22 Sep 2015 15:21:16 +0200 Subject: Explicitly instantiate and export templates in header and move defintions of tag_array::at to header --- include/tag_array.h | 10 +++++++--- include/tag_primitive.h | 8 ++++++++ src/tag_array.cpp | 16 ---------------- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/include/tag_array.h b/include/tag_array.h index c59249b571..be9591c0f3 100644 --- a/include/tag_array.h +++ b/include/tag_array.h @@ -32,7 +32,7 @@ namespace detail { ///Meta-struct that holds the tag_type value for a specific array type template struct get_array_type - { static_assert(sizeof(T) != sizeof(T), "Invalid type paramter for tag_primitive, can only use byte or int"); }; + { static_assert(sizeof(T) != sizeof(T), "Invalid type paramter for tag_array, can only use byte or int"); }; template<> struct get_array_type : public std::integral_constant {}; template<> struct get_array_type : public std::integral_constant {}; @@ -73,8 +73,8 @@ public: * @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); - T at(size_t i) const; + 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 @@ -124,6 +124,10 @@ template bool operator!=(const tag_array& lhs, const tag_array& r typedef tag_array tag_byte_array; typedef tag_array tag_int_array; +//Explicit instantiations +template class NBT_EXPORT tag_array; +template class NBT_EXPORT tag_array; + } #endif // TAG_ARRAY_H_INCLUDED diff --git a/include/tag_primitive.h b/include/tag_primitive.h index 07ae985559..e3d5111e76 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -77,6 +77,14 @@ typedef tag_primitive tag_long; typedef tag_primitive tag_float; typedef tag_primitive tag_double; +//Explicit instantiations +template class NBT_EXPORT tag_primitive; +template class NBT_EXPORT tag_primitive; +template class NBT_EXPORT tag_primitive; +template class NBT_EXPORT tag_primitive; +template class NBT_EXPORT tag_primitive; +template class NBT_EXPORT tag_primitive; + template void tag_primitive::read_payload(io::stream_reader& reader) { diff --git a/src/tag_array.cpp b/src/tag_array.cpp index 3f073232d1..6173dbb1dd 100644 --- a/src/tag_array.cpp +++ b/src/tag_array.cpp @@ -25,18 +25,6 @@ namespace nbt { -template -T& tag_array::at(size_t i) -{ - return data.at(i); -} - -template -T tag_array::at(size_t i) const -{ - return data.at(i); -} - //Slightly different between byte_array and int_array //Reading template<> @@ -103,8 +91,4 @@ void tag_array::write_payload(io::stream_writer& writer) const writer.write_num(i); } -//Enforce template instantiations -template class NBT_EXPORT tag_array; -template class NBT_EXPORT tag_array; - } -- cgit 0.0.5-2-1-g0f52 From 682caa865d4a064b7fa1b46b02c6ffef1f3a7597 Mon Sep 17 00:00:00 2001 From: ljfa Date: Tue, 22 Sep 2015 15:57:20 +0200 Subject: Only search CxxTest when NBT_BUILD_TESTS is on --- CMakeLists.txt | 3 +-- test/CMakeLists.txt | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ebd2d0c6c..ad05cb74a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,10 +31,9 @@ target_link_libraries(nbt++ z) generate_export_header(nbt++ BASE_NAME nbt) find_package(ZLIB REQUIRED) -find_package(CxxTest) option(BUILD_SHARED_LIBS "Build shared libraries" OFF) -option(NBT_BUILD_TESTS "Build the unit tests. Requires CxxTest." ${CXXTEST_FOUND}) +option(NBT_BUILD_TESTS "Build the unit tests. Requires CxxTest." ON) if(NBT_BUILD_TESTS) enable_testing() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 418f6b4b14..04a461a07a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,6 @@ enable_testing() +find_package(CxxTest REQUIRED) + include_directories(${libnbt++_SOURCE_DIR}/include) include_directories(${CXXTEST_INCLUDE_DIR}) -- cgit 0.0.5-2-1-g0f52 From 9511d3c46dd8a1ad2f1c45a973d69bd048a0c5bf Mon Sep 17 00:00:00 2001 From: ljfa Date: Tue, 22 Sep 2015 16:21:17 +0200 Subject: Use target-specific CMake commands (thanks jandal) --- CMakeLists.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ad05cb74a3..12f816d285 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,8 +6,7 @@ include(GenerateExportHeader) set(CMAKE_CXX_VISIBILITY_PRESET hidden) set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) -add_definitions(-std=c++11) -include_directories(include ${CMAKE_CURRENT_BINARY_DIR}) +find_package(ZLIB REQUIRED) set(NBT_SOURCES src/endian_str.cpp @@ -27,11 +26,11 @@ set(NBT_SOURCES src/text/json_formatter.cpp) add_library(nbt++ ${NBT_SOURCES}) +target_include_directories(nbt++ PUBLIC include ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(nbt++ z) +target_compile_options(nbt++ PUBLIC -std=c++11) generate_export_header(nbt++ BASE_NAME nbt) -find_package(ZLIB REQUIRED) - option(BUILD_SHARED_LIBS "Build shared libraries" OFF) option(NBT_BUILD_TESTS "Build the unit tests. Requires CxxTest." ON) -- cgit 0.0.5-2-1-g0f52 From 7f38044924fe5f13a61c3265a73d0045ddd2a1b2 Mon Sep 17 00:00:00 2001 From: ljfa-ag Date: Tue, 22 Sep 2015 17:21:51 +0200 Subject: Fix CMake CMP0063 warning --- CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 12f816d285..2cde676fca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,8 +3,6 @@ project(libnbt++ VERSION 2.3) include(GenerateExportHeader) -set(CMAKE_CXX_VISIBILITY_PRESET hidden) -set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) find_package(ZLIB REQUIRED) @@ -28,8 +26,12 @@ set(NBT_SOURCES add_library(nbt++ ${NBT_SOURCES}) target_include_directories(nbt++ PUBLIC include ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(nbt++ z) + target_compile_options(nbt++ PUBLIC -std=c++11) generate_export_header(nbt++ BASE_NAME nbt) +set_target_properties(nbt++ PROPERTIES + CXX_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN 1) option(BUILD_SHARED_LIBS "Build shared libraries" OFF) option(NBT_BUILD_TESTS "Build the unit tests. Requires CxxTest." ON) -- cgit 0.0.5-2-1-g0f52 From 076647597f2ebb6f243f34aacaacf0a974752085 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Wed, 23 Sep 2015 00:33:33 +0200 Subject: Make zlib optional, adapt for including --- CMakeLists.txt | 29 +++++++++++++++++++++-------- test/CMakeLists.txt | 16 +++++++++++----- test/read_test.h | 5 ++++- test/write_test.h | 4 ++++ 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2cde676fca..9fc6bf1122 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,9 +2,15 @@ cmake_minimum_required(VERSION 3.0) project(libnbt++ VERSION 2.3) -include(GenerateExportHeader) +# supported configure options +option(NBT_BUILD_SHARED "Build shared libraries" OFF) +option(NBT_USE_ZLIB "Build additional zlib stream functionality" ON) +option(NBT_BUILD_TESTS "Build the unit tests. Requires CxxTest." ON) + +# hide this from includers. +set(BUILD_SHARED_LIBS ${NBT_BUILD_SHARED}) -find_package(ZLIB REQUIRED) +include(GenerateExportHeader) set(NBT_SOURCES src/endian_str.cpp @@ -16,26 +22,33 @@ set(NBT_SOURCES src/value.cpp src/value_initializer.cpp - src/io/izlibstream.cpp - src/io/ozlibstream.cpp src/io/stream_reader.cpp src/io/stream_writer.cpp src/text/json_formatter.cpp) +set(NBT_SOURCES_Z + src/io/izlibstream.cpp + src/io/ozlibstream.cpp) + +if(NBT_USE_ZLIB) + find_package(ZLIB REQUIRED) + list(APPEND NBT_SOURCES ${NBT_SOURCES_Z}) + add_definitions("-DNBT_HAVE_ZLIB") +endif() + add_library(nbt++ ${NBT_SOURCES}) target_include_directories(nbt++ PUBLIC include ${CMAKE_CURRENT_BINARY_DIR}) -target_link_libraries(nbt++ z) +if(NBT_USE_ZLIB) + target_link_libraries(nbt++ z) +endif() target_compile_options(nbt++ PUBLIC -std=c++11) generate_export_header(nbt++ BASE_NAME nbt) set_target_properties(nbt++ PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1) -option(BUILD_SHARED_LIBS "Build shared libraries" OFF) -option(NBT_BUILD_TESTS "Build the unit tests. Requires CxxTest." ON) - if(NBT_BUILD_TESTS) enable_testing() add_subdirectory(test) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 04a461a07a..5dedfcf1fb 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -11,6 +11,10 @@ function(use_testfiles target) copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/testfiles ${CMAKE_CURRENT_BINARY_DIR}) endfunction() +if(NBT_USE_ZLIB) + set(EXTRA_TEST_LIBS ${ZLIB_LIBRARY}) +endif() + CXXTEST_ADD_TEST(nbttest nbttest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/nbttest.h) target_link_libraries(nbttest nbt++) @@ -18,16 +22,18 @@ CXXTEST_ADD_TEST(endian_str_test endian_str_test.cpp ${CMAKE_CURRENT_SOURCE_DIR} target_link_libraries(endian_str_test nbt++) CXXTEST_ADD_TEST(read_test read_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/read_test.h) -target_link_libraries(read_test nbt++ z) +target_link_libraries(read_test nbt++ ${EXTRA_TEST_LIBS}) use_testfiles(read_test) CXXTEST_ADD_TEST(write_test write_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/write_test.h) -target_link_libraries(write_test nbt++ z) +target_link_libraries(write_test nbt++ ${EXTRA_TEST_LIBS}) use_testfiles(write_test) -CXXTEST_ADD_TEST(zlibstream_test zlibstream_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/zlibstream_test.h) -target_link_libraries(zlibstream_test nbt++ z) -use_testfiles(zlibstream_test) +if(NBT_USE_ZLIB) + CXXTEST_ADD_TEST(zlibstream_test zlibstream_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/zlibstream_test.h) + target_link_libraries(zlibstream_test nbt++ ${EXTRA_TEST_LIBS}) + use_testfiles(zlibstream_test) +endif() add_executable(format_test format_test.cpp) target_link_libraries(format_test nbt++) diff --git a/test/read_test.h b/test/read_test.h index eab30cfaad..e7c00a8d8e 100644 --- a/test/read_test.h +++ b/test/read_test.h @@ -19,7 +19,9 @@ */ #include #include "io/stream_reader.h" +#ifdef NBT_HAVE_ZLIB #include "io/izlibstream.h" +#endif #include "nbt_tags.h" #include #include @@ -214,9 +216,9 @@ public: "Even though unprovided for by NBT, the library should also handle " "the case where the file consists of something else than tag_compound")); } - void test_read_gzip() { +#ifdef NBT_HAVE_ZLIB std::ifstream file("bigtest.nbt", std::ios::binary); zlib::izlibstream igzs(file); TS_ASSERT(file && igzs); @@ -225,5 +227,6 @@ public: TS_ASSERT(igzs); TS_ASSERT_EQUALS(pair.first, "Level"); verify_bigtest_structure(*pair.second); +#endif } }; diff --git a/test/write_test.h b/test/write_test.h index 69c5dfdb97..a302891074 100644 --- a/test/write_test.h +++ b/test/write_test.h @@ -20,8 +20,10 @@ #include #include "io/stream_writer.h" #include "io/stream_reader.h" +#ifdef NBT_HAVE_ZLIB #include "io/ozlibstream.h" #include "io/izlibstream.h" +#endif #include "nbt_tags.h" #include #include @@ -247,6 +249,7 @@ public: TS_ASSERT_EQUALS(orig_pair.first, written_pair.first); TS_ASSERT(*orig_pair.second == *written_pair.second); +#ifdef NBT_HAVE_ZLIB //Now with gzip compression sstr.str(""); zlib::ozlibstream ogzs(sstr, -1, true); @@ -260,5 +263,6 @@ public: TS_ASSERT(igzs); TS_ASSERT_EQUALS(orig_pair.first, written_pair.first); TS_ASSERT(*orig_pair.second == *written_pair.second); +#endif } }; -- cgit 0.0.5-2-1-g0f52 From 9d17b0a9b93e684a5ac6cac8f70d9a7909a877cf Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Wed, 23 Sep 2015 00:38:31 +0200 Subject: Fix CMake CMP0063 warning when building static --- CMakeLists.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fc6bf1122..cda1297027 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,9 +45,12 @@ if(NBT_USE_ZLIB) endif() target_compile_options(nbt++ PUBLIC -std=c++11) generate_export_header(nbt++ BASE_NAME nbt) -set_target_properties(nbt++ PROPERTIES - CXX_VISIBILITY_PRESET hidden - VISIBILITY_INLINES_HIDDEN 1) + +if(${BUILD_SHARED_LIBS}) + set_target_properties(nbt++ PROPERTIES + CXX_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN 1) +endif() if(NBT_BUILD_TESTS) enable_testing() -- cgit 0.0.5-2-1-g0f52 From 5d0ffb50a526173ce58ae57136bf5d79a7e1920d Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Mon, 28 Sep 2015 20:51:00 +0200 Subject: Bump cmake to 3.1 and use the new CXX_STANDARD settings --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cda1297027..2472cb2b9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.1) project(libnbt++ VERSION 2.3) @@ -43,7 +43,7 @@ target_include_directories(nbt++ PUBLIC include ${CMAKE_CURRENT_BINARY_DIR}) if(NBT_USE_ZLIB) target_link_libraries(nbt++ z) endif() -target_compile_options(nbt++ PUBLIC -std=c++11) +set_property(TARGET nbt++ PROPERTY CXX_STANDARD 11) generate_export_header(nbt++ BASE_NAME nbt) if(${BUILD_SHARED_LIBS}) -- cgit 0.0.5-2-1-g0f52 From 4b305bbd2ac0e7a26987baf7949a484a87b474d4 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Wed, 28 Dec 2016 21:12:10 +0100 Subject: Allow renaming the library file from a parent project You can set NBT_NAME to something. Otherwise it defaults to 'nbt++'. --- CMakeLists.txt | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2472cb2b9d..904f00bc2d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,12 @@ option(NBT_BUILD_SHARED "Build shared libraries" OFF) option(NBT_USE_ZLIB "Build additional zlib stream functionality" ON) option(NBT_BUILD_TESTS "Build the unit tests. Requires CxxTest." ON) +if(NBT_NAME) + message("Using override nbt++ name: ${NBT_NAME}") +else() + set(NBT_NAME nbt++) +endif() + # hide this from includers. set(BUILD_SHARED_LIBS ${NBT_BUILD_SHARED}) @@ -37,17 +43,17 @@ if(NBT_USE_ZLIB) add_definitions("-DNBT_HAVE_ZLIB") endif() -add_library(nbt++ ${NBT_SOURCES}) -target_include_directories(nbt++ PUBLIC include ${CMAKE_CURRENT_BINARY_DIR}) +add_library(${NBT_NAME} ${NBT_SOURCES}) +target_include_directories(${NBT_NAME} PUBLIC include ${CMAKE_CURRENT_BINARY_DIR}) if(NBT_USE_ZLIB) - target_link_libraries(nbt++ z) + target_link_libraries(${NBT_NAME} z) endif() -set_property(TARGET nbt++ PROPERTY CXX_STANDARD 11) -generate_export_header(nbt++ BASE_NAME nbt) +set_property(TARGET ${NBT_NAME} PROPERTY CXX_STANDARD 11) +generate_export_header(${NBT_NAME} BASE_NAME nbt) if(${BUILD_SHARED_LIBS}) - set_target_properties(nbt++ PROPERTIES + set_target_properties(${NBT_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1) endif() -- cgit 0.0.5-2-1-g0f52 From c3ec5b3bc76f63c0de93d29077e3fc67e6222795 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Mon, 22 Jan 2018 04:21:39 +0100 Subject: Add cmake install command --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 904f00bc2d..dfad5e650f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,11 @@ endif() add_library(${NBT_NAME} ${NBT_SOURCES}) target_include_directories(${NBT_NAME} PUBLIC include ${CMAKE_CURRENT_BINARY_DIR}) +# Install it +if(DEFINED NBT_DEST_DIR) + install(TARGETS ${NBT_NAME} DESTINATION ${NBT_DEST_DIR}) +endif() + if(NBT_USE_ZLIB) target_link_libraries(${NBT_NAME} z) endif() -- cgit 0.0.5-2-1-g0f52 From 92f8d57227feb94643378ecf595626c60c0f59b8 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sat, 27 Jan 2018 02:41:07 +0100 Subject: fix cmake install command to not install .a files on windows --- CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dfad5e650f..b45655c4a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,7 +48,11 @@ target_include_directories(${NBT_NAME} PUBLIC include ${CMAKE_CURRENT_BINARY_DIR # Install it if(DEFINED NBT_DEST_DIR) - install(TARGETS ${NBT_NAME} DESTINATION ${NBT_DEST_DIR}) + install( + TARGETS ${NBT_NAME} + RUNTIME DESTINATION ${LIBRARY_DEST_DIR} + LIBRARY DESTINATION ${LIBRARY_DEST_DIR} + ) endif() if(NBT_USE_ZLIB) -- cgit 0.0.5-2-1-g0f52 From d7deea6dac9ca210a4dec273a7e10453492e04b5 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sat, 21 Apr 2018 22:21:05 +0200 Subject: Implement tag 12 (array of 64bit long) and fix tests --- include/nbt_visitor.h | 2 ++ include/tag.h | 1 + include/tag_array.h | 5 ++- include/tag_list.h | 1 + include/tagfwd.h | 1 + src/tag.cpp | 4 ++- src/tag_array.cpp | 49 ++++++++++++++++++++++---- src/tag_list.cpp | 1 + src/text/json_formatter.cpp | 12 +++++++ test/CMakeLists.txt | 83 ++++++++++++++++++++++++++++++++++++++++----- test/data.h | 39 +++++++++++++++++++++ test/format_test.cpp | 1 + test/nbttest.h | 37 ++++++++++++++++++-- test/read_test.h | 54 +++++++++++++++++++---------- test/write_test.h | 6 +++- test/zlibstream_test.h | 46 ++++++++++++++----------- 16 files changed, 283 insertions(+), 59 deletions(-) create mode 100644 test/data.h diff --git a/include/nbt_visitor.h b/include/nbt_visitor.h index fd24ae22d7..fe2688afb9 100644 --- a/include/nbt_visitor.h +++ b/include/nbt_visitor.h @@ -46,6 +46,7 @@ public: virtual void visit(tag_list&) {} virtual void visit(tag_compound&) {} virtual void visit(tag_int_array&) {} + virtual void visit(tag_long_array&) {} }; /** @@ -69,6 +70,7 @@ public: 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 {} diff --git a/include/tag.h b/include/tag.h index 35c71dfd30..c4f1d5d0b2 100644 --- a/include/tag.h +++ b/include/tag.h @@ -43,6 +43,7 @@ enum class tag_type : int8_t List = 9, Compound = 10, Int_Array = 11, + Long_Array = 12, Null = -1 ///< Used to denote empty @ref value s }; diff --git a/include/tag_array.h b/include/tag_array.h index be9591c0f3..6e6a92bbeb 100644 --- a/include/tag_array.h +++ b/include/tag_array.h @@ -36,13 +36,14 @@ namespace detail template<> struct get_array_type : public std::integral_constant {}; template<> struct get_array_type : public std::integral_constant {}; + template<> struct get_array_type : public std::integral_constant {}; } ///@cond /** * @brief Tag that contains an array of byte or int values * - * Common class for tag_byte_array and tag_int_array. + * Common class for tag_byte_array, tag_int_array and tag_long_array. */ template class tag_array final : public detail::crtp_tag> @@ -123,10 +124,12 @@ template bool operator!=(const tag_array& lhs, const tag_array& r //Typedefs that should be used instead of the template tag_array. typedef tag_array tag_byte_array; typedef tag_array tag_int_array; +typedef tag_array tag_long_array; //Explicit instantiations template class NBT_EXPORT tag_array; template class NBT_EXPORT tag_array; +template class NBT_EXPORT tag_array; } diff --git a/include/tag_list.h b/include/tag_list.h index c6a568ec36..ecd7e89c11 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -81,6 +81,7 @@ public: tag_list(std::initializer_list init); tag_list(std::initializer_list init); tag_list(std::initializer_list init); + tag_list(std::initializer_list init); /** * @brief Constructs a list with the given contents diff --git a/include/tagfwd.h b/include/tagfwd.h index 178edd7e47..a359885ce1 100644 --- a/include/tagfwd.h +++ b/include/tagfwd.h @@ -42,6 +42,7 @@ class tag_string; template class tag_array; typedef tag_array tag_byte_array; typedef tag_array tag_int_array; +typedef tag_array tag_long_array; class tag_list; class tag_compound; diff --git a/src/tag.cpp b/src/tag.cpp index df4d8cb555..7e3be3911c 100644 --- a/src/tag.cpp +++ b/src/tag.cpp @@ -33,7 +33,7 @@ static_assert(std::numeric_limits::is_iec559 && std::numeric_limits tag::clone() && @@ -56,6 +56,7 @@ std::unique_ptr tag::create(tag_type type) case tag_type::List: return make_unique(); case tag_type::Compound: return make_unique(); case tag_type::Int_Array: return make_unique(); + case tag_type::Long_Array: return make_unique(); default: throw std::invalid_argument("Invalid tag type"); } @@ -89,6 +90,7 @@ std::ostream& operator<<(std::ostream& os, tag_type tt) case tag_type::List: return os << "list"; case tag_type::Compound: return os << "compound"; case tag_type::Int_Array: return os << "int_array"; + case tag_type::Long_Array: return os << "long_array"; case tag_type::Null: return os << "null"; default: return os << "invalid"; diff --git a/src/tag_array.cpp b/src/tag_array.cpp index 6173dbb1dd..4a1668ad1e 100644 --- a/src/tag_array.cpp +++ b/src/tag_array.cpp @@ -43,26 +43,48 @@ void tag_array::read_payload(io::stream_reader& reader) throw io::input_error("Error reading contents of tag_byte_array"); } +template +void tag_array::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<> -void tag_array::read_payload(io::stream_reader& reader) +void tag_array::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_int_array"); + throw io::input_error("Error reading length of tag_long_array"); data.clear(); data.reserve(length); for(int32_t i = 0; i < length; ++i) { - int32_t val; + int64_t val; reader.read_num(val); data.push_back(val); } if(!reader.get_istr()) - throw io::input_error("Error reading contents of tag_int_array"); + throw io::input_error("Error reading contents of tag_long_array"); } //Writing @@ -78,16 +100,29 @@ void tag_array::write_payload(io::stream_writer& writer) const writer.get_ostr().write(reinterpret_cast(data.data()), data.size()); } +template +void tag_array::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(size())); + for(T i: data) + writer.write_num(i); +} + template<> -void tag_array::write_payload(io::stream_writer& writer) const +void tag_array::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("Int array is too large for NBT"); + throw std::length_error("Long array is too large for NBT"); } writer.write_num(static_cast(size())); - for(int32_t i: data) + for(int64_t i: data) writer.write_num(i); } diff --git a/src/tag_list.cpp b/src/tag_list.cpp index 67a3d4c15f..1650e602e3 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -37,6 +37,7 @@ tag_list::tag_list(std::initializer_list il) { init il) { init(il); } tag_list::tag_list(std::initializer_list il) { init(il); } tag_list::tag_list(std::initializer_list il) { init(il); } +tag_list::tag_list(std::initializer_list il) { init(il); } tag_list::tag_list(std::initializer_list init) { diff --git a/src/text/json_formatter.cpp b/src/text/json_formatter.cpp index 9b47210c6a..3001ff0958 100644 --- a/src/text/json_formatter.cpp +++ b/src/text/json_formatter.cpp @@ -151,6 +151,18 @@ namespace //anonymous os << "]"; } + void visit(const tag_long_array& la) override + { + os << "["; + for(unsigned int i = 0; i < la.size(); ++i) + { + os << la[i]; + if(i != la.size()-1) + os << ", "; + } + os << "]"; + } + private: const std::string indent_str = " "; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5dedfcf1fb..5f6e241f93 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,9 +1,64 @@ +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + if(CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64 OR CMAKE_SYSTEM_PROCESSOR STREQUAL amd64) + set(OBJCOPY_TARGET "elf64-x86-64") + set(OBJCOPY_ARCH "x86_64") + elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL i686) + set(OBJCOPY_TARGET "elf32-i386") + set(OBJCOPY_ARCH "i386") + else() + message(AUTHOR_WARNING "This is not a platform that would support testing nbt++") + return() + endif() +else() + message(AUTHOR_WARNING "This is not a platform that would support testing nbt++") + return() +endif() + enable_testing() find_package(CxxTest REQUIRED) include_directories(${libnbt++_SOURCE_DIR}/include) include_directories(${CXXTEST_INCLUDE_DIR}) +function(build_data out_var) + set(result) + foreach(in_f ${ARGN}) + set(out_f "${CMAKE_CURRENT_BINARY_DIR}/testfiles/${in_f}.obj") + add_custom_command( + COMMAND mkdir -p "${CMAKE_CURRENT_BINARY_DIR}/testfiles" + COMMAND ${CMAKE_OBJCOPY} --prefix-symbol=_ --input-target=binary --output-target=${OBJCOPY_TARGET} "${in_f}" "${out_f}" + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/testfiles/${in_f} + OUTPUT ${out_f} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/testfiles/ + VERBATIM + ) + SET_SOURCE_FILES_PROPERTIES( + ${out_f} + PROPERTIES + EXTERNAL_OBJECT true + GENERATED true + ) + list(APPEND result ${out_f}) + endforeach() + set(${out_var} "${result}" PARENT_SCOPE) +endfunction() + +build_data(DATA_OBJECTS + bigtest.nbt + bigtest.zlib + bigtest_corrupt.nbt + bigtest_eof.nbt + bigtest_uncompr + errortest_eof1 + errortest_eof2 + errortest_neg_length + errortest_noend + littletest_uncompr + toplevel_string + trailing_data.zlib +) +add_library(NbtTestData STATIC ${DATA_OBJECTS}) + #Specifies that the directory containing the testfiles get copied when the target is built function(use_testfiles target) add_custom_command(TARGET ${target} POST_BUILD @@ -11,30 +66,40 @@ function(use_testfiles target) copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/testfiles ${CMAKE_CURRENT_BINARY_DIR}) endfunction() +function(stop_warnings target) + target_compile_options(${target} PRIVATE + -Wno-unused-value + -Wno-self-assign-overloaded + ) +endfunction() + if(NBT_USE_ZLIB) set(EXTRA_TEST_LIBS ${ZLIB_LIBRARY}) endif() CXXTEST_ADD_TEST(nbttest nbttest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/nbttest.h) -target_link_libraries(nbttest nbt++) +target_link_libraries(nbttest ${NBT_NAME}) +stop_warnings(nbttest) CXXTEST_ADD_TEST(endian_str_test endian_str_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/endian_str_test.h) -target_link_libraries(endian_str_test nbt++) +target_link_libraries(endian_str_test ${NBT_NAME}) +stop_warnings(endian_str_test) CXXTEST_ADD_TEST(read_test read_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/read_test.h) -target_link_libraries(read_test nbt++ ${EXTRA_TEST_LIBS}) -use_testfiles(read_test) +target_link_libraries(read_test ${NBT_NAME} ${EXTRA_TEST_LIBS} NbtTestData) +stop_warnings(read_test) CXXTEST_ADD_TEST(write_test write_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/write_test.h) -target_link_libraries(write_test nbt++ ${EXTRA_TEST_LIBS}) -use_testfiles(write_test) +target_link_libraries(write_test ${NBT_NAME} ${EXTRA_TEST_LIBS} NbtTestData) +stop_warnings(write_test) if(NBT_USE_ZLIB) CXXTEST_ADD_TEST(zlibstream_test zlibstream_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/zlibstream_test.h) - target_link_libraries(zlibstream_test nbt++ ${EXTRA_TEST_LIBS}) - use_testfiles(zlibstream_test) + target_link_libraries(zlibstream_test ${NBT_NAME} ${EXTRA_TEST_LIBS} NbtTestData) + stop_warnings(zlibstream_test) endif() add_executable(format_test format_test.cpp) -target_link_libraries(format_test nbt++) +target_link_libraries(format_test ${NBT_NAME}) add_test(format_test format_test) +stop_warnings(format_test) diff --git a/test/data.h b/test/data.h new file mode 100644 index 0000000000..b6995190c5 --- /dev/null +++ b/test/data.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +extern "C" uint8_t __binary_bigtest_uncompr_start[]; +extern "C" uint8_t __binary_bigtest_uncompr_end[]; + +extern "C" uint8_t __binary_littletest_uncompr_start[]; +extern "C" uint8_t __binary_littletest_uncompr_end[]; + +extern "C" uint8_t __binary_errortest_eof1_start[]; +extern "C" uint8_t __binary_errortest_eof1_end[]; + +extern "C" uint8_t __binary_errortest_eof2_start[]; +extern "C" uint8_t __binary_errortest_eof2_end[]; + +extern "C" uint8_t __binary_errortest_noend_start[]; +extern "C" uint8_t __binary_errortest_noend_end[]; + +extern "C" uint8_t __binary_errortest_neg_length_start[]; +extern "C" uint8_t __binary_errortest_neg_length_end[]; + +extern "C" uint8_t __binary_toplevel_string_start[]; +extern "C" uint8_t __binary_toplevel_string_end[]; + +extern "C" uint8_t __binary_bigtest_nbt_start[]; +extern "C" uint8_t __binary_bigtest_nbt_end[]; + +extern "C" uint8_t __binary_bigtest_zlib_start[]; +extern "C" uint8_t __binary_bigtest_zlib_end[]; + +extern "C" uint8_t __binary_bigtest_corrupt_nbt_start[]; +extern "C" uint8_t __binary_bigtest_corrupt_nbt_end[]; + +extern "C" uint8_t __binary_bigtest_eof_nbt_start[]; +extern "C" uint8_t __binary_bigtest_eof_nbt_end[]; + +extern "C" uint8_t __binary_trailing_data_zlib_start[]; +extern "C" uint8_t __binary_trailing_data_zlib_end[]; diff --git a/test/format_test.cpp b/test/format_test.cpp index 8ea4095c90..87f7b21d72 100644 --- a/test/format_test.cpp +++ b/test/format_test.cpp @@ -52,6 +52,7 @@ int main() {"byte array", tag_byte_array{12, 13, 14, 15, 16}}, {"int array", tag_int_array{0x0badc0de, -0x0dedbeef, 0x1badbabe}}, + {"long array", tag_long_array{0x0badc0de0badc0de, -0x0dedbeef0dedbeef, 0x1badbabe1badbabe}}, {"list (empty)", tag_list::of({})}, {"list (float)", tag_list{2.0f, 1.0f, 0.5f, 0.25f}}, diff --git a/test/nbttest.h b/test/nbttest.h index 0ebce0c306..e3e16c548f 100644 --- a/test/nbttest.h +++ b/test/nbttest.h @@ -37,8 +37,8 @@ public: TS_ASSERT(is_valid_type(1)); TS_ASSERT(is_valid_type(5, false)); TS_ASSERT(is_valid_type(7, true)); - TS_ASSERT(is_valid_type(11)); - TS_ASSERT(!is_valid_type(12)); + TS_ASSERT(is_valid_type(12)); + TS_ASSERT(!is_valid_type(13)); //looks like TS_ASSERT_EQUALS can't handle abstract classes... TS_ASSERT(*tag::create(tag_type::Byte) == tag_byte()); @@ -78,6 +78,7 @@ public: TS_ASSERT_EQUALS(tag_list().get_type() , tag_type::List); TS_ASSERT_EQUALS(tag_compound().get_type() , tag_type::Compound); TS_ASSERT_EQUALS(tag_int_array().get_type() , tag_type::Int_Array); + TS_ASSERT_EQUALS(tag_long_array().get_type(), tag_type::Long_Array); } void test_tag_primitive() @@ -396,6 +397,7 @@ public: TS_ASSERT_EQUALS(arr.size(), 3u); TS_ASSERT((arr == tag_byte_array{1, 2, 127})); TS_ASSERT((arr != tag_int_array{1, 2, 127})); + TS_ASSERT((arr != tag_long_array{1, 2, 127})); TS_ASSERT((arr != tag_byte_array{1, 2, -1})); arr.clear(); @@ -431,6 +433,35 @@ public: TS_ASSERT(arr == tag_int_array()); } + void test_tag_long_array() + { + std::vector vec{100, 200, INT64_MAX, INT64_MIN}; + tag_long_array arr{100, 200, INT64_MAX, INT64_MIN}; + TS_ASSERT_EQUALS(arr.size(), 4u); + TS_ASSERT(arr.at(0) == 100 && arr[1] == 200 && arr[2] == INT64_MAX && arr.at(3) == INT64_MIN); + TS_ASSERT_THROWS(arr.at(-1), std::out_of_range); + TS_ASSERT_THROWS(arr.at(4), std::out_of_range); + + TS_ASSERT(arr.get() == vec); + TS_ASSERT(arr == tag_long_array(std::vector(vec))); + + arr.push_back(42); + vec.push_back(42); + + TS_ASSERT_EQUALS(arr.size(), 5u); + TS_ASSERT_EQUALS(arr.end() - arr.begin(), int(arr.size())); + TS_ASSERT(std::equal(arr.begin(), arr.end(), vec.begin())); + + arr.pop_back(); + arr.pop_back(); + TS_ASSERT_EQUALS(arr.size(), 3u); + TS_ASSERT((arr == tag_long_array{100, 200, INT64_MAX})); + TS_ASSERT((arr != tag_long_array{100, -56, -1})); + + arr.clear(); + TS_ASSERT(arr == tag_long_array()); + } + void test_visitor() { struct : public nbt_visitor @@ -448,6 +479,7 @@ public: void visit(tag_list& tag) { visited = &tag; } void visit(tag_compound& tag) { visited = &tag; } void visit(tag_int_array& tag) { visited = &tag; } + void visit(tag_long_array& tag) { visited = &tag; } } v; tag_byte b; b.accept(v); TS_ASSERT_EQUALS(v.visited, &b); @@ -461,5 +493,6 @@ public: tag_list ls; ls.accept(v); TS_ASSERT_EQUALS(v.visited, &ls); tag_compound c; c.accept(v); TS_ASSERT_EQUALS(v.visited, &c); tag_int_array ia; ia.accept(v); TS_ASSERT_EQUALS(v.visited, &ia); + tag_long_array la; la.accept(v); TS_ASSERT_EQUALS(v.visited, &la); } }; diff --git a/test/read_test.h b/test/read_test.h index e7c00a8d8e..75ddbd5594 100644 --- a/test/read_test.h +++ b/test/read_test.h @@ -29,6 +29,8 @@ using namespace nbt; +#include "data.h" + class read_test : public CxxTest::TestSuite { public: @@ -66,8 +68,8 @@ public: TS_ASSERT(!is); is.clear(); - //Test for invalid tag type 12 - is.str("\x0c"); + //Test for invalid tag type 13 + is.str("\x0d"); TS_ASSERT_THROWS(reader.read_type(), io::input_error); TS_ASSERT(!is); is.clear(); @@ -143,8 +145,8 @@ public: void test_read_bigtest() { //Uses an extended variant of Notch's original bigtest file - std::ifstream file("bigtest_uncompr", std::ios::binary); - TS_ASSERT(file); + std::string input(__binary_bigtest_uncompr_start, __binary_bigtest_uncompr_end); + std::istringstream file(input, std::ios::binary); auto pair = nbt::io::read_compound(file); TS_ASSERT_EQUALS(pair.first, "Level"); @@ -154,8 +156,8 @@ public: void test_read_littletest() { //Same as bigtest, but little endian - std::ifstream file("littletest_uncompr", std::ios::binary); - TS_ASSERT(file); + std::string input(__binary_littletest_uncompr_start, __binary_littletest_uncompr_end); + std::istringstream file(input, std::ios::binary); auto pair = nbt::io::read_compound(file, endian::little); TS_ASSERT_EQUALS(pair.first, "Level"); @@ -163,34 +165,49 @@ public: verify_bigtest_structure(*pair.second); } - void test_read_errors() + void test_read_eof1() { - std::ifstream file; + std::string input(__binary_errortest_eof1_start, __binary_errortest_eof1_end); + std::istringstream file(input, std::ios::binary); nbt::io::stream_reader reader(file); //EOF within a tag_double payload - file.open("errortest_eof1", std::ios::binary); TS_ASSERT(file); TS_ASSERT_THROWS(reader.read_tag(), io::input_error); TS_ASSERT(!file); + } + + void test_read_eof2() + { + std::string input(__binary_errortest_eof2_start, __binary_errortest_eof2_end); + std::istringstream file(input, std::ios::binary); + nbt::io::stream_reader reader(file); //EOF within a key in a compound - file.close(); - file.open("errortest_eof2", std::ios::binary); TS_ASSERT(file); TS_ASSERT_THROWS(reader.read_tag(), io::input_error); TS_ASSERT(!file); + } + + void test_read_errortest_noend() + { + std::string input(__binary_errortest_noend_start, __binary_errortest_noend_end); + std::istringstream file(input, std::ios::binary); + nbt::io::stream_reader reader(file); //Missing tag_end - file.close(); - file.open("errortest_noend", std::ios::binary); TS_ASSERT(file); TS_ASSERT_THROWS(reader.read_tag(), io::input_error); TS_ASSERT(!file); + } + + void test_read_errortest_neg_length() + { + std::string input(__binary_errortest_neg_length_start, __binary_errortest_neg_length_end); + std::istringstream file(input, std::ios::binary); + nbt::io::stream_reader reader(file); //Negative list length - file.close(); - file.open("errortest_neg_length", std::ios::binary); TS_ASSERT(file); TS_ASSERT_THROWS(reader.read_tag(), io::input_error); TS_ASSERT(!file); @@ -198,11 +215,11 @@ public: void test_read_misc() { - std::ifstream file; + std::string input(__binary_toplevel_string_start, __binary_toplevel_string_end); + std::istringstream file(input, std::ios::binary); nbt::io::stream_reader reader(file); //Toplevel tag other than compound - file.open("toplevel_string", std::ios::binary); TS_ASSERT(file); TS_ASSERT_THROWS(reader.read_compound(), io::input_error); TS_ASSERT(!file); @@ -219,7 +236,8 @@ public: void test_read_gzip() { #ifdef NBT_HAVE_ZLIB - std::ifstream file("bigtest.nbt", std::ios::binary); + std::string input(__binary_bigtest_nbt_start, __binary_bigtest_nbt_end); + std::istringstream file(input, std::ios::binary); zlib::izlibstream igzs(file); TS_ASSERT(file && igzs); diff --git a/test/write_test.h b/test/write_test.h index a302891074..8b16b2a779 100644 --- a/test/write_test.h +++ b/test/write_test.h @@ -29,6 +29,8 @@ #include #include +#include "data.h" + using namespace nbt; class read_test : public CxxTest::TestSuite @@ -226,7 +228,9 @@ public: Instead, we assume that reading already works correctly and re-read the written tag. Smaller-grained tests are already done above. */ - std::ifstream file("bigtest_uncompr", std::ios::binary); + std::string input(__binary_bigtest_uncompr_start, __binary_bigtest_uncompr_end); + std::istringstream file(input, std::ios::binary); + const auto orig_pair = io::read_compound(file); std::stringstream sstr; diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h index e76745f80f..26f86e0538 100644 --- a/test/zlibstream_test.h +++ b/test/zlibstream_test.h @@ -23,6 +23,8 @@ #include #include +#include "data.h" + using namespace zlib; class zlibstream_test : public CxxTest::TestSuite @@ -33,7 +35,8 @@ private: public: zlibstream_test() { - std::ifstream bigtest_f("bigtest_uncompr", std::ios::binary); + std::string input(__binary_bigtest_uncompr_start, __binary_bigtest_uncompr_end); + std::istringstream bigtest_f(input, std::ios::binary); std::stringbuf bigtest_b; bigtest_f >> &bigtest_b; bigtest = bigtest_b.str(); @@ -43,7 +46,8 @@ public: void test_inflate_gzip() { - std::ifstream gzip_in("bigtest.nbt", std::ios::binary); + std::string input(__binary_bigtest_nbt_start, __binary_bigtest_nbt_end); + std::istringstream gzip_in(input, std::ios::binary); TS_ASSERT(gzip_in); std::stringbuf data; @@ -93,7 +97,8 @@ public: void test_inflate_zlib() { - std::ifstream zlib_in("bigtest.zlib", std::ios::binary); + std::string input(__binary_bigtest_zlib_start, __binary_bigtest_zlib_end); + std::istringstream zlib_in(input, std::ios::binary); TS_ASSERT(zlib_in); std::stringbuf data; @@ -109,34 +114,35 @@ public: void test_inflate_corrupt() { - std::ifstream gzip_in("bigtest_corrupt.nbt", std::ios::binary); + std::string input(__binary_bigtest_corrupt_nbt_start, __binary_bigtest_corrupt_nbt_end); + std::istringstream gzip_in(input, std::ios::binary); TS_ASSERT(gzip_in); std::vector buf(bigtest.size()); - { - izlibstream igzs(gzip_in); - igzs.exceptions(std::ios::failbit | std::ios::badbit); - TS_ASSERT_THROWS(igzs.read(buf.data(), buf.size()), zlib_error); - TS_ASSERT(igzs.bad()); - } + izlibstream igzs(gzip_in); + igzs.exceptions(std::ios::failbit | std::ios::badbit); + TS_ASSERT_THROWS(igzs.read(buf.data(), buf.size()), zlib_error); + TS_ASSERT(igzs.bad()); + } - gzip_in.close(); - gzip_in.clear(); - gzip_in.open("bigtest_eof.nbt", std::ios::binary); + void test_inflate_eof() + { + std::string input(__binary_bigtest_eof_nbt_start, __binary_bigtest_eof_nbt_end); + std::istringstream gzip_in(input, std::ios::binary); TS_ASSERT(gzip_in); - { - izlibstream igzs(gzip_in); - igzs.exceptions(std::ios::failbit | std::ios::badbit); - TS_ASSERT_THROWS(igzs.read(buf.data(), buf.size()), zlib_error); - TS_ASSERT(igzs.bad()); - } + std::vector buf(bigtest.size()); + izlibstream igzs(gzip_in); + igzs.exceptions(std::ios::failbit | std::ios::badbit); + TS_ASSERT_THROWS(igzs.read(buf.data(), buf.size()), zlib_error); + TS_ASSERT(igzs.bad()); } void test_inflate_trailing() { //This file contains additional uncompressed data after the zlib-compressed data - std::ifstream file("trailing_data.zlib", std::ios::binary); + std::string input(__binary_trailing_data_zlib_start, __binary_trailing_data_zlib_end); + std::istringstream file(input, std::ios::binary); izlibstream izls(file, 32); TS_ASSERT(file && izls); -- cgit 0.0.5-2-1-g0f52 From dc72a20b7efd304d12af2025223fad07b4b78464 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sun, 7 Jun 2020 21:30:03 +0200 Subject: Fix not building on Gentoo Originally from https://github.com/Barteks2x --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index b45655c4a1..1faa5dc034 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ target_include_directories(${NBT_NAME} PUBLIC include ${CMAKE_CURRENT_BINARY_DIR if(DEFINED NBT_DEST_DIR) install( TARGETS ${NBT_NAME} + ARCHIVE DESTINATION ${LIBRARY_DEST_DIR} RUNTIME DESTINATION ${LIBRARY_DEST_DIR} LIBRARY DESTINATION ${LIBRARY_DEST_DIR} ) -- cgit 0.0.5-2-1-g0f52 From 691cea4b1a4aa1779c9ae88d88924e2fd3dcb6fd Mon Sep 17 00:00:00 2001 From: gazelleguardian Date: Mon, 6 Dec 2021 03:54:47 +0100 Subject: Fix building on windows with clang and msvc Signed-off-by: Philipp David --- CMakeLists.txt | 2 +- include/io/stream_writer.h | 1 + include/tag_array.h | 5 ----- include/tag_list.h | 2 +- src/tag_array.cpp | 5 +++++ 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1faa5dc034..fafeae4b27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,7 +57,7 @@ if(DEFINED NBT_DEST_DIR) endif() if(NBT_USE_ZLIB) - target_link_libraries(${NBT_NAME} z) + target_link_libraries(${NBT_NAME} ZLIB::ZLIB) endif() set_property(TARGET ${NBT_NAME} PROPERTY CXX_STANDARD 11) generate_export_header(${NBT_NAME} BASE_NAME nbt) diff --git a/include/io/stream_writer.h b/include/io/stream_writer.h index b10f03adfa..a69508f392 100644 --- a/include/io/stream_writer.h +++ b/include/io/stream_writer.h @@ -23,6 +23,7 @@ #include "tag.h" #include "endian_str.h" #include +#include namespace nbt { diff --git a/include/tag_array.h b/include/tag_array.h index 6e6a92bbeb..bed84d8070 100644 --- a/include/tag_array.h +++ b/include/tag_array.h @@ -126,11 +126,6 @@ typedef tag_array tag_byte_array; typedef tag_array tag_int_array; typedef tag_array tag_long_array; -//Explicit instantiations -template class NBT_EXPORT tag_array; -template class NBT_EXPORT tag_array; -template class NBT_EXPORT tag_array; - } #endif // TAG_ARRAY_H_INCLUDED diff --git a/include/tag_list.h b/include/tag_list.h index ecd7e89c11..e2c9b7bb7c 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -215,7 +215,7 @@ void tag_list::init(std::initializer_list init) el_type_ = T::type; tags.reserve(init.size()); for(const Arg& arg: init) - tags.emplace_back(make_unique(arg)); + tags.emplace_back(nbt::make_unique(arg)); } } diff --git a/src/tag_array.cpp b/src/tag_array.cpp index 4a1668ad1e..a48f52a9c2 100644 --- a/src/tag_array.cpp +++ b/src/tag_array.cpp @@ -126,4 +126,9 @@ void tag_array::write_payload(io::stream_writer& writer) const writer.write_num(i); } +//Explicit instantiations +template class NBT_EXPORT tag_array; +template class NBT_EXPORT tag_array; +template class NBT_EXPORT tag_array; + } -- cgit 0.0.5-2-1-g0f52 From d84c3a5d9a463eb7256f0244ad69d3adac9de92e Mon Sep 17 00:00:00 2001 From: Philipp David Date: Sat, 19 Mar 2022 17:54:19 +0100 Subject: Bump cmake requirement for LTO --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fafeae4b27..d7e4c6a037 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.9.4) project(libnbt++ VERSION 2.3) -- cgit 0.0.5-2-1-g0f52 From b156bcaa4acf0a5b392bbed60bd274c39e2398d4 Mon Sep 17 00:00:00 2001 From: Philipp David Date: Tue, 22 Mar 2022 17:02:06 +0100 Subject: Retab CMakeLists.txt --- CMakeLists.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d7e4c6a037..adbd3443d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,9 +8,9 @@ option(NBT_USE_ZLIB "Build additional zlib stream functionality" ON) option(NBT_BUILD_TESTS "Build the unit tests. Requires CxxTest." ON) if(NBT_NAME) - message("Using override nbt++ name: ${NBT_NAME}") + message("Using override nbt++ name: ${NBT_NAME}") else() - set(NBT_NAME nbt++) + set(NBT_NAME nbt++) endif() # hide this from includers. @@ -48,12 +48,12 @@ target_include_directories(${NBT_NAME} PUBLIC include ${CMAKE_CURRENT_BINARY_DIR # Install it if(DEFINED NBT_DEST_DIR) - install( - TARGETS ${NBT_NAME} - ARCHIVE DESTINATION ${LIBRARY_DEST_DIR} - RUNTIME DESTINATION ${LIBRARY_DEST_DIR} - LIBRARY DESTINATION ${LIBRARY_DEST_DIR} - ) + install( + TARGETS ${NBT_NAME} + ARCHIVE DESTINATION ${LIBRARY_DEST_DIR} + RUNTIME DESTINATION ${LIBRARY_DEST_DIR} + LIBRARY DESTINATION ${LIBRARY_DEST_DIR} + ) endif() if(NBT_USE_ZLIB) -- cgit 0.0.5-2-1-g0f52 From 129be45a7f91920e76673af104534d215c497d85 Mon Sep 17 00:00:00 2001 From: Philipp David Date: Thu, 24 Mar 2022 08:29:40 +0100 Subject: Move templated methods to header --- CMakeLists.txt | 1 - include/tag_array.h | 104 ++++++++++++++++++++++++++++++++++++++++ src/tag_array.cpp | 134 ---------------------------------------------------- 3 files changed, 104 insertions(+), 135 deletions(-) delete mode 100644 src/tag_array.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index adbd3443d0..389da31bc9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,6 @@ include(GenerateExportHeader) set(NBT_SOURCES src/endian_str.cpp src/tag.cpp - src/tag_array.cpp src/tag_compound.cpp src/tag_list.cpp src/tag_string.cpp diff --git a/include/tag_array.h b/include/tag_array.h index bed84d8070..a12c22688b 100644 --- a/include/tag_array.h +++ b/include/tag_array.h @@ -21,8 +21,11 @@ #define TAG_ARRAY_H_INCLUDED #include "crtp_tag.h" +#include "io/stream_reader.h" +#include "io/stream_writer.h" #include #include +#include namespace nbt { @@ -121,6 +124,107 @@ template bool operator==(const tag_array& lhs, const tag_array& r template bool operator!=(const tag_array& lhs, const tag_array& rhs) { return !(lhs == rhs); } +//Slightly different between byte_array and int_array +//Reading +template<> +inline void tag_array::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(data.data()), length); + if(!reader.get_istr()) + throw io::input_error("Error reading contents of tag_byte_array"); +} + +template +inline void tag_array::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::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::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(size())); + writer.get_ostr().write(reinterpret_cast(data.data()), data.size()); +} + +template +inline void tag_array::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(size())); + for(T i: data) + writer.write_num(i); +} + +template<> +inline void tag_array::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(size())); + for(int64_t i: data) + writer.write_num(i); +} + //Typedefs that should be used instead of the template tag_array. typedef tag_array tag_byte_array; typedef tag_array tag_int_array; diff --git a/src/tag_array.cpp b/src/tag_array.cpp deleted file mode 100644 index a48f52a9c2..0000000000 --- a/src/tag_array.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * libnbt++ - A library for the Minecraft Named Binary Tag format. - * Copyright (C) 2013, 2015 ljfa-ag - * - * This file is part of libnbt++. - * - * libnbt++ is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * libnbt++ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with libnbt++. If not, see . - */ -#include "tag_array.h" -#include "io/stream_reader.h" -#include "io/stream_writer.h" -#include - -namespace nbt -{ - -//Slightly different between byte_array and int_array -//Reading -template<> -void tag_array::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(data.data()), length); - if(!reader.get_istr()) - throw io::input_error("Error reading contents of tag_byte_array"); -} - -template -void tag_array::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<> -void tag_array::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<> -void tag_array::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(size())); - writer.get_ostr().write(reinterpret_cast(data.data()), data.size()); -} - -template -void tag_array::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(size())); - for(T i: data) - writer.write_num(i); -} - -template<> -void tag_array::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(size())); - for(int64_t i: data) - writer.write_num(i); -} - -//Explicit instantiations -template class NBT_EXPORT tag_array; -template class NBT_EXPORT tag_array; -template class NBT_EXPORT tag_array; - -} -- cgit 0.0.5-2-1-g0f52 From 7105bd748e63ca75a2893ef9e411f9641fbfce2f Mon Sep 17 00:00:00 2001 From: PandaNinjas Date: Wed, 7 Jun 2023 14:43:02 -0400 Subject: Remove unused parameter in json_formatter.cpp --- src/text/json_formatter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/text/json_formatter.cpp b/src/text/json_formatter.cpp index 3001ff0958..8a6282554e 100644 --- a/src/text/json_formatter.cpp +++ b/src/text/json_formatter.cpp @@ -35,7 +35,7 @@ namespace //anonymous class json_fmt_visitor : public const_nbt_visitor { public: - json_fmt_visitor(std::ostream& os, const json_formatter& fmt): + json_fmt_visitor(std::ostream& os): os(os) {} @@ -199,7 +199,7 @@ namespace //anonymous void json_formatter::print(std::ostream& os, const tag& t) const { - json_fmt_visitor v(os, *this); + json_fmt_visitor v(os); t.accept(v); } -- cgit 0.0.5-2-1-g0f52 From 37084afebfb09b30126f26eb64e508c5b8f9e8b6 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 5 Nov 2023 12:00:28 +0000 Subject: Limit recursion, attempt 2 --- include/io/stream_reader.h | 1 + src/io/stream_reader.cpp | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/include/io/stream_reader.h b/include/io/stream_reader.h index 469e18ce87..3a677db748 100644 --- a/include/io/stream_reader.h +++ b/include/io/stream_reader.h @@ -121,6 +121,7 @@ public: private: std::istream& is; + int depth = 0; const endian::endian endian; }; diff --git a/src/io/stream_reader.cpp b/src/io/stream_reader.cpp index f6f30a5b49..cf0dee38e8 100644 --- a/src/io/stream_reader.cpp +++ b/src/io/stream_reader.cpp @@ -27,6 +27,8 @@ namespace nbt namespace io { +constexpr int MAX_DEPTH = 1024; + std::pair> read_compound(std::istream& is, endian::endian e) { return stream_reader(is, e).read_compound(); @@ -74,8 +76,11 @@ std::pair> stream_reader::read_tag() std::unique_ptr stream_reader::read_payload(tag_type type) { + if (++depth > MAX_DEPTH) + throw input_error("Too deeply nested"); std::unique_ptr t = tag::create(type); t->read_payload(*this); + --depth; return t; } -- cgit 0.0.5-2-1-g0f52 From 3554a63406187b9dac5f72647644527f7fc3396b Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 6 Nov 2023 16:05:47 +0000 Subject: Small tweak Signed-off-by: TheKodeToad --- src/io/stream_reader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/stream_reader.cpp b/src/io/stream_reader.cpp index cf0dee38e8..28c1e1d89f 100644 --- a/src/io/stream_reader.cpp +++ b/src/io/stream_reader.cpp @@ -27,7 +27,7 @@ namespace nbt namespace io { -constexpr int MAX_DEPTH = 1024; +static constexpr int MAX_DEPTH = 1024; std::pair> read_compound(std::istream& is, endian::endian e) { -- cgit 0.0.5-2-1-g0f52 From 994ff2dda7a15bca9cbf551ecfe0dab6c08e4899 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 16 Apr 2025 16:43:14 +0300 Subject: chore:increment cmake version Signed-off-by: Trial97 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 389da31bc9..f01e80c1d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9.4) +cmake_minimum_required(VERSION 3.15) project(libnbt++ VERSION 2.3) -- cgit 0.0.5-2-1-g0f52 From e901c93362dd7c901a49e75ceb3bcaf8b8760caa Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Sun, 1 Feb 2026 13:56:14 +0100 Subject: chore: comply with reuse Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .gitattributes | 4 + .github/workflows/lint-reuse.yml | 15 ++ .gitignore | 4 + CMakeLists.txt | 4 + LICENSES/CC0-1.0.txt | 121 +++++++++++ LICENSES/LGPL-3.0-or-later.txt | 304 ++++++++++++++++++++++++++++ README.md | 6 + include/crtp_tag.h | 6 + include/endian_str.h | 6 + include/io/izlibstream.h | 6 + include/io/ozlibstream.h | 6 + include/io/stream_reader.h | 6 + include/io/stream_writer.h | 6 + include/io/zlib_streambuf.h | 6 + include/make_unique.h | 6 + include/nbt_tags.h | 6 + include/nbt_visitor.h | 6 + include/primitive_detail.h | 6 + include/tag.h | 6 + include/tag_array.h | 6 + include/tag_compound.h | 6 + include/tag_list.h | 6 + include/tag_primitive.h | 6 + include/tag_string.h | 6 + include/tagfwd.h | 6 + include/text/json_formatter.h | 6 + include/value.h | 6 + include/value_initializer.h | 6 + src/endian_str.cpp | 4 + src/io/izlibstream.cpp | 4 + src/io/ozlibstream.cpp | 4 + src/io/stream_reader.cpp | 4 + src/io/stream_writer.cpp | 4 + src/tag.cpp | 4 + src/tag_compound.cpp | 4 + src/tag_list.cpp | 4 + src/tag_string.cpp | 4 + src/text/json_formatter.cpp | 4 + src/value.cpp | 4 + src/value_initializer.cpp | 4 + test/CMakeLists.txt | 4 + test/data.h | 6 + test/endian_str_test.h | 6 + test/format_test.cpp | 4 + test/nbttest.h | 6 + test/read_test.h | 6 + test/testfiles/bigtest.nbt.license | 3 + test/testfiles/bigtest.zlib.license | 3 + test/testfiles/bigtest_corrupt.nbt.license | 3 + test/testfiles/bigtest_eof.nbt.license | 3 + test/testfiles/bigtest_uncompr.license | 3 + test/testfiles/errortest_eof1.license | 3 + test/testfiles/errortest_eof2.license | 3 + test/testfiles/errortest_neg_length.license | 3 + test/testfiles/errortest_noend.license | 3 + test/testfiles/littletest_uncompr.license | 3 + test/testfiles/toplevel_string.license | 3 + test/testfiles/trailing_data.zlib.license | 3 + test/write_test.h | 6 + test/zlibstream_test.h | 6 + 60 files changed, 712 insertions(+) create mode 100644 .github/workflows/lint-reuse.yml create mode 100644 LICENSES/CC0-1.0.txt create mode 100644 LICENSES/LGPL-3.0-or-later.txt create mode 100644 test/testfiles/bigtest.nbt.license create mode 100644 test/testfiles/bigtest.zlib.license create mode 100644 test/testfiles/bigtest_corrupt.nbt.license create mode 100644 test/testfiles/bigtest_eof.nbt.license create mode 100644 test/testfiles/bigtest_uncompr.license create mode 100644 test/testfiles/errortest_eof1.license create mode 100644 test/testfiles/errortest_eof2.license create mode 100644 test/testfiles/errortest_neg_length.license create mode 100644 test/testfiles/errortest_noend.license create mode 100644 test/testfiles/littletest_uncompr.license create mode 100644 test/testfiles/toplevel_string.license create mode 100644 test/testfiles/trailing_data.zlib.license diff --git a/.gitattributes b/.gitattributes index dfe0770424..82b0feeb64 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,6 @@ +# SPDX-FileCopyrightText: 2013, 2015 ljfa-ag +# +# SPDX-License-Identifier: LGPL-3.0-or-later + # Auto detect text files and perform LF normalization * text=auto diff --git a/.github/workflows/lint-reuse.yml b/.github/workflows/lint-reuse.yml new file mode 100644 index 0000000000..942ffd1e15 --- /dev/null +++ b/.github/workflows/lint-reuse.yml @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2022 Free Software Foundation Europe e.V. +# +# SPDX-License-Identifier: CC0-1.0 + +name: REUSE Compliance Check + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: REUSE Compliance Check + uses: fsfe/reuse-action@v6 diff --git a/.gitignore b/.gitignore index b1ef936ea3..bbd5e827cb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2013, 2015 ljfa-ag +# +# SPDX-License-Identifier: LGPL-3.0-or-later + *.cbp *.depend *.layout diff --git a/CMakeLists.txt b/CMakeLists.txt index f01e80c1d9..6e3486c400 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2013, 2015 ljfa-ag +# +# SPDX-License-Identifier: LGPL-3.0-or-later + cmake_minimum_required(VERSION 3.15) project(libnbt++ VERSION 2.3) diff --git a/LICENSES/CC0-1.0.txt b/LICENSES/CC0-1.0.txt new file mode 100644 index 0000000000..0e259d42c9 --- /dev/null +++ b/LICENSES/CC0-1.0.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/LICENSES/LGPL-3.0-or-later.txt b/LICENSES/LGPL-3.0-or-later.txt new file mode 100644 index 0000000000..513d1c01fe --- /dev/null +++ b/LICENSES/LGPL-3.0-or-later.txt @@ -0,0 +1,304 @@ +GNU LESSER GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. + +0. Additional Definitions. + +As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. + +"The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. + +An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. + +A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". + +The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. + +The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. + +1. Exception to Section 3 of the GNU GPL. +You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. + +2. Conveying Modified Versions. +If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: + + a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. + +3. Object Code Incorporating Material from Library Header Files. +The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license document. + +4. Combined Works. +You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: + + a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license document. + + c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. + + e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) + +5. Combined Libraries. +You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. + +6. Revised Versions of the GNU Lesser General Public License. +The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. + +If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. + +GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright © 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +Preamble + +The GNU General Public License is a free, copyleft license for software and other kinds of works. + +The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS + +0. Definitions. + +“This License” refers to version 3 of the GNU General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based on the Program. + +To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. + +To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. + +1. Source Code. +The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. + +A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. + +The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +2. Basic Permissions. +All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. +No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. + +4. Conveying Verbatim Copies. +You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. + +5. Conveying Modified Source Versions. +You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. + + c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. + +A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. + +6. Conveying Non-Source Forms. +You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: + + a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. + + d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. + +A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. + +7. Additional Terms. +“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or authors of the material; or + + e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. + +All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. + +8. Termination. +You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. + +9. Acceptance Not Required for Having Copies. +You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. + +10. Automatic Licensing of Downstream Recipients. +Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. + +An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. + +11. Patents. +A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. + +In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. + +A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. + +12. No Surrender of Others' Freedom. +If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. + +13. Use with the GNU Affero General Public License. +Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. + +14. Revised Versions of this License. +The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. + +Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. + +15. Disclaimer of Warranty. +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +17. Interpretation of Sections 15 and 16. +If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. + +You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . + +The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . diff --git a/README.md b/README.md index 6ec6b57e03..975ef3c1ec 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ + + # libnbt++ 2 libnbt++ is a free C++ library for Minecraft's file format Named Binary Tag diff --git a/include/crtp_tag.h b/include/crtp_tag.h index 7b802979a9..28a59f1530 100644 --- a/include/crtp_tag.h +++ b/include/crtp_tag.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/include/endian_str.h b/include/endian_str.h index ca36835fd0..4a3500342b 100644 --- a/include/endian_str.h +++ b/include/endian_str.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/include/io/izlibstream.h b/include/io/izlibstream.h index 2b9b91a74a..f7d180f4e8 100644 --- a/include/io/izlibstream.h +++ b/include/io/izlibstream.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/include/io/ozlibstream.h b/include/io/ozlibstream.h index 65c97c7d69..5ac3dba97f 100644 --- a/include/io/ozlibstream.h +++ b/include/io/ozlibstream.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/include/io/stream_reader.h b/include/io/stream_reader.h index 3a677db748..821a9ff8d3 100644 --- a/include/io/stream_reader.h +++ b/include/io/stream_reader.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/include/io/stream_writer.h b/include/io/stream_writer.h index a69508f392..32a01c89c9 100644 --- a/include/io/stream_writer.h +++ b/include/io/stream_writer.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/include/io/zlib_streambuf.h b/include/io/zlib_streambuf.h index 4241769e4e..e8eea1822f 100644 --- a/include/io/zlib_streambuf.h +++ b/include/io/zlib_streambuf.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + #ifndef ZLIB_STREAMBUF_H_INCLUDED #define ZLIB_STREAMBUF_H_INCLUDED diff --git a/include/make_unique.h b/include/make_unique.h index 9a929543cc..a475a4a734 100644 --- a/include/make_unique.h +++ b/include/make_unique.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/include/nbt_tags.h b/include/nbt_tags.h index 7f557fc769..bcab332919 100644 --- a/include/nbt_tags.h +++ b/include/nbt_tags.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/include/nbt_visitor.h b/include/nbt_visitor.h index fe2688afb9..0b37161a5f 100644 --- a/include/nbt_visitor.h +++ b/include/nbt_visitor.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/include/primitive_detail.h b/include/primitive_detail.h index 4715ee7ed6..de070d3320 100644 --- a/include/primitive_detail.h +++ b/include/primitive_detail.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/include/tag.h b/include/tag.h index c4f1d5d0b2..0cb3c82c95 100644 --- a/include/tag.h +++ b/include/tag.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/include/tag_array.h b/include/tag_array.h index a12c22688b..de1dd0953a 100644 --- a/include/tag_array.h +++ b/include/tag_array.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/include/tag_compound.h b/include/tag_compound.h index 3bbc1f2fb0..60d9d83927 100644 --- a/include/tag_compound.h +++ b/include/tag_compound.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/include/tag_list.h b/include/tag_list.h index e2c9b7bb7c..8601c9081f 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/include/tag_primitive.h b/include/tag_primitive.h index e3d5111e76..938ec51087 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/include/tag_string.h b/include/tag_string.h index f6c49fd5ad..35ddd371aa 100644 --- a/include/tag_string.h +++ b/include/tag_string.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/include/tagfwd.h b/include/tagfwd.h index a359885ce1..a302fe327b 100644 --- a/include/tagfwd.h +++ b/include/tagfwd.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/include/text/json_formatter.h b/include/text/json_formatter.h index 876caff086..2f9246939b 100644 --- a/include/text/json_formatter.h +++ b/include/text/json_formatter.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/include/value.h b/include/value.h index fffe5cd3ec..36b55ffa57 100644 --- a/include/value.h +++ b/include/value.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/include/value_initializer.h b/include/value_initializer.h index 20fd436e67..7bcc424de2 100644 --- a/include/value_initializer.h +++ b/include/value_initializer.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/src/endian_str.cpp b/src/endian_str.cpp index 8d136b09a5..c6de84201d 100644 --- a/src/endian_str.cpp +++ b/src/endian_str.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag +// +// SPDX-License-Identifier: LGPL-3.0-or-later + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/src/io/izlibstream.cpp b/src/io/izlibstream.cpp index 0a7512469e..b361971081 100644 --- a/src/io/izlibstream.cpp +++ b/src/io/izlibstream.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag +// +// SPDX-License-Identifier: LGPL-3.0-or-later + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/src/io/ozlibstream.cpp b/src/io/ozlibstream.cpp index 73f1057c67..cf0f505394 100644 --- a/src/io/ozlibstream.cpp +++ b/src/io/ozlibstream.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag +// +// SPDX-License-Identifier: LGPL-3.0-or-later + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/src/io/stream_reader.cpp b/src/io/stream_reader.cpp index 28c1e1d89f..06375ca0d5 100644 --- a/src/io/stream_reader.cpp +++ b/src/io/stream_reader.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag +// +// SPDX-License-Identifier: LGPL-3.0-or-later + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/src/io/stream_writer.cpp b/src/io/stream_writer.cpp index 036c5d40ac..0294a6d4e9 100644 --- a/src/io/stream_writer.cpp +++ b/src/io/stream_writer.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag +// +// SPDX-License-Identifier: LGPL-3.0-or-later + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/src/tag.cpp b/src/tag.cpp index 7e3be3911c..0bff2c6440 100644 --- a/src/tag.cpp +++ b/src/tag.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag +// +// SPDX-License-Identifier: LGPL-3.0-or-later + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index 4085bb4e0c..c71166de95 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag +// +// SPDX-License-Identifier: LGPL-3.0-or-later + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/src/tag_list.cpp b/src/tag_list.cpp index 1650e602e3..f72f86bb24 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag +// +// SPDX-License-Identifier: LGPL-3.0-or-later + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/src/tag_string.cpp b/src/tag_string.cpp index 30347818bd..e630b1dfc4 100644 --- a/src/tag_string.cpp +++ b/src/tag_string.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag +// +// SPDX-License-Identifier: LGPL-3.0-or-later + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/src/text/json_formatter.cpp b/src/text/json_formatter.cpp index 8a6282554e..efa807f58c 100644 --- a/src/text/json_formatter.cpp +++ b/src/text/json_formatter.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag +// +// SPDX-License-Identifier: LGPL-3.0-or-later + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/src/value.cpp b/src/value.cpp index 8376dc9b9e..56c44053a1 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag +// +// SPDX-License-Identifier: LGPL-3.0-or-later + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/src/value_initializer.cpp b/src/value_initializer.cpp index 3735bfdf09..b0d0d3d747 100644 --- a/src/value_initializer.cpp +++ b/src/value_initializer.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag +// +// SPDX-License-Identifier: LGPL-3.0-or-later + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5f6e241f93..234b0cf9d2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2013, 2015 ljfa-ag +# +# SPDX-License-Identifier: LGPL-3.0-or-later + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") if(CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64 OR CMAKE_SYSTEM_PROCESSOR STREQUAL amd64) set(OBJCOPY_TARGET "elf64-x86-64") diff --git a/test/data.h b/test/data.h index b6995190c5..32aca4542d 100644 --- a/test/data.h +++ b/test/data.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + #pragma once #include diff --git a/test/endian_str_test.h b/test/endian_str_test.h index 6dfba9fc27..386ba60b57 100644 --- a/test/endian_str_test.h +++ b/test/endian_str_test.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/test/format_test.cpp b/test/format_test.cpp index 87f7b21d72..559af11299 100644 --- a/test/format_test.cpp +++ b/test/format_test.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag +// +// SPDX-License-Identifier: LGPL-3.0-or-later + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/test/nbttest.h b/test/nbttest.h index e3e16c548f..4d7f2cb5de 100644 --- a/test/nbttest.h +++ b/test/nbttest.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/test/read_test.h b/test/read_test.h index 75ddbd5594..d8750f5cb6 100644 --- a/test/read_test.h +++ b/test/read_test.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/test/testfiles/bigtest.nbt.license b/test/testfiles/bigtest.nbt.license new file mode 100644 index 0000000000..6a1a9dd40b --- /dev/null +++ b/test/testfiles/bigtest.nbt.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + +SPDX-License-Identifier: LGPL-3.0-or-later diff --git a/test/testfiles/bigtest.zlib.license b/test/testfiles/bigtest.zlib.license new file mode 100644 index 0000000000..6a1a9dd40b --- /dev/null +++ b/test/testfiles/bigtest.zlib.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + +SPDX-License-Identifier: LGPL-3.0-or-later diff --git a/test/testfiles/bigtest_corrupt.nbt.license b/test/testfiles/bigtest_corrupt.nbt.license new file mode 100644 index 0000000000..6a1a9dd40b --- /dev/null +++ b/test/testfiles/bigtest_corrupt.nbt.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + +SPDX-License-Identifier: LGPL-3.0-or-later diff --git a/test/testfiles/bigtest_eof.nbt.license b/test/testfiles/bigtest_eof.nbt.license new file mode 100644 index 0000000000..6a1a9dd40b --- /dev/null +++ b/test/testfiles/bigtest_eof.nbt.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + +SPDX-License-Identifier: LGPL-3.0-or-later diff --git a/test/testfiles/bigtest_uncompr.license b/test/testfiles/bigtest_uncompr.license new file mode 100644 index 0000000000..6a1a9dd40b --- /dev/null +++ b/test/testfiles/bigtest_uncompr.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + +SPDX-License-Identifier: LGPL-3.0-or-later diff --git a/test/testfiles/errortest_eof1.license b/test/testfiles/errortest_eof1.license new file mode 100644 index 0000000000..6a1a9dd40b --- /dev/null +++ b/test/testfiles/errortest_eof1.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + +SPDX-License-Identifier: LGPL-3.0-or-later diff --git a/test/testfiles/errortest_eof2.license b/test/testfiles/errortest_eof2.license new file mode 100644 index 0000000000..6a1a9dd40b --- /dev/null +++ b/test/testfiles/errortest_eof2.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + +SPDX-License-Identifier: LGPL-3.0-or-later diff --git a/test/testfiles/errortest_neg_length.license b/test/testfiles/errortest_neg_length.license new file mode 100644 index 0000000000..6a1a9dd40b --- /dev/null +++ b/test/testfiles/errortest_neg_length.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + +SPDX-License-Identifier: LGPL-3.0-or-later diff --git a/test/testfiles/errortest_noend.license b/test/testfiles/errortest_noend.license new file mode 100644 index 0000000000..6a1a9dd40b --- /dev/null +++ b/test/testfiles/errortest_noend.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + +SPDX-License-Identifier: LGPL-3.0-or-later diff --git a/test/testfiles/littletest_uncompr.license b/test/testfiles/littletest_uncompr.license new file mode 100644 index 0000000000..6a1a9dd40b --- /dev/null +++ b/test/testfiles/littletest_uncompr.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + +SPDX-License-Identifier: LGPL-3.0-or-later diff --git a/test/testfiles/toplevel_string.license b/test/testfiles/toplevel_string.license new file mode 100644 index 0000000000..6a1a9dd40b --- /dev/null +++ b/test/testfiles/toplevel_string.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + +SPDX-License-Identifier: LGPL-3.0-or-later diff --git a/test/testfiles/trailing_data.zlib.license b/test/testfiles/trailing_data.zlib.license new file mode 100644 index 0000000000..6a1a9dd40b --- /dev/null +++ b/test/testfiles/trailing_data.zlib.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + +SPDX-License-Identifier: LGPL-3.0-or-later diff --git a/test/write_test.h b/test/write_test.h index 8b16b2a779..376eafa59d 100644 --- a/test/write_test.h +++ b/test/write_test.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h index 26f86e0538..67a608e2ad 100644 --- a/test/zlibstream_test.h +++ b/test/zlibstream_test.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2013, 2015 ljfa-ag + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + /* * libnbt++ - A library for the Minecraft Named Binary Tag format. * Copyright (C) 2013, 2015 ljfa-ag -- cgit 0.0.5-2-1-g0f52 From 59ea781f5d45b24cf3819a44902cacf297785d84 Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Wed, 25 Feb 2026 15:39:02 +0500 Subject: change(tag_list::ctor): rename "type" to "content_type" Fixes compiler warning caused by `type` constructor parameter shadowing `type` static constexpr value Signed-off-by: Octol1ttle --- include/tag_list.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/tag_list.h b/include/tag_list.h index e2c9b7bb7c..07b68c25fc 100644 --- a/include/tag_list.h +++ b/include/tag_list.h @@ -67,7 +67,7 @@ public: tag_list(): tag_list(tag_type::Null) {} ///Constructs an empty list with the given content type - explicit tag_list(tag_type type): el_type_(type) {} + explicit tag_list(tag_type content_type): el_type_(content_type) {} ///Constructs a list with the given contents tag_list(std::initializer_list init); -- cgit 0.0.5-2-1-g0f52 From edbbe8dcfd30fcfe84f6b62240e22dbf9138677c Mon Sep 17 00:00:00 2001 From: YongDo-Hyun Date: Wed, 26 Nov 2025 20:10:42 +0300 Subject: feat: add local test executable and improve JSON string escaping - Added option to build a local test executable for value assignments. - Enhanced JSON string formatting by escaping special characters. - Updated README with build instructions and prerequisites. - Modified .gitignore to include .vscode directory. - Added file read/write tests in format_test.cpp. - Refactored value assignment logic to reduce code duplication. Signed-off-by: YongDo-Hyun --- .gitignore | 1 + CMakeLists.txt | 2 + README.md | 67 +++++++++++++++++ src/io/ozlibstream.cpp | 17 ++++- src/text/json_formatter.cpp | 29 ++++++- src/value.cpp | 180 +++++++++++++++++--------------------------- test/CMakeLists.txt | 9 +++ test/format_test.cpp | 21 +++++- test/test_value.cpp | 34 +++++++++ 9 files changed, 243 insertions(+), 117 deletions(-) create mode 100644 test/test_value.cpp diff --git a/.gitignore b/.gitignore index bbd5e827cb..a5e8264be3 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ /obj /build /doxygen +.vscode \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e3486c400..cb3bff5baa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,8 @@ endif() add_library(${NBT_NAME} ${NBT_SOURCES}) target_include_directories(${NBT_NAME} PUBLIC include ${CMAKE_CURRENT_BINARY_DIR}) +# Local developer tests are built in the test subdirectory now + # Install it if(DEFINED NBT_DEST_DIR) install( diff --git a/README.md b/README.md index 975ef3c1ec..fdc96df533 100644 --- a/README.md +++ b/README.md @@ -16,3 +16,70 @@ libnbt++2 is a remake of the old libnbt++ library with the goal of making it more easily usable and fixing some problems. The old libnbt++ especially suffered from a very convoluted syntax and boilerplate code needed to work with NBT data. + +## Building + +This project uses CMake for building. Ensure you have CMake installed. + +### Prerequisites +- C++11 compatible compiler +- CMake 3.15 or later +- ZLIB (optional, for compressed NBT support) + +### Build Steps +1. Clone the repository: + ``` + git clone https://github.com/PrismLauncher/libnbtplusplus.git + cd libnbtplusplus + ``` + +2. Create a build directory: + ``` + mkdir build + cd build + ``` + +3. Configure with CMake: + ``` + cmake .. + ``` + Options: + - `NBT_BUILD_SHARED=OFF` (default): Build static library + - `NBT_USE_ZLIB=ON` (default): Enable zlib support + - `NBT_BUILD_TESTS=ON` (default): Build tests + +4. Build: + ``` + cmake --build . + ``` + +5. Install (optional): + ``` + cmake --install . + ``` + +## Usage + +Include the headers and link against the library. + +### Example +```cpp +#include +#include +#include + +int main() { + // Read an NBT file + std::ifstream file("example.nbt", std::ios::binary); + nbt::tag_compound root = nbt::io::read_compound(file).first; + + // Access data + std::cout << root["some_key"].as() << std::endl; + + return 0; +} +``` + +## License + +This project is licensed under the GNU General Public License v3.0. See the [COPYING](COPYING) file for details. diff --git a/src/io/ozlibstream.cpp b/src/io/ozlibstream.cpp index cf0f505394..b936219787 100644 --- a/src/io/ozlibstream.cpp +++ b/src/io/ozlibstream.cpp @@ -101,9 +101,20 @@ void ozlibstream::close() } catch(...) { - setstate(badbit); //FIXME: This will throw the wrong type of exception - //but there's no good way of setting the badbit - //without causing an exception when exceptions is set + // Setting the stream state while exceptions are enabled may cause + // `setstate` to throw an `ios_base::failure` which would replace + // the original exception. Temporarily disable exceptions on this + // stream, set the `badbit`, then restore the exception mask. + std::ios_base::iostate old_ex = exceptions(); + try { + exceptions(std::ios_base::goodbit); + setstate(std::ios_base::badbit); + } + catch(...) { + // If anything unexpected happens while setting state, swallow + // it — we don't want this to throw here. + } + exceptions(old_ex); } } diff --git a/src/text/json_formatter.cpp b/src/text/json_formatter.cpp index efa807f58c..bd882c15d2 100644 --- a/src/text/json_formatter.cpp +++ b/src/text/json_formatter.cpp @@ -71,7 +71,11 @@ namespace //anonymous { os << "[" << ba.size() << " bytes]"; } void visit(const tag_string& s) override - { os << '"' << s.get() << '"'; } //TODO: escape special characters + { + os << '"'; + write_escaped_string(s.get()); + os << '"'; + } void visit(const tag_list& l) override { @@ -198,6 +202,29 @@ namespace //anonymous { os << "null"; } + + void write_escaped_string(const std::string& str) + { + for (char c : str) { + switch (c) { + case '"': os << "\\\""; break; + case '\\': os << "\\\\"; break; + case '\b': os << "\\b"; break; + case '\f': os << "\\f"; break; + case '\n': os << "\\n"; break; + case '\r': os << "\\r"; break; + case '\t': os << "\\t"; break; + default: + if (static_cast(c) < 32 || c == 127) { + // Control characters, escape as \u00XX + os << "\\u00" << std::hex << std::setw(2) << std::setfill('0') << static_cast(static_cast(c)); + } else { + os << c; + } + break; + } + } + } }; } diff --git a/src/value.cpp b/src/value.cpp index 56c44053a1..8c16da6e23 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -24,6 +24,10 @@ #include "value.h" #include "nbt_tags.h" #include +#include +#include +#include +#include namespace nbt { @@ -60,145 +64,99 @@ void value::set(tag&& t) } //Primitive assignment -//FIXME: Make this less copypaste! -value& value::operator=(int8_t val) +namespace // helper functions local to this translation unit { - if(!tag_) - set(tag_byte(val)); - else switch(tag_->get_type()) + template + void assign_numeric_impl(std::unique_ptr& tag_ptr, T val, + tag_type default_type) { - case tag_type::Byte: - static_cast(*tag_).set(val); - break; - case tag_type::Short: - static_cast(*tag_).set(val); - break; - case tag_type::Int: - static_cast(*tag_).set(val); - break; - case tag_type::Long: - static_cast(*tag_).set(val); - break; - case tag_type::Float: - static_cast(*tag_).set(val); - break; - case tag_type::Double: - static_cast(*tag_).set(val); - break; - - default: - throw std::bad_cast(); + using nbt::tag_type; + if(!tag_ptr) + { + switch(default_type) + { + case tag_type::Byte: tag_ptr.reset(new tag_byte(static_cast(val))); break; + case tag_type::Short: tag_ptr.reset(new tag_short(static_cast(val))); break; + case tag_type::Int: tag_ptr.reset(new tag_int(static_cast(val))); break; + case tag_type::Long: tag_ptr.reset(new tag_long(static_cast(val))); break; + case tag_type::Float: tag_ptr.reset(new tag_float(static_cast(val))); break; + case tag_type::Double: tag_ptr.reset(new tag_double(static_cast(val))); break; + default: throw std::invalid_argument("Invalid default_type"); + } + return; + } + + // Determine the incoming tag type for T + auto incoming_type = detail::get_primitive_type::value; + + // If the existing tag is of a narrower type than the incoming type, + // replace it with a new tag of the incoming type so the value is not + // truncated. Otherwise set the existing tag (possibly narrowing). + auto existing_type = tag_ptr->get_type(); + + if(static_cast(existing_type) < static_cast(incoming_type)) + { + // replace with a new, wider tag that preserves the value + switch(incoming_type) + { + case tag_type::Byte: tag_ptr.reset(new tag_byte(static_cast(val))); break; + case tag_type::Short: tag_ptr.reset(new tag_short(static_cast(val))); break; + case tag_type::Int: tag_ptr.reset(new tag_int(static_cast(val))); break; + case tag_type::Long: tag_ptr.reset(new tag_long(static_cast(val))); break; + case tag_type::Float: tag_ptr.reset(new tag_float(static_cast(val))); break; + case tag_type::Double: tag_ptr.reset(new tag_double(static_cast(val))); break; + default: throw std::bad_cast(); + } + return; + } + + // Existing type is same or wider: write into the existing tag (may narrow) + switch(existing_type) + { + case tag_type::Byte: static_cast(*tag_ptr).set(static_cast(val)); break; + case tag_type::Short: static_cast(*tag_ptr).set(static_cast(val)); break; + case tag_type::Int: static_cast(*tag_ptr).set(static_cast(val)); break; + case tag_type::Long: static_cast(*tag_ptr).set(static_cast(val)); break; + case tag_type::Float: static_cast(*tag_ptr).set(static_cast(val)); break; + case tag_type::Double: static_cast(*tag_ptr).set(static_cast(val)); break; + default: throw std::bad_cast(); + } } +} + +value& value::operator=(int8_t val) +{ + assign_numeric_impl(tag_, val, tag_type::Byte); return *this; } value& value::operator=(int16_t val) { - if(!tag_) - set(tag_short(val)); - else switch(tag_->get_type()) - { - case tag_type::Short: - static_cast(*tag_).set(val); - break; - case tag_type::Int: - static_cast(*tag_).set(val); - break; - case tag_type::Long: - static_cast(*tag_).set(val); - break; - case tag_type::Float: - static_cast(*tag_).set(val); - break; - case tag_type::Double: - static_cast(*tag_).set(val); - break; - - default: - throw std::bad_cast(); - } + assign_numeric_impl(tag_, val, tag_type::Short); return *this; } value& value::operator=(int32_t val) { - if(!tag_) - set(tag_int(val)); - else switch(tag_->get_type()) - { - case tag_type::Int: - static_cast(*tag_).set(val); - break; - case tag_type::Long: - static_cast(*tag_).set(val); - break; - case tag_type::Float: - static_cast(*tag_).set(val); - break; - case tag_type::Double: - static_cast(*tag_).set(val); - break; - - default: - throw std::bad_cast(); - } + assign_numeric_impl(tag_, val, tag_type::Int); return *this; } value& value::operator=(int64_t val) { - if(!tag_) - set(tag_long(val)); - else switch(tag_->get_type()) - { - case tag_type::Long: - static_cast(*tag_).set(val); - break; - case tag_type::Float: - static_cast(*tag_).set(val); - break; - case tag_type::Double: - static_cast(*tag_).set(val); - break; - - default: - throw std::bad_cast(); - } + assign_numeric_impl(tag_, val, tag_type::Long); return *this; } value& value::operator=(float val) { - if(!tag_) - set(tag_float(val)); - else switch(tag_->get_type()) - { - case tag_type::Float: - static_cast(*tag_).set(val); - break; - case tag_type::Double: - static_cast(*tag_).set(val); - break; - - default: - throw std::bad_cast(); - } + assign_numeric_impl(tag_, val, tag_type::Float); return *this; } value& value::operator=(double val) { - if(!tag_) - set(tag_double(val)); - else switch(tag_->get_type()) - { - case tag_type::Double: - static_cast(*tag_).set(val); - break; - - default: - throw std::bad_cast(); - } + assign_numeric_impl(tag_, val, tag_type::Double); return *this; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 234b0cf9d2..41549842cb 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -107,3 +107,12 @@ add_executable(format_test format_test.cpp) target_link_libraries(format_test ${NBT_NAME}) add_test(format_test format_test) stop_warnings(format_test) + +# Optional local test executable to verify value assignments (developer helper) +option(NBT_BUILD_LOCAL_TEST "Build a small local test executable for value assignments" ON) +if(NBT_BUILD_LOCAL_TEST) + add_executable(test_value test_value.cpp) + target_link_libraries(test_value ${NBT_NAME}) + add_test(test_value test_value) + stop_warnings(test_value) +endif() diff --git a/test/format_test.cpp b/test/format_test.cpp index 559af11299..1a689ed3f2 100644 --- a/test/format_test.cpp +++ b/test/format_test.cpp @@ -22,7 +22,8 @@ * along with libnbt++. If not, see . */ //#include "text/json_formatter.h" -//#include "io/stream_reader.h" +#include "io/stream_reader.h" +#include "io/stream_writer.h" #include #include #include @@ -32,7 +33,7 @@ using namespace nbt; int main() { - //TODO: Write that into a file + // Write that into a file and read back for testing tag_compound comp{ {"byte", tag_byte(-128)}, {"short", tag_short(-32768)}, @@ -83,4 +84,20 @@ int main() std::cout << "----- default operator<<:\n"; std::cout << comp; std::cout << "\n-----" << std::endl; + + // Write to file and read back + { + std::ofstream out("test_output.nbt", std::ios::binary); + nbt::io::write_compound(out, comp); + } + + { + std::ifstream in("test_output.nbt", std::ios::binary); + auto [read_comp, name] = nbt::io::read_compound(in); + std::cout << "----- read back from file:\n"; + std::cout << read_comp; + std::cout << "\n-----" << std::endl; + } + + return 0; } diff --git a/test/test_value.cpp b/test/test_value.cpp new file mode 100644 index 0000000000..009cefe8ef --- /dev/null +++ b/test/test_value.cpp @@ -0,0 +1,34 @@ +#include +#include "nbt_tags.h" +#include "value.h" + +using namespace nbt; + +int main() +{ + try { + value v; + + v = int8_t(-5); + std::cout << "assigned int8_t(-5): as int32=" << int32_t(v) << ", as double=" << double(v) << "\n"; + + v = int16_t(12345); + std::cout << "assigned int16_t(12345): as int32=" << int32_t(v) << ", as double=" << double(v) << "\n"; + + v = int32_t(100000); + std::cout << "assigned int32_t(100000): as int64=" << int64_t(v) << ", as double=" << double(v) << "\n"; + + v = float(3.14f); + std::cout << "assigned float(3.14): as double=" << double(v) << "\n"; + + v = double(2.718281828); + std::cout << "assigned double(2.71828): as double=" << double(v) << "\n"; + + std::cout << "Test finished OK\n"; + } + catch(const std::exception& e) { + std::cerr << "Exception: " << e.what() << "\n"; + return 1; + } + return 0; +} -- cgit 0.0.5-2-1-g0f52 From a89a6eac665c1b53d2c511d2e8ccb40e3a6949c4 Mon Sep 17 00:00:00 2001 From: YongDo-Hyun Date: Fri, 26 Dec 2025 19:31:38 +0300 Subject: fix: ensure .vscode directory is ignored correctly in .gitignore Signed-off-by: YongDo-Hyun --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a5e8264be3..d5ef690155 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,4 @@ /obj /build /doxygen -.vscode \ No newline at end of file +.vscode/ \ No newline at end of file -- cgit 0.0.5-2-1-g0f52 From df0519bea5390ac8f3d9a0e168e2f799bd068985 Mon Sep 17 00:00:00 2001 From: YongDo-Hyun Date: Sat, 27 Dec 2025 01:06:26 +0300 Subject: feat: enhance error handling in ozlibstream and improve JSON escaping in json_formatter; add make_numeric_tag for cleaner tag creation Signed-off-by: YongDo-Hyun --- src/io/ozlibstream.cpp | 2 +- src/text/json_formatter.cpp | 2 +- src/value.cpp | 41 +++++++++++++++++++++-------------------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/io/ozlibstream.cpp b/src/io/ozlibstream.cpp index b936219787..5f479f83f8 100644 --- a/src/io/ozlibstream.cpp +++ b/src/io/ozlibstream.cpp @@ -95,6 +95,7 @@ int deflate_streambuf::sync() void ozlibstream::close() { + std::ios_base::iostate old_ex = exceptions(); try { buf.close(); @@ -105,7 +106,6 @@ void ozlibstream::close() // `setstate` to throw an `ios_base::failure` which would replace // the original exception. Temporarily disable exceptions on this // stream, set the `badbit`, then restore the exception mask. - std::ios_base::iostate old_ex = exceptions(); try { exceptions(std::ios_base::goodbit); setstate(std::ios_base::badbit); diff --git a/src/text/json_formatter.cpp b/src/text/json_formatter.cpp index bd882c15d2..06a9c54f60 100644 --- a/src/text/json_formatter.cpp +++ b/src/text/json_formatter.cpp @@ -217,7 +217,7 @@ namespace //anonymous default: if (static_cast(c) < 32 || c == 127) { // Control characters, escape as \u00XX - os << "\\u00" << std::hex << std::setw(2) << std::setfill('0') << static_cast(static_cast(c)); + os << "\\u00" << std::hex << std::setw(2) << std::setfill('0') << static_cast(c); } else { os << c; } diff --git a/src/value.cpp b/src/value.cpp index 8c16da6e23..273049baf9 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -66,6 +66,21 @@ void value::set(tag&& t) //Primitive assignment namespace // helper functions local to this translation unit { + template + std::unique_ptr make_numeric_tag(tag_type type, T val) + { + switch(type) + { + case tag_type::Byte: return std::unique_ptr(new tag_byte(val)); + case tag_type::Short: return std::unique_ptr(new tag_short(val)); + case tag_type::Int: return std::unique_ptr(new tag_int(val)); + case tag_type::Long: return std::unique_ptr(new tag_long(val)); + case tag_type::Float: return std::unique_ptr(new tag_float(val)); + case tag_type::Double: return std::unique_ptr(new tag_double(val)); + default: return nullptr; + } + } + template void assign_numeric_impl(std::unique_ptr& tag_ptr, T val, tag_type default_type) @@ -73,16 +88,9 @@ namespace // helper functions local to this translation unit using nbt::tag_type; if(!tag_ptr) { - switch(default_type) - { - case tag_type::Byte: tag_ptr.reset(new tag_byte(static_cast(val))); break; - case tag_type::Short: tag_ptr.reset(new tag_short(static_cast(val))); break; - case tag_type::Int: tag_ptr.reset(new tag_int(static_cast(val))); break; - case tag_type::Long: tag_ptr.reset(new tag_long(static_cast(val))); break; - case tag_type::Float: tag_ptr.reset(new tag_float(static_cast(val))); break; - case tag_type::Double: tag_ptr.reset(new tag_double(static_cast(val))); break; - default: throw std::invalid_argument("Invalid default_type"); - } + tag_ptr = make_numeric_tag(default_type, val); + if(!tag_ptr) + throw std::invalid_argument("Invalid default_type"); return; } @@ -97,16 +105,9 @@ namespace // helper functions local to this translation unit if(static_cast(existing_type) < static_cast(incoming_type)) { // replace with a new, wider tag that preserves the value - switch(incoming_type) - { - case tag_type::Byte: tag_ptr.reset(new tag_byte(static_cast(val))); break; - case tag_type::Short: tag_ptr.reset(new tag_short(static_cast(val))); break; - case tag_type::Int: tag_ptr.reset(new tag_int(static_cast(val))); break; - case tag_type::Long: tag_ptr.reset(new tag_long(static_cast(val))); break; - case tag_type::Float: tag_ptr.reset(new tag_float(static_cast(val))); break; - case tag_type::Double: tag_ptr.reset(new tag_double(static_cast(val))); break; - default: throw std::bad_cast(); - } + tag_ptr = make_numeric_tag(incoming_type, val); + if(!tag_ptr) + throw std::bad_cast(); return; } -- cgit 0.0.5-2-1-g0f52 From 7c9c42329e453c0c1e70ec37efcf5375065b3567 Mon Sep 17 00:00:00 2001 From: YongDo-Hyun Date: Sat, 27 Dec 2025 01:16:53 +0300 Subject: fix: prevent widening of stored tag type in value assignment; update test output handling Signed-off-by: YongDo-Hyun --- src/value.cpp | 9 ++------- test/CMakeLists.txt | 1 + test/format_test.cpp | 8 +++++--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/value.cpp b/src/value.cpp index 273049baf9..f07e28d82c 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -98,17 +98,12 @@ namespace // helper functions local to this translation unit auto incoming_type = detail::get_primitive_type::value; // If the existing tag is of a narrower type than the incoming type, - // replace it with a new tag of the incoming type so the value is not - // truncated. Otherwise set the existing tag (possibly narrowing). + // reject the assignment to avoid widening the stored tag type. auto existing_type = tag_ptr->get_type(); if(static_cast(existing_type) < static_cast(incoming_type)) { - // replace with a new, wider tag that preserves the value - tag_ptr = make_numeric_tag(incoming_type, val); - if(!tag_ptr) - throw std::bad_cast(); - return; + throw std::bad_cast(); } // Existing type is same or wider: write into the existing tag (may narrow) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 41549842cb..500b939d51 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -62,6 +62,7 @@ build_data(DATA_OBJECTS trailing_data.zlib ) add_library(NbtTestData STATIC ${DATA_OBJECTS}) +set_property(TARGET NbtTestData PROPERTY LINKER_LANGUAGE CXX) #Specifies that the directory containing the testfiles get copied when the target is built function(use_testfiles target) diff --git a/test/format_test.cpp b/test/format_test.cpp index 1a689ed3f2..ed8d2d1bfc 100644 --- a/test/format_test.cpp +++ b/test/format_test.cpp @@ -87,15 +87,17 @@ int main() // Write to file and read back { + tag_compound file_comp = comp; + file_comp.erase("null"); std::ofstream out("test_output.nbt", std::ios::binary); - nbt::io::write_compound(out, comp); + nbt::io::write_tag("root", file_comp, out); } { std::ifstream in("test_output.nbt", std::ios::binary); - auto [read_comp, name] = nbt::io::read_compound(in); + auto read_pair = nbt::io::read_compound(in); std::cout << "----- read back from file:\n"; - std::cout << read_comp; + std::cout << *read_pair.second; std::cout << "\n-----" << std::endl; } -- cgit 0.0.5-2-1-g0f52 From 37a3f01be6fb37d6cb04ae12216b5d74cef2aca9 Mon Sep 17 00:00:00 2001 From: YongDo-Hyun Date: Sat, 27 Dec 2025 01:17:49 +0300 Subject: fix: reset value before each assignment in test_value.cpp for clarity Signed-off-by: YongDo-Hyun --- test/test_value.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/test_value.cpp b/test/test_value.cpp index 009cefe8ef..6b4729ea59 100644 --- a/test/test_value.cpp +++ b/test/test_value.cpp @@ -12,15 +12,19 @@ int main() v = int8_t(-5); std::cout << "assigned int8_t(-5): as int32=" << int32_t(v) << ", as double=" << double(v) << "\n"; + v = value(); v = int16_t(12345); std::cout << "assigned int16_t(12345): as int32=" << int32_t(v) << ", as double=" << double(v) << "\n"; + v = value(); v = int32_t(100000); std::cout << "assigned int32_t(100000): as int64=" << int64_t(v) << ", as double=" << double(v) << "\n"; + v = value(); v = float(3.14f); std::cout << "assigned float(3.14): as double=" << double(v) << "\n"; + v = value(); v = double(2.718281828); std::cout << "assigned double(2.71828): as double=" << double(v) << "\n"; -- cgit 0.0.5-2-1-g0f52 From b6d496b1e83853cd272a8ffdea273c59bbf4b87c Mon Sep 17 00:00:00 2001 From: YongDo-Hyun Date: Sat, 27 Dec 2025 13:05:30 +0300 Subject: feat: add numeric tag creation methods and corresponding tests for value assignments Signed-off-by: YongDo-Hyun --- include/tag.h | 10 ++++++++++ src/tag.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ src/value.cpp | 19 +------------------ test/CMakeLists.txt | 3 +-- test/test_value.cpp | 38 -------------------------------------- test/test_value.h | 36 ++++++++++++++++++++++++++++++++++++ 6 files changed, 96 insertions(+), 58 deletions(-) delete mode 100644 test/test_value.cpp create mode 100644 test/test_value.h diff --git a/include/tag.h b/include/tag.h index 0cb3c82c95..ac3b49fa3a 100644 --- a/include/tag.h +++ b/include/tag.h @@ -123,6 +123,16 @@ public: * @throw std::invalid_argument if the type is not valid (e.g. End or Null) */ static std::unique_ptr 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 create(tag_type type, int8_t val); + static std::unique_ptr create(tag_type type, int16_t val); + static std::unique_ptr create(tag_type type, int32_t val); + static std::unique_ptr create(tag_type type, int64_t val); + static std::unique_ptr create(tag_type type, float val); + static std::unique_ptr 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); diff --git a/src/tag.cpp b/src/tag.cpp index 0bff2c6440..6175a36293 100644 --- a/src/tag.cpp +++ b/src/tag.cpp @@ -45,6 +45,24 @@ std::unique_ptr tag::clone() && return std::move(*this).move_clone(); } +namespace +{ + template + std::unique_ptr create_numeric_tag(tag_type type, T val) + { + switch(type) + { + case tag_type::Byte: return make_unique(static_cast(val)); + case tag_type::Short: return make_unique(static_cast(val)); + case tag_type::Int: return make_unique(static_cast(val)); + case tag_type::Long: return make_unique(static_cast(val)); + case tag_type::Float: return make_unique(static_cast(val)); + case tag_type::Double: return make_unique(static_cast(val)); + default: throw std::invalid_argument("Invalid numeric tag type"); + } + } +} + std::unique_ptr tag::create(tag_type type) { switch(type) @@ -66,6 +84,36 @@ std::unique_ptr tag::create(tag_type type) } } +std::unique_ptr tag::create(tag_type type, int8_t val) +{ + return create_numeric_tag(type, val); +} + +std::unique_ptr tag::create(tag_type type, int16_t val) +{ + return create_numeric_tag(type, val); +} + +std::unique_ptr tag::create(tag_type type, int32_t val) +{ + return create_numeric_tag(type, val); +} + +std::unique_ptr tag::create(tag_type type, int64_t val) +{ + return create_numeric_tag(type, val); +} + +std::unique_ptr tag::create(tag_type type, float val) +{ + return create_numeric_tag(type, val); +} + +std::unique_ptr tag::create(tag_type type, double val) +{ + return create_numeric_tag(type, val); +} + bool operator==(const tag& lhs, const tag& rhs) { if(typeid(lhs) != typeid(rhs)) diff --git a/src/value.cpp b/src/value.cpp index f07e28d82c..bf0ffcad78 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -66,21 +66,6 @@ void value::set(tag&& t) //Primitive assignment namespace // helper functions local to this translation unit { - template - std::unique_ptr make_numeric_tag(tag_type type, T val) - { - switch(type) - { - case tag_type::Byte: return std::unique_ptr(new tag_byte(val)); - case tag_type::Short: return std::unique_ptr(new tag_short(val)); - case tag_type::Int: return std::unique_ptr(new tag_int(val)); - case tag_type::Long: return std::unique_ptr(new tag_long(val)); - case tag_type::Float: return std::unique_ptr(new tag_float(val)); - case tag_type::Double: return std::unique_ptr(new tag_double(val)); - default: return nullptr; - } - } - template void assign_numeric_impl(std::unique_ptr& tag_ptr, T val, tag_type default_type) @@ -88,9 +73,7 @@ namespace // helper functions local to this translation unit using nbt::tag_type; if(!tag_ptr) { - tag_ptr = make_numeric_tag(default_type, val); - if(!tag_ptr) - throw std::invalid_argument("Invalid default_type"); + tag_ptr = tag::create(default_type, val); return; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 500b939d51..fab1f58c00 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -112,8 +112,7 @@ stop_warnings(format_test) # Optional local test executable to verify value assignments (developer helper) option(NBT_BUILD_LOCAL_TEST "Build a small local test executable for value assignments" ON) if(NBT_BUILD_LOCAL_TEST) - add_executable(test_value test_value.cpp) + CXXTEST_ADD_TEST(test_value test_value.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_value.h) target_link_libraries(test_value ${NBT_NAME}) - add_test(test_value test_value) stop_warnings(test_value) endif() diff --git a/test/test_value.cpp b/test/test_value.cpp deleted file mode 100644 index 6b4729ea59..0000000000 --- a/test/test_value.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include "nbt_tags.h" -#include "value.h" - -using namespace nbt; - -int main() -{ - try { - value v; - - v = int8_t(-5); - std::cout << "assigned int8_t(-5): as int32=" << int32_t(v) << ", as double=" << double(v) << "\n"; - - v = value(); - v = int16_t(12345); - std::cout << "assigned int16_t(12345): as int32=" << int32_t(v) << ", as double=" << double(v) << "\n"; - - v = value(); - v = int32_t(100000); - std::cout << "assigned int32_t(100000): as int64=" << int64_t(v) << ", as double=" << double(v) << "\n"; - - v = value(); - v = float(3.14f); - std::cout << "assigned float(3.14): as double=" << double(v) << "\n"; - - v = value(); - v = double(2.718281828); - std::cout << "assigned double(2.71828): as double=" << double(v) << "\n"; - - std::cout << "Test finished OK\n"; - } - catch(const std::exception& e) { - std::cerr << "Exception: " << e.what() << "\n"; - return 1; - } - return 0; -} diff --git a/test/test_value.h b/test/test_value.h new file mode 100644 index 0000000000..db93ffdf7a --- /dev/null +++ b/test/test_value.h @@ -0,0 +1,36 @@ +#include +#include +#include "value.h" + +using namespace nbt; + +class value_assignment_test : public CxxTest::TestSuite +{ +public: + void test_numeric_assignments() + { + value v; + + v = int8_t(-5); + TS_ASSERT_EQUALS(int32_t(v), int32_t(-5)); + TS_ASSERT_EQUALS(double(v), static_cast(int8_t(-5))); + + v = value(); + v = int16_t(12345); + TS_ASSERT_EQUALS(int32_t(v), int32_t(12345)); + TS_ASSERT_EQUALS(double(v), static_cast(int16_t(12345))); + + v = value(); + v = int32_t(100000); + TS_ASSERT_EQUALS(int64_t(v), int64_t(100000)); + TS_ASSERT_EQUALS(double(v), static_cast(int32_t(100000))); + + v = value(); + v = float(3.14f); + TS_ASSERT_EQUALS(double(v), static_cast(3.14f)); + + v = value(); + v = double(2.718281828); + TS_ASSERT_EQUALS(double(v), 2.718281828); + } +}; -- cgit 0.0.5-2-1-g0f52 From 53fa78b3dbcddff3bac152c89d69621526bc2639 Mon Sep 17 00:00:00 2001 From: YongDo-Hyun Date: Sat, 27 Dec 2025 13:07:05 +0300 Subject: fix: simplify control character check in JSON formatting Signed-off-by: YongDo-Hyun --- src/text/json_formatter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text/json_formatter.cpp b/src/text/json_formatter.cpp index 06a9c54f60..88f3a7c58e 100644 --- a/src/text/json_formatter.cpp +++ b/src/text/json_formatter.cpp @@ -215,7 +215,7 @@ namespace //anonymous case '\r': os << "\\r"; break; case '\t': os << "\\t"; break; default: - if (static_cast(c) < 32 || c == 127) { + if (c < 32 || c == 127) { // Control characters, escape as \u00XX os << "\\u00" << std::hex << std::setw(2) << std::setfill('0') << static_cast(c); } else { -- cgit 0.0.5-2-1-g0f52 From 7c71181c27c3ccedf8f183b041dd7656e764cdb3 Mon Sep 17 00:00:00 2001 From: YongDo-Hyun Date: Sat, 27 Dec 2025 13:15:25 +0300 Subject: fix: improve exception handling in ozlibstream::close to prevent masking errors Signed-off-by: YongDo-Hyun --- src/io/ozlibstream.cpp | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/io/ozlibstream.cpp b/src/io/ozlibstream.cpp index 5f479f83f8..ba6187645c 100644 --- a/src/io/ozlibstream.cpp +++ b/src/io/ozlibstream.cpp @@ -95,27 +95,33 @@ int deflate_streambuf::sync() void ozlibstream::close() { + // Capture the exception mask so we can restore it if close() fails. std::ios_base::iostate old_ex = exceptions(); + bool close_failed = false; try { buf.close(); } catch(...) { - // Setting the stream state while exceptions are enabled may cause - // `setstate` to throw an `ios_base::failure` which would replace - // the original exception. Temporarily disable exceptions on this - // stream, set the `badbit`, then restore the exception mask. - try { - exceptions(std::ios_base::goodbit); - setstate(std::ios_base::badbit); - } - catch(...) { - // If anything unexpected happens while setting state, swallow - // it — we don't want this to throw here. - } - exceptions(old_ex); + close_failed = true; + } + if(!close_failed) + return; + + // Setting the stream state while exceptions are enabled may cause + // `setstate` to throw an `ios_base::failure` which would replace + // the original exception. Temporarily disable exceptions on this + // stream, set the `badbit`, then restore the exception mask. + try { + exceptions(std::ios_base::goodbit); + setstate(std::ios_base::badbit); + } + catch(...) { + // If anything unexpected happens while setting state, swallow + // it — we don't want this to throw here. } + exceptions(old_ex); } } -- cgit 0.0.5-2-1-g0f52 From 65f57b875f164ed693cdacd1125a64de7f98a515 Mon Sep 17 00:00:00 2001 From: YongDo-Hyun Date: Sat, 27 Dec 2025 16:24:03 +0300 Subject: fix: correct exception handling in ozlibstream::close method Signed-off-by: YongDo-Hyun --- src/io/ozlibstream.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/io/ozlibstream.cpp b/src/io/ozlibstream.cpp index ba6187645c..d1cb25dac0 100644 --- a/src/io/ozlibstream.cpp +++ b/src/io/ozlibstream.cpp @@ -113,15 +113,14 @@ void ozlibstream::close() // `setstate` to throw an `ios_base::failure` which would replace // the original exception. Temporarily disable exceptions on this // stream, set the `badbit`, then restore the exception mask. + std::ios_base::iostate old_ex = exceptions(); + exceptions(std::ios_base::goodbit); + setstate(std::ios_base::badbit); try { - exceptions(std::ios_base::goodbit); - setstate(std::ios_base::badbit); - } - catch(...) { - // If anything unexpected happens while setting state, swallow - // it — we don't want this to throw here. + exceptions(old_ex); + } catch (...) { + // If anything unexpected happens while restoring the exception mask, + // swallow it — we don't want this to throw here. } - exceptions(old_ex); -} - + } } -- cgit 0.0.5-2-1-g0f52 From bf3635fd75c9afd736c826b1cc89e3d07afad887 Mon Sep 17 00:00:00 2001 From: YongDo-Hyun Date: Sat, 27 Dec 2025 16:38:59 +0300 Subject: fix: improve exception handling in ozlibstream::close method to prevent masking errors Signed-off-by: YongDo-Hyun --- src/io/ozlibstream.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/io/ozlibstream.cpp b/src/io/ozlibstream.cpp index d1cb25dac0..c62f58dace 100644 --- a/src/io/ozlibstream.cpp +++ b/src/io/ozlibstream.cpp @@ -97,30 +97,31 @@ void ozlibstream::close() { // Capture the exception mask so we can restore it if close() fails. std::ios_base::iostate old_ex = exceptions(); - bool close_failed = false; try { buf.close(); + return; } catch(...) { - close_failed = true; + // fall through to mark the stream as bad } - if(!close_failed) - return; // Setting the stream state while exceptions are enabled may cause // `setstate` to throw an `ios_base::failure` which would replace // the original exception. Temporarily disable exceptions on this // stream, set the `badbit`, then restore the exception mask. - std::ios_base::iostate old_ex = exceptions(); exceptions(std::ios_base::goodbit); setstate(std::ios_base::badbit); - try { - exceptions(old_ex); - } catch (...) { - // If anything unexpected happens while restoring the exception mask, - // swallow it — we don't want this to throw here. + try + { + exceptions(old_ex); + } + catch(...) + { + // If anything unexpected happens while restoring the exception mask, + // swallow it — we don't want this to throw here. } - } } + +} // namespace zlib -- cgit 0.0.5-2-1-g0f52 From 6943e7f0a4533eb4e1c0e40854607974f152ec3b Mon Sep 17 00:00:00 2001 From: YongDo-Hyun Date: Sat, 27 Dec 2025 16:41:30 +0300 Subject: fix: simplify exception handling in ozlibstream::close method Signed-off-by: YongDo-Hyun --- src/io/ozlibstream.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/io/ozlibstream.cpp b/src/io/ozlibstream.cpp index c62f58dace..eefe38a91c 100644 --- a/src/io/ozlibstream.cpp +++ b/src/io/ozlibstream.cpp @@ -113,15 +113,7 @@ void ozlibstream::close() // stream, set the `badbit`, then restore the exception mask. exceptions(std::ios_base::goodbit); setstate(std::ios_base::badbit); - try - { - exceptions(old_ex); - } - catch(...) - { - // If anything unexpected happens while restoring the exception mask, - // swallow it — we don't want this to throw here. - } + exceptions(old_ex); } } // namespace zlib -- cgit 0.0.5-2-1-g0f52 From 14b431d7df3ebf9ba672d1758c3d50cf22955194 Mon Sep 17 00:00:00 2001 From: YongDo-Hyun <97219311+YongDo-Hyun@users.noreply.github.com> Date: Sun, 28 Dec 2025 00:06:51 +0300 Subject: feat: Update test/test_value.h Co-authored-by: Alexandru Ionut Tripon Signed-off-by: YongDo-Hyun <97219311+YongDo-Hyun@users.noreply.github.com> --- test/test_value.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_value.h b/test/test_value.h index db93ffdf7a..c1f94a1847 100644 --- a/test/test_value.h +++ b/test/test_value.h @@ -13,7 +13,7 @@ public: v = int8_t(-5); TS_ASSERT_EQUALS(int32_t(v), int32_t(-5)); - TS_ASSERT_EQUALS(double(v), static_cast(int8_t(-5))); + TS_ASSERT_EQUALS(double(v), 5.); v = value(); v = int16_t(12345); -- cgit 0.0.5-2-1-g0f52 From 4999a71561b8a82f3e87cde28b634b843933aefa Mon Sep 17 00:00:00 2001 From: YongDo-Hyun <97219311+YongDo-Hyun@users.noreply.github.com> Date: Sun, 28 Dec 2025 00:09:42 +0300 Subject: feat: Update test/test_value.h Co-authored-by: Alexandru Ionut Tripon Signed-off-by: YongDo-Hyun <97219311+YongDo-Hyun@users.noreply.github.com> --- test/test_value.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_value.h b/test/test_value.h index c1f94a1847..9f666be2aa 100644 --- a/test/test_value.h +++ b/test/test_value.h @@ -18,7 +18,7 @@ public: v = value(); v = int16_t(12345); TS_ASSERT_EQUALS(int32_t(v), int32_t(12345)); - TS_ASSERT_EQUALS(double(v), static_cast(int16_t(12345))); + TS_ASSERT_EQUALS(double(v), 12345.); v = value(); v = int32_t(100000); -- cgit 0.0.5-2-1-g0f52 From 4cf18d33552a10c82ee5cf650074ec42bc73654c Mon Sep 17 00:00:00 2001 From: YongDo-Hyun <97219311+YongDo-Hyun@users.noreply.github.com> Date: Sun, 28 Dec 2025 00:09:54 +0300 Subject: feat: Update test/test_value.h Co-authored-by: Alexandru Ionut Tripon Signed-off-by: YongDo-Hyun <97219311+YongDo-Hyun@users.noreply.github.com> --- test/test_value.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_value.h b/test/test_value.h index 9f666be2aa..6bccb6d487 100644 --- a/test/test_value.h +++ b/test/test_value.h @@ -23,7 +23,7 @@ public: v = value(); v = int32_t(100000); TS_ASSERT_EQUALS(int64_t(v), int64_t(100000)); - TS_ASSERT_EQUALS(double(v), static_cast(int32_t(100000))); + TS_ASSERT_EQUALS(double(v), 100000.); v = value(); v = float(3.14f); -- cgit 0.0.5-2-1-g0f52 From bc952e1298e7aba15a7e97df521b8fc57efd5d73 Mon Sep 17 00:00:00 2001 From: YongDo-Hyun <97219311+YongDo-Hyun@users.noreply.github.com> Date: Sun, 28 Dec 2025 00:10:13 +0300 Subject: feat: Update test/test_value.h Co-authored-by: Alexandru Ionut Tripon Signed-off-by: YongDo-Hyun <97219311+YongDo-Hyun@users.noreply.github.com> --- test/test_value.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_value.h b/test/test_value.h index 6bccb6d487..f63d051d28 100644 --- a/test/test_value.h +++ b/test/test_value.h @@ -27,7 +27,7 @@ public: v = value(); v = float(3.14f); - TS_ASSERT_EQUALS(double(v), static_cast(3.14f)); + TS_ASSERT_EQUALS(double(v), 3.14); v = value(); v = double(2.718281828); -- cgit 0.0.5-2-1-g0f52 From f4d8ea0fa76174843adf7f77ebad0ad17d2377ed Mon Sep 17 00:00:00 2001 From: Mehmet Samet Duman Date: Sun, 29 Mar 2026 15:01:23 +0300 Subject: NOISSUE fix declarations Signed-off-by: Mehmet Samet Duman --- include/tag_primitive.h | 14 +++++++------- src/tag.cpp | 8 ++++++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/include/tag_primitive.h b/include/tag_primitive.h index 938ec51087..a2da0cfab9 100644 --- a/include/tag_primitive.h +++ b/include/tag_primitive.h @@ -83,13 +83,13 @@ typedef tag_primitive tag_long; typedef tag_primitive tag_float; typedef tag_primitive tag_double; -//Explicit instantiations -template class NBT_EXPORT tag_primitive; -template class NBT_EXPORT tag_primitive; -template class NBT_EXPORT tag_primitive; -template class NBT_EXPORT tag_primitive; -template class NBT_EXPORT tag_primitive; -template class NBT_EXPORT tag_primitive; +//Explicit instantiation declarations +extern template class NBT_EXPORT tag_primitive; +extern template class NBT_EXPORT tag_primitive; +extern template class NBT_EXPORT tag_primitive; +extern template class NBT_EXPORT tag_primitive; +extern template class NBT_EXPORT tag_primitive; +extern template class NBT_EXPORT tag_primitive; template void tag_primitive::read_payload(io::stream_reader& reader) diff --git a/src/tag.cpp b/src/tag.cpp index 6175a36293..0446dfd3f8 100644 --- a/src/tag.cpp +++ b/src/tag.cpp @@ -32,6 +32,14 @@ namespace nbt { +//Explicit instantiation definitions for tag_primitive +template class tag_primitive; +template class tag_primitive; +template class tag_primitive; +template class tag_primitive; +template class tag_primitive; +template class tag_primitive; + static_assert(std::numeric_limits::is_iec559 && std::numeric_limits::is_iec559, "The floating point values for NBT must conform to IEC 559/IEEE 754"); -- cgit 0.0.5-2-1-g0f52 From 1a0ffe372f4da8408c5d08a36013536a3396b9e6 Mon Sep 17 00:00:00 2001 From: Mehmet Samet Duman Date: Thu, 2 Apr 2026 17:35:01 +0300 Subject: NOISSUE reformat libnbtplusplus to new MeshMC clang format rules Signed-off-by: Mehmet Samet Duman --- include/crtp_tag.h | 71 ++- include/endian_str.h | 132 +++--- include/io/izlibstream.h | 120 ++--- include/io/ozlibstream.h | 131 +++--- include/io/stream_reader.h | 213 ++++----- include/io/stream_writer.h | 195 ++++---- include/io/zlib_streambuf.h | 63 ++- include/make_unique.h | 14 +- include/nbt_visitor.h | 94 ++-- include/primitive_detail.h | 48 +- include/tag.h | 274 +++++------ include/tag_array.h | 467 ++++++++++--------- include/tag_compound.h | 257 ++++++----- include/tag_list.h | 416 +++++++++-------- include/tag_primitive.h | 170 ++++--- include/tag_string.h | 101 +++-- include/tagfwd.h | 32 +- include/text/json_formatter.h | 31 +- include/value.h | 408 +++++++++-------- include/value_initializer.h | 72 +-- src/endian_str.cpp | 518 ++++++++++----------- src/io/izlibstream.cpp | 123 +++-- src/io/ozlibstream.cpp | 155 +++---- src/io/stream_reader.cpp | 155 +++---- src/io/stream_writer.cpp | 53 +-- src/tag.cpp | 300 ++++++------ src/tag_compound.cpp | 138 +++--- src/tag_list.cpp | 278 +++++++----- src/tag_string.cpp | 29 +- src/text/json_formatter.cpp | 426 +++++++++--------- src/value.cpp | 576 +++++++++++------------ src/value_initializer.cpp | 29 +- test/endian_str_test.h | 278 ++++++------ test/format_test.cpp | 119 ++--- test/nbttest.h | 1002 ++++++++++++++++++++++------------------- test/read_test.h | 448 +++++++++--------- test/test_value.h | 44 +- test/write_test.h | 451 +++++++++---------- test/zlibstream_test.h | 453 ++++++++++--------- 39 files changed, 4695 insertions(+), 4189 deletions(-) diff --git a/include/crtp_tag.h b/include/crtp_tag.h index 28a59f1530..91c629732d 100644 --- a/include/crtp_tag.h +++ b/include/crtp_tag.h @@ -33,38 +33,63 @@ namespace nbt { -namespace detail -{ + namespace detail + { - template - class crtp_tag : public tag - { - public: - //Pure virtual destructor to make the class abstract - virtual ~crtp_tag() noexcept = 0; + template 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 clone() const& override final { return make_unique(sub_this()); } - std::unique_ptr move_clone() && override final { return make_unique(std::move(sub_this())); } + std::unique_ptr clone() const& override final + { + return make_unique(sub_this()); + } + std::unique_ptr move_clone() && override final + { + return make_unique(std::move(sub_this())); + } - tag& assign(tag&& rhs) override final { return sub_this() = dynamic_cast(rhs); } + tag& assign(tag&& rhs) override final + { + return sub_this() = dynamic_cast(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(rhs); } + private: + bool equals(const tag& rhs) const override final + { + return sub_this() == static_cast(rhs); + } - Sub& sub_this() { return static_cast(*this); } - const Sub& sub_this() const { return static_cast(*this); } - }; + Sub& sub_this() + { + return static_cast(*this); + } + const Sub& sub_this() const + { + return static_cast(*this); + } + }; - template - crtp_tag::~crtp_tag() noexcept {} + template crtp_tag::~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 -void read(std::istream& is, T& x, endian e); + /// Reads number from stream in specified endian + template 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 -void write(std::ostream& os, T x, endian e); + /// Writes number to stream in specified endian + template 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 -void read(std::istream& is, T& x, endian e) -{ - if(e == little) - read_little(is, x); - else - read_big(is, x); -} + template void read(std::istream& is, T& x, endian e) + { + if (e == little) + read_little(is, x); + else + read_big(is, x); + } -template -void write(std::ostream& os, T x, endian e) -{ - if(e == little) - write_little(os, x); - else - write_big(os, x); -} + template 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> 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> 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> read_compound(); - - /** - * @brief Reads a named tag from the stream - * @throw input_error on failure - */ - std::pair> read_tag(); - - /** - * @brief Reads a tag of the given type without name from the stream - * @throw input_error on failure - */ - std::unique_ptr 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 - 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 -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> + 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> + 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> + read_compound(); + + /** + * @brief Reads a named tag from the stream + * @throw input_error on failure + */ + std::pair> read_tag(); + + /** + * @brief Reads a tag of the given type without name from the stream + * @throw input_error on failure + */ + std::unique_ptr 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 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 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(tt)); } - - /** - * @brief Writes a binary number to the stream - */ - template - 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 -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(tt)); + } + + /** + * @brief Writes a binary number to the stream + */ + template 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 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 in; - std::vector 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 in; + std::vector 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 -std::unique_ptr make_unique(Args&&... args) -{ - return std::unique_ptr(new T(std::forward(args)...)); -} + /// Creates a new object of type T and returns a std::unique_ptr to it + template + std::unique_ptr make_unique(Args&&... args) + { + return std::unique_ptr(new T(std::forward(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 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 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 : public std::integral_constant {}; - template<> struct get_primitive_type : public std::integral_constant {}; - template<> struct get_primitive_type : public std::integral_constant {}; - template<> struct get_primitive_type : public std::integral_constant {}; - template<> struct get_primitive_type : public std::integral_constant {}; - template<> struct get_primitive_type : public std::integral_constant {}; -} + template <> + struct get_primitive_type + : public std::integral_constant { + }; + template <> + struct get_primitive_type + : public std::integral_constant { + }; + template <> + struct get_primitive_type + : public std::integral_constant { + }; + template <> + struct get_primitive_type + : public std::integral_constant { + }; + template <> + struct get_primitive_type + : public std::integral_constant { + }; + template <> + struct get_primitive_type + : public std::integral_constant { + }; + } // 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 clone() const& = 0; - virtual std::unique_ptr move_clone() && = 0; - std::unique_ptr 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 - T& as(); - template - 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 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 create(tag_type type, int8_t val); - static std::unique_ptr create(tag_type type, int16_t val); - static std::unique_ptr create(tag_type type, int32_t val); - static std::unique_ptr create(tag_type type, int64_t val); - static std::unique_ptr create(tag_type type, float val); - static std::unique_ptr 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 -T& tag::as() -{ - static_assert(std::is_base_of::value, "T must be a subclass of tag"); - return dynamic_cast(*this); -} - -template -const T& tag::as() const -{ - static_assert(std::is_base_of::value, "T must be a subclass of tag"); - return dynamic_cast(*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 clone() const& = 0; + virtual std::unique_ptr move_clone() && = 0; + std::unique_ptr 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 T& as(); + template 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 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 create(tag_type type, int8_t val); + static std::unique_ptr create(tag_type type, int16_t val); + static std::unique_ptr create(tag_type type, int32_t val); + static std::unique_ptr create(tag_type type, int64_t val); + static std::unique_ptr create(tag_type type, float val); + static std::unique_ptr 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 T& tag::as() + { + static_assert(std::is_base_of::value, + "T must be a subclass of tag"); + return dynamic_cast(*this); + } + + template const T& tag::as() const + { + static_assert(std::is_base_of::value, + "T must be a subclass of tag"); + return dynamic_cast(*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 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 : public std::integral_constant {}; - template<> struct get_array_type : public std::integral_constant {}; - template<> struct get_array_type : public std::integral_constant {}; -} -///@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 tag_array final : public detail::crtp_tag> -{ -public: - //Iterator types - typedef typename std::vector::iterator iterator; - typedef typename std::vector::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::value; - - ///Constructs an empty array - tag_array() {} - - ///Constructs an array with the given values - tag_array(std::initializer_list init): data(init) {} - tag_array(std::vector&& vec) noexcept: data(std::move(vec)) {} - - ///Returns a reference to the vector that contains the values - std::vector& get() { return data; } - const std::vector& 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 data; -}; - -template bool operator==(const tag_array& lhs, const tag_array& rhs) -{ return lhs.get() == rhs.get(); } -template bool operator!=(const tag_array& lhs, const tag_array& rhs) -{ return !(lhs == rhs); } - -//Slightly different between byte_array and int_array -//Reading -template<> -inline void tag_array::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(data.data()), length); - if(!reader.get_istr()) - throw io::input_error("Error reading contents of tag_byte_array"); -} - -template -inline void tag_array::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::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::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(size())); - writer.get_ostr().write(reinterpret_cast(data.data()), data.size()); -} - -template -inline void tag_array::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(size())); - for(T i: data) - writer.write_num(i); -} - -template<> -inline void tag_array::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(size())); - for(int64_t i: data) - writer.write_num(i); -} - -//Typedefs that should be used instead of the template tag_array. -typedef tag_array tag_byte_array; -typedef tag_array tag_int_array; -typedef tag_array tag_long_array; - -} + ///@cond + namespace detail + { + /// Meta-struct that holds the tag_type value for a specific array type + template 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 + : public std::integral_constant { + }; + template <> + struct get_array_type + : public std::integral_constant { + }; + template <> + struct get_array_type + : public std::integral_constant { + }; + } // 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 tag_array final : public detail::crtp_tag> + { + public: + // Iterator types + typedef typename std::vector::iterator iterator; + typedef typename std::vector::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::value; + + /// Constructs an empty array + tag_array() {} + + /// Constructs an array with the given values + tag_array(std::initializer_list init) : data(init) {} + tag_array(std::vector&& vec) noexcept : data(std::move(vec)) {} + + /// Returns a reference to the vector that contains the values + std::vector& get() + { + return data; + } + const std::vector& 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 data; + }; + + template + bool operator==(const tag_array& lhs, const tag_array& rhs) + { + return lhs.get() == rhs.get(); + } + template + bool operator!=(const tag_array& lhs, const tag_array& rhs) + { + return !(lhs == rhs); + } + + // Slightly different between byte_array and int_array + // Reading + template <> + inline void tag_array::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(data.data()), length); + if (!reader.get_istr()) + throw io::input_error("Error reading contents of tag_byte_array"); + } + + template + inline void tag_array::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::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::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(size())); + writer.get_ostr().write(reinterpret_cast(data.data()), + data.size()); + } + + template + inline void tag_array::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(size())); + for (T i : data) + writer.write_num(i); + } + + template <> + inline void + tag_array::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(size())); + for (int64_t i : data) + writer.write_num(i); + } + + // Typedefs that should be used instead of the template tag_array. + typedef tag_array tag_byte_array; + typedef tag_array tag_int_array; + typedef tag_array 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 -{ - typedef std::map 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> 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 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 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 - std::pair 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 -std::pair tag_compound::emplace(const std::string& key, Args&&... args) -{ - return put(key, value(make_unique(std::forward(args)...))); -} - -} + /// Tag that contains multiple unordered named tags of arbitrary types + class NBT_EXPORT tag_compound final : public detail::crtp_tag + { + typedef std::map 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> + 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 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 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 + std::pair 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 + std::pair + tag_compound::emplace(const std::string& key, Args&&... args) + { + return put(key, value(make_unique(std::forward(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 -{ -public: - //Iterator types - typedef std::vector::iterator iterator; - typedef std::vector::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({3, 4, 5}) @endcode - * @param init list of values from which the elements are constructed - */ - template - static tag_list of(std::initializer_list 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 init); - tag_list(std::initializer_list init); - tag_list(std::initializer_list init); - tag_list(std::initializer_list init); - tag_list(std::initializer_list init); - tag_list(std::initializer_list init); - tag_list(std::initializer_list init); - tag_list(std::initializer_list init); - tag_list(std::initializer_list init); - tag_list(std::initializer_list init); - tag_list(std::initializer_list init); - tag_list(std::initializer_list 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 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 - 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 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 - void init(std::initializer_list il); -}; - -template -tag_list tag_list::of(std::initializer_list il) -{ - tag_list result; - result.init(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 + { + public: + // Iterator types + typedef std::vector::iterator iterator; + typedef std::vector::const_iterator const_iterator; -template -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(std::forward(args)...)); -} - -template -void tag_list::init(std::initializer_list init) -{ - el_type_ = T::type; - tags.reserve(init.size()); - for(const Arg& arg: init) - tags.emplace_back(nbt::make_unique(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({3, 4, 5}) @endcode + * @param init list of values from which the elements are constructed + */ + template static tag_list of(std::initializer_list 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 init); + tag_list(std::initializer_list init); + tag_list(std::initializer_list init); + tag_list(std::initializer_list init); + tag_list(std::initializer_list init); + tag_list(std::initializer_list init); + tag_list(std::initializer_list init); + tag_list(std::initializer_list init); + tag_list(std::initializer_list init); + tag_list(std::initializer_list init); + tag_list(std::initializer_list init); + tag_list(std::initializer_list 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 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 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 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 void init(std::initializer_list il); + }; + + template tag_list tag_list::of(std::initializer_list il) + { + tag_list result; + result.init(il); + return result; + } + + template + 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(std::forward(args)...)); + } + + template + void tag_list::init(std::initializer_list init) + { + el_type_ = T::type; + tags.reserve(init.size()); + for (const Arg& arg : init) + tags.emplace_back(nbt::make_unique(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 tag_primitive final : public detail::crtp_tag> -{ -public: - ///The type of the value - typedef T value_type; - - ///The type of the tag - static constexpr tag_type type = detail::get_primitive_type::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 bool operator==(const tag_primitive& lhs, const tag_primitive& rhs) -{ return lhs.get() == rhs.get(); } -template bool operator!=(const tag_primitive& lhs, const tag_primitive& rhs) -{ return !(lhs == rhs); } - -//Typedefs that should be used instead of the template tag_primitive. -typedef tag_primitive tag_byte; -typedef tag_primitive tag_short; -typedef tag_primitive tag_int; -typedef tag_primitive tag_long; -typedef tag_primitive tag_float; -typedef tag_primitive tag_double; - -//Explicit instantiation declarations -extern template class NBT_EXPORT tag_primitive; -extern template class NBT_EXPORT tag_primitive; -extern template class NBT_EXPORT tag_primitive; -extern template class NBT_EXPORT tag_primitive; -extern template class NBT_EXPORT tag_primitive; -extern template class NBT_EXPORT tag_primitive; - -template -void tag_primitive::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 -void tag_primitive::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 tag_primitive final : public detail::crtp_tag> + { + public: + /// The type of the value + typedef T value_type; + + /// The type of the tag + static constexpr tag_type type = detail::get_primitive_type::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 + bool operator==(const tag_primitive& lhs, const tag_primitive& rhs) + { + return lhs.get() == rhs.get(); + } + template + bool operator!=(const tag_primitive& lhs, const tag_primitive& rhs) + { + return !(lhs == rhs); + } + + // Typedefs that should be used instead of the template tag_primitive. + typedef tag_primitive tag_byte; + typedef tag_primitive tag_short; + typedef tag_primitive tag_int; + typedef tag_primitive tag_long; + typedef tag_primitive tag_float; + typedef tag_primitive tag_double; + + // Explicit instantiation declarations + extern template class NBT_EXPORT tag_primitive; + extern template class NBT_EXPORT tag_primitive; + extern template class NBT_EXPORT tag_primitive; + extern template class NBT_EXPORT tag_primitive; + extern template class NBT_EXPORT tag_primitive; + extern template class NBT_EXPORT tag_primitive; + + template + void tag_primitive::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 + void tag_primitive::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 -{ -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 + { + 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 tag_primitive; -typedef tag_primitive tag_byte; -typedef tag_primitive tag_short; -typedef tag_primitive tag_int; -typedef tag_primitive tag_long; -typedef tag_primitive tag_float; -typedef tag_primitive tag_double; + template class tag_primitive; + typedef tag_primitive tag_byte; + typedef tag_primitive tag_short; + typedef tag_primitive tag_int; + typedef tag_primitive tag_long; + typedef tag_primitive tag_float; + typedef tag_primitive tag_double; -class tag_string; + class tag_string; -template class tag_array; -typedef tag_array tag_byte_array; -typedef tag_array tag_int_array; -typedef tag_array tag_long_array; + template class tag_array; + typedef tag_array tag_byte_array; + typedef tag_array tag_int_array; + typedef tag_array 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. - * 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 - * 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 - * compound["foo"] = 42. 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&& 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 - T& as(); - template - 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 - std::unique_ptr& get_ptr() { return tag_; } - const std::unique_ptr& get_ptr() const { return tag_; } - ///Resets the underlying std::unique_ptr to a different value - void set_ptr(std::unique_ptr&& 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_; -}; - -template -T& value::as() -{ - return tag_->as(); -} - -template -const T& value::as() const -{ - return tag_->as(); -} - -} + /** + * @brief Contains an NBT value of fixed type + * + * This class is a convenience wrapper for @c std::unique_ptr. + * 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 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 + * compound["foo"] = 42. 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&& 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 T& as(); + template 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 + std::unique_ptr& get_ptr() + { + return tag_; + } + const std::unique_ptr& get_ptr() const + { + return tag_; + } + /// Resets the underlying std::unique_ptr to a different value + void set_ptr(std::unique_ptr&& 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_; + }; + + template T& value::as() + { + return tag_->as(); + } + + template const T& value::as() const + { + return tag_->as(); + } + +} // 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&& 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&& t) noexcept + : value(std::move(t)) + { + } + value_initializer(std::nullptr_t) noexcept : value(nullptr) {} + value_initializer(value&& val) noexcept : value(std::move(val)) {} + value_initializer(tag&& t) : value(std::move(t)) {} - value_initializer(int8_t val); - value_initializer(int16_t val); - value_initializer(int32_t val); - value_initializer(int64_t val); - value_initializer(float val); - value_initializer(double val); - value_initializer(const std::string& str); - value_initializer(std::string&& str); - value_initializer(const char* str); -}; + value_initializer(int8_t val); + value_initializer(int16_t val); + value_initializer(int32_t val); + value_initializer(int64_t val); + value_initializer(float val); + value_initializer(double val); + value_initializer(const std::string& str); + value_initializer(std::string&& str); + value_initializer(const char* str); + }; -} +} // namespace nbt #endif // VALUE_INITIALIZER_H_INCLUDED diff --git a/src/endian_str.cpp b/src/endian_str.cpp index c6de84201d..e9553cd9d3 100644 --- a/src/endian_str.cpp +++ b/src/endian_str.cpp @@ -33,256 +33,268 @@ static_assert(sizeof(double) == 8, "Assuming that a double is 8 byte long"); namespace endian { -namespace //anonymous -{ - void pun_int_to_float(float& f, uint32_t i) - { - //Yes we need to do it this way to avoid undefined behavior - memcpy(&f, &i, 4); - } - - uint32_t pun_float_to_int(float f) - { - uint32_t ret; - memcpy(&ret, &f, 4); - return ret; - } - - void pun_int_to_double(double& d, uint64_t i) - { - memcpy(&d, &i, 8); - } - - uint64_t pun_double_to_int(double f) - { - uint64_t ret; - memcpy(&ret, &f, 8); - return ret; - } -} - -//------------------------------------------------------------------------------ - -void read_little(std::istream& is, uint8_t& x) -{ - is.get(reinterpret_cast(x)); -} - -void read_little(std::istream& is, uint16_t& x) -{ - uint8_t tmp[2]; - is.read(reinterpret_cast(tmp), 2); - x = uint16_t(tmp[0]) - | (uint16_t(tmp[1]) << 8); -} - -void read_little(std::istream& is, uint32_t& x) -{ - uint8_t tmp[4]; - is.read(reinterpret_cast(tmp), 4); - x = uint32_t(tmp[0]) - | (uint32_t(tmp[1]) << 8) - | (uint32_t(tmp[2]) << 16) - | (uint32_t(tmp[3]) << 24); -} - -void read_little(std::istream& is, uint64_t& x) -{ - uint8_t tmp[8]; - is.read(reinterpret_cast(tmp), 8); - x = uint64_t(tmp[0]) - | (uint64_t(tmp[1]) << 8) - | (uint64_t(tmp[2]) << 16) - | (uint64_t(tmp[3]) << 24) - | (uint64_t(tmp[4]) << 32) - | (uint64_t(tmp[5]) << 40) - | (uint64_t(tmp[6]) << 48) - | (uint64_t(tmp[7]) << 56); -} - -void read_little(std::istream& is, int8_t & x) { read_little(is, reinterpret_cast(x)); } -void read_little(std::istream& is, int16_t& x) { read_little(is, reinterpret_cast(x)); } -void read_little(std::istream& is, int32_t& x) { read_little(is, reinterpret_cast(x)); } -void read_little(std::istream& is, int64_t& x) { read_little(is, reinterpret_cast(x)); } - -void read_little(std::istream& is, float& x) -{ - uint32_t tmp; - read_little(is, tmp); - pun_int_to_float(x, tmp); -} - -void read_little(std::istream& is, double& x) -{ - uint64_t tmp; - read_little(is, tmp); - pun_int_to_double(x, tmp); -} - -//------------------------------------------------------------------------------ - -void read_big(std::istream& is, uint8_t& x) -{ - is.read(reinterpret_cast(&x), 1); -} - -void read_big(std::istream& is, uint16_t& x) -{ - uint8_t tmp[2]; - is.read(reinterpret_cast(tmp), 2); - x = uint16_t(tmp[1]) - | (uint16_t(tmp[0]) << 8); -} - -void read_big(std::istream& is, uint32_t& x) -{ - uint8_t tmp[4]; - is.read(reinterpret_cast(tmp), 4); - x = uint32_t(tmp[3]) - | (uint32_t(tmp[2]) << 8) - | (uint32_t(tmp[1]) << 16) - | (uint32_t(tmp[0]) << 24); -} - -void read_big(std::istream& is, uint64_t& x) -{ - uint8_t tmp[8]; - is.read(reinterpret_cast(tmp), 8); - x = uint64_t(tmp[7]) - | (uint64_t(tmp[6]) << 8) - | (uint64_t(tmp[5]) << 16) - | (uint64_t(tmp[4]) << 24) - | (uint64_t(tmp[3]) << 32) - | (uint64_t(tmp[2]) << 40) - | (uint64_t(tmp[1]) << 48) - | (uint64_t(tmp[0]) << 56); -} - -void read_big(std::istream& is, int8_t & x) { read_big(is, reinterpret_cast(x)); } -void read_big(std::istream& is, int16_t& x) { read_big(is, reinterpret_cast(x)); } -void read_big(std::istream& is, int32_t& x) { read_big(is, reinterpret_cast(x)); } -void read_big(std::istream& is, int64_t& x) { read_big(is, reinterpret_cast(x)); } - -void read_big(std::istream& is, float& x) -{ - uint32_t tmp; - read_big(is, tmp); - pun_int_to_float(x, tmp); -} - -void read_big(std::istream& is, double& x) -{ - uint64_t tmp; - read_big(is, tmp); - pun_int_to_double(x, tmp); -} - -//------------------------------------------------------------------------------ - -void write_little(std::ostream& os, uint8_t x) -{ - os.put(x); -} - -void write_little(std::ostream& os, uint16_t x) -{ - uint8_t tmp[2] { - uint8_t(x), - uint8_t(x >> 8)}; - os.write(reinterpret_cast(tmp), 2); -} - -void write_little(std::ostream& os, uint32_t x) -{ - uint8_t tmp[4] { - uint8_t(x), - uint8_t(x >> 8), - uint8_t(x >> 16), - uint8_t(x >> 24)}; - os.write(reinterpret_cast(tmp), 4); -} - -void write_little(std::ostream& os, uint64_t x) -{ - uint8_t tmp[8] { - uint8_t(x), - uint8_t(x >> 8), - uint8_t(x >> 16), - uint8_t(x >> 24), - uint8_t(x >> 32), - uint8_t(x >> 40), - uint8_t(x >> 48), - uint8_t(x >> 56)}; - os.write(reinterpret_cast(tmp), 8); -} - -void write_little(std::ostream& os, int8_t x) { write_little(os, static_cast(x)); } -void write_little(std::ostream& os, int16_t x) { write_little(os, static_cast(x)); } -void write_little(std::ostream& os, int32_t x) { write_little(os, static_cast(x)); } -void write_little(std::ostream& os, int64_t x) { write_little(os, static_cast(x)); } - -void write_little(std::ostream& os, float x) -{ - write_little(os, pun_float_to_int(x)); -} - -void write_little(std::ostream& os, double x) -{ - write_little(os, pun_double_to_int(x)); -} - -//------------------------------------------------------------------------------ - -void write_big(std::ostream& os, uint8_t x) -{ - os.put(x); -} - -void write_big(std::ostream& os, uint16_t x) -{ - uint8_t tmp[2] { - uint8_t(x >> 8), - uint8_t(x)}; - os.write(reinterpret_cast(tmp), 2); -} - -void write_big(std::ostream& os, uint32_t x) -{ - uint8_t tmp[4] { - uint8_t(x >> 24), - uint8_t(x >> 16), - uint8_t(x >> 8), - uint8_t(x)}; - os.write(reinterpret_cast(tmp), 4); -} - -void write_big(std::ostream& os, uint64_t x) -{ - uint8_t tmp[8] { - uint8_t(x >> 56), - uint8_t(x >> 48), - uint8_t(x >> 40), - uint8_t(x >> 32), - uint8_t(x >> 24), - uint8_t(x >> 16), - uint8_t(x >> 8), - uint8_t(x)}; - os.write(reinterpret_cast(tmp), 8); -} - -void write_big(std::ostream& os, int8_t x) { write_big(os, static_cast(x)); } -void write_big(std::ostream& os, int16_t x) { write_big(os, static_cast(x)); } -void write_big(std::ostream& os, int32_t x) { write_big(os, static_cast(x)); } -void write_big(std::ostream& os, int64_t x) { write_big(os, static_cast(x)); } - -void write_big(std::ostream& os, float x) -{ - write_big(os, pun_float_to_int(x)); -} - -void write_big(std::ostream& os, double x) -{ - write_big(os, pun_double_to_int(x)); -} - -} + namespace // anonymous + { + void pun_int_to_float(float& f, uint32_t i) + { + // Yes we need to do it this way to avoid undefined behavior + memcpy(&f, &i, 4); + } + + uint32_t pun_float_to_int(float f) + { + uint32_t ret; + memcpy(&ret, &f, 4); + return ret; + } + + void pun_int_to_double(double& d, uint64_t i) + { + memcpy(&d, &i, 8); + } + + uint64_t pun_double_to_int(double f) + { + uint64_t ret; + memcpy(&ret, &f, 8); + return ret; + } + } // namespace + + //------------------------------------------------------------------------------ + + void read_little(std::istream& is, uint8_t& x) + { + is.get(reinterpret_cast(x)); + } + + void read_little(std::istream& is, uint16_t& x) + { + uint8_t tmp[2]; + is.read(reinterpret_cast(tmp), 2); + x = uint16_t(tmp[0]) | (uint16_t(tmp[1]) << 8); + } + + void read_little(std::istream& is, uint32_t& x) + { + uint8_t tmp[4]; + is.read(reinterpret_cast(tmp), 4); + x = uint32_t(tmp[0]) | (uint32_t(tmp[1]) << 8) | + (uint32_t(tmp[2]) << 16) | (uint32_t(tmp[3]) << 24); + } + + void read_little(std::istream& is, uint64_t& x) + { + uint8_t tmp[8]; + is.read(reinterpret_cast(tmp), 8); + x = uint64_t(tmp[0]) | (uint64_t(tmp[1]) << 8) | + (uint64_t(tmp[2]) << 16) | (uint64_t(tmp[3]) << 24) | + (uint64_t(tmp[4]) << 32) | (uint64_t(tmp[5]) << 40) | + (uint64_t(tmp[6]) << 48) | (uint64_t(tmp[7]) << 56); + } + + void read_little(std::istream& is, int8_t& x) + { + read_little(is, reinterpret_cast(x)); + } + void read_little(std::istream& is, int16_t& x) + { + read_little(is, reinterpret_cast(x)); + } + void read_little(std::istream& is, int32_t& x) + { + read_little(is, reinterpret_cast(x)); + } + void read_little(std::istream& is, int64_t& x) + { + read_little(is, reinterpret_cast(x)); + } + + void read_little(std::istream& is, float& x) + { + uint32_t tmp; + read_little(is, tmp); + pun_int_to_float(x, tmp); + } + + void read_little(std::istream& is, double& x) + { + uint64_t tmp; + read_little(is, tmp); + pun_int_to_double(x, tmp); + } + + //------------------------------------------------------------------------------ + + void read_big(std::istream& is, uint8_t& x) + { + is.read(reinterpret_cast(&x), 1); + } + + void read_big(std::istream& is, uint16_t& x) + { + uint8_t tmp[2]; + is.read(reinterpret_cast(tmp), 2); + x = uint16_t(tmp[1]) | (uint16_t(tmp[0]) << 8); + } + + void read_big(std::istream& is, uint32_t& x) + { + uint8_t tmp[4]; + is.read(reinterpret_cast(tmp), 4); + x = uint32_t(tmp[3]) | (uint32_t(tmp[2]) << 8) | + (uint32_t(tmp[1]) << 16) | (uint32_t(tmp[0]) << 24); + } + + void read_big(std::istream& is, uint64_t& x) + { + uint8_t tmp[8]; + is.read(reinterpret_cast(tmp), 8); + x = uint64_t(tmp[7]) | (uint64_t(tmp[6]) << 8) | + (uint64_t(tmp[5]) << 16) | (uint64_t(tmp[4]) << 24) | + (uint64_t(tmp[3]) << 32) | (uint64_t(tmp[2]) << 40) | + (uint64_t(tmp[1]) << 48) | (uint64_t(tmp[0]) << 56); + } + + void read_big(std::istream& is, int8_t& x) + { + read_big(is, reinterpret_cast(x)); + } + void read_big(std::istream& is, int16_t& x) + { + read_big(is, reinterpret_cast(x)); + } + void read_big(std::istream& is, int32_t& x) + { + read_big(is, reinterpret_cast(x)); + } + void read_big(std::istream& is, int64_t& x) + { + read_big(is, reinterpret_cast(x)); + } + + void read_big(std::istream& is, float& x) + { + uint32_t tmp; + read_big(is, tmp); + pun_int_to_float(x, tmp); + } + + void read_big(std::istream& is, double& x) + { + uint64_t tmp; + read_big(is, tmp); + pun_int_to_double(x, tmp); + } + + //------------------------------------------------------------------------------ + + void write_little(std::ostream& os, uint8_t x) + { + os.put(x); + } + + void write_little(std::ostream& os, uint16_t x) + { + uint8_t tmp[2]{uint8_t(x), uint8_t(x >> 8)}; + os.write(reinterpret_cast(tmp), 2); + } + + void write_little(std::ostream& os, uint32_t x) + { + uint8_t tmp[4]{uint8_t(x), uint8_t(x >> 8), uint8_t(x >> 16), + uint8_t(x >> 24)}; + os.write(reinterpret_cast(tmp), 4); + } + + void write_little(std::ostream& os, uint64_t x) + { + uint8_t tmp[8]{uint8_t(x), uint8_t(x >> 8), uint8_t(x >> 16), + uint8_t(x >> 24), uint8_t(x >> 32), uint8_t(x >> 40), + uint8_t(x >> 48), uint8_t(x >> 56)}; + os.write(reinterpret_cast(tmp), 8); + } + + void write_little(std::ostream& os, int8_t x) + { + write_little(os, static_cast(x)); + } + void write_little(std::ostream& os, int16_t x) + { + write_little(os, static_cast(x)); + } + void write_little(std::ostream& os, int32_t x) + { + write_little(os, static_cast(x)); + } + void write_little(std::ostream& os, int64_t x) + { + write_little(os, static_cast(x)); + } + + void write_little(std::ostream& os, float x) + { + write_little(os, pun_float_to_int(x)); + } + + void write_little(std::ostream& os, double x) + { + write_little(os, pun_double_to_int(x)); + } + + //------------------------------------------------------------------------------ + + void write_big(std::ostream& os, uint8_t x) + { + os.put(x); + } + + void write_big(std::ostream& os, uint16_t x) + { + uint8_t tmp[2]{uint8_t(x >> 8), uint8_t(x)}; + os.write(reinterpret_cast(tmp), 2); + } + + void write_big(std::ostream& os, uint32_t x) + { + uint8_t tmp[4]{uint8_t(x >> 24), uint8_t(x >> 16), uint8_t(x >> 8), + uint8_t(x)}; + os.write(reinterpret_cast(tmp), 4); + } + + void write_big(std::ostream& os, uint64_t x) + { + uint8_t tmp[8]{uint8_t(x >> 56), uint8_t(x >> 48), uint8_t(x >> 40), + uint8_t(x >> 32), uint8_t(x >> 24), uint8_t(x >> 16), + uint8_t(x >> 8), uint8_t(x)}; + os.write(reinterpret_cast(tmp), 8); + } + + void write_big(std::ostream& os, int8_t x) + { + write_big(os, static_cast(x)); + } + void write_big(std::ostream& os, int16_t x) + { + write_big(os, static_cast(x)); + } + void write_big(std::ostream& os, int32_t x) + { + write_big(os, static_cast(x)); + } + void write_big(std::ostream& os, int64_t x) + { + write_big(os, static_cast(x)); + } + + void write_big(std::ostream& os, float x) + { + write_big(os, pun_float_to_int(x)); + } + + void write_big(std::ostream& os, double x) + { + write_big(os, pun_double_to_int(x)); + } + +} // namespace endian diff --git a/src/io/izlibstream.cpp b/src/io/izlibstream.cpp index b361971081..9d57545360 100644 --- a/src/io/izlibstream.cpp +++ b/src/io/izlibstream.cpp @@ -27,76 +27,75 @@ namespace zlib { -inflate_streambuf::inflate_streambuf(std::istream& input, size_t bufsize, int window_bits): - zlib_streambuf(bufsize), is(input), stream_end(false) -{ - zstr.next_in = Z_NULL; - zstr.avail_in = 0; - int ret = inflateInit2(&zstr, window_bits); - if(ret != Z_OK) - throw zlib_error(zstr.msg, ret); + inflate_streambuf::inflate_streambuf(std::istream& input, size_t bufsize, + int window_bits) + : zlib_streambuf(bufsize), is(input), stream_end(false) + { + zstr.next_in = Z_NULL; + zstr.avail_in = 0; + int ret = inflateInit2(&zstr, window_bits); + if (ret != Z_OK) + throw zlib_error(zstr.msg, ret); - char* end = out.data() + out.size(); - setg(end, end, end); -} + char* end = out.data() + out.size(); + setg(end, end, end); + } -inflate_streambuf::~inflate_streambuf() noexcept -{ - inflateEnd(&zstr); -} + inflate_streambuf::~inflate_streambuf() noexcept + { + inflateEnd(&zstr); + } -inflate_streambuf::int_type inflate_streambuf::underflow() -{ - if(gptr() < egptr()) - return traits_type::to_int_type(*gptr()); + inflate_streambuf::int_type inflate_streambuf::underflow() + { + if (gptr() < egptr()) + return traits_type::to_int_type(*gptr()); - size_t have; - do - { - //Read if input buffer is empty - if(zstr.avail_in <= 0) - { - is.read(in.data(), in.size()); - if(is.bad()) - throw std::ios_base::failure("Input stream is bad"); - size_t count = is.gcount(); - if(count == 0 && !stream_end) - throw zlib_error("Unexpected end of stream", Z_DATA_ERROR); + size_t have; + do { + // Read if input buffer is empty + if (zstr.avail_in <= 0) { + is.read(in.data(), in.size()); + if (is.bad()) + throw std::ios_base::failure("Input stream is bad"); + size_t count = is.gcount(); + if (count == 0 && !stream_end) + throw zlib_error("Unexpected end of stream", Z_DATA_ERROR); - zstr.next_in = reinterpret_cast(in.data()); - zstr.avail_in = count; - } + zstr.next_in = reinterpret_cast(in.data()); + zstr.avail_in = count; + } - zstr.next_out = reinterpret_cast(out.data()); - zstr.avail_out = out.size(); + zstr.next_out = reinterpret_cast(out.data()); + zstr.avail_out = out.size(); - int ret = inflate(&zstr, Z_NO_FLUSH); - have = out.size() - zstr.avail_out; - switch(ret) - { - case Z_NEED_DICT: - case Z_DATA_ERROR: - throw zlib_error(zstr.msg, ret); + int ret = inflate(&zstr, Z_NO_FLUSH); + have = out.size() - zstr.avail_out; + switch (ret) { + case Z_NEED_DICT: + case Z_DATA_ERROR: + throw zlib_error(zstr.msg, ret); - case Z_MEM_ERROR: - throw std::bad_alloc(); + case Z_MEM_ERROR: + throw std::bad_alloc(); - case Z_STREAM_END: - if(!stream_end) - { - stream_end = true; - //In case we consumed too much, we have to rewind the input stream - is.clear(); - is.seekg(-static_cast(zstr.avail_in), std::ios_base::cur); - } - if(have == 0) - return traits_type::eof(); - break; - } - } while(have == 0); + case Z_STREAM_END: + if (!stream_end) { + stream_end = true; + // In case we consumed too much, we have to rewind the + // input stream + is.clear(); + is.seekg(-static_cast(zstr.avail_in), + std::ios_base::cur); + } + if (have == 0) + return traits_type::eof(); + break; + } + } while (have == 0); - setg(out.data(), out.data(), out.data() + have); - return traits_type::to_int_type(*gptr()); -} + setg(out.data(), out.data(), out.data() + have); + return traits_type::to_int_type(*gptr()); + } -} +} // namespace zlib diff --git a/src/io/ozlibstream.cpp b/src/io/ozlibstream.cpp index eefe38a91c..c8a3e862a1 100644 --- a/src/io/ozlibstream.cpp +++ b/src/io/ozlibstream.cpp @@ -27,93 +27,88 @@ namespace zlib { -deflate_streambuf::deflate_streambuf(std::ostream& output, size_t bufsize, int level, int window_bits, int mem_level, int strategy): - zlib_streambuf(bufsize), os(output) -{ - int ret = deflateInit2(&zstr, level, Z_DEFLATED, window_bits, mem_level, strategy); - if(ret != Z_OK) - throw zlib_error(zstr.msg, ret); + deflate_streambuf::deflate_streambuf(std::ostream& output, size_t bufsize, + int level, int window_bits, + int mem_level, int strategy) + : zlib_streambuf(bufsize), os(output) + { + int ret = deflateInit2(&zstr, level, Z_DEFLATED, window_bits, mem_level, + strategy); + if (ret != Z_OK) + throw zlib_error(zstr.msg, ret); - setp(in.data(), in.data() + in.size()); -} + setp(in.data(), in.data() + in.size()); + } -deflate_streambuf::~deflate_streambuf() noexcept -{ - try - { - close(); - } - catch(...) - { - //ignore as we can't do anything about it - } - deflateEnd(&zstr); -} + deflate_streambuf::~deflate_streambuf() noexcept + { + try { + close(); + } catch (...) { + // ignore as we can't do anything about it + } + deflateEnd(&zstr); + } -void deflate_streambuf::close() -{ - deflate_chunk(Z_FINISH); -} + void deflate_streambuf::close() + { + deflate_chunk(Z_FINISH); + } -void deflate_streambuf::deflate_chunk(int flush) -{ - zstr.next_in = reinterpret_cast(pbase()); - zstr.avail_in = pptr() - pbase(); - do - { - zstr.next_out = reinterpret_cast(out.data()); - zstr.avail_out = out.size(); - int ret = deflate(&zstr, flush); - if(ret != Z_OK && ret != Z_STREAM_END) - { - os.setstate(std::ios_base::failbit); - throw zlib_error(zstr.msg, ret); - } - int have = out.size() - zstr.avail_out; - if(!os.write(out.data(), have)) - throw std::ios_base::failure("Could not write to the output stream"); - } while(zstr.avail_out == 0); - setp(in.data(), in.data() + in.size()); -} + void deflate_streambuf::deflate_chunk(int flush) + { + zstr.next_in = reinterpret_cast(pbase()); + zstr.avail_in = pptr() - pbase(); + do { + zstr.next_out = reinterpret_cast(out.data()); + zstr.avail_out = out.size(); + int ret = deflate(&zstr, flush); + if (ret != Z_OK && ret != Z_STREAM_END) { + os.setstate(std::ios_base::failbit); + throw zlib_error(zstr.msg, ret); + } + int have = out.size() - zstr.avail_out; + if (!os.write(out.data(), have)) + throw std::ios_base::failure( + "Could not write to the output stream"); + } while (zstr.avail_out == 0); + setp(in.data(), in.data() + in.size()); + } -deflate_streambuf::int_type deflate_streambuf::overflow(int_type ch) -{ - deflate_chunk(); - if(ch != traits_type::eof()) - { - *pptr() = ch; - pbump(1); - } - return ch; -} + deflate_streambuf::int_type deflate_streambuf::overflow(int_type ch) + { + deflate_chunk(); + if (ch != traits_type::eof()) { + *pptr() = ch; + pbump(1); + } + return ch; + } -int deflate_streambuf::sync() -{ - deflate_chunk(); - return 0; -} + int deflate_streambuf::sync() + { + deflate_chunk(); + return 0; + } -void ozlibstream::close() -{ - // Capture the exception mask so we can restore it if close() fails. - std::ios_base::iostate old_ex = exceptions(); - try - { - buf.close(); - return; - } - catch(...) - { - // fall through to mark the stream as bad - } + void ozlibstream::close() + { + // Capture the exception mask so we can restore it if close() fails. + std::ios_base::iostate old_ex = exceptions(); + try { + buf.close(); + return; + } catch (...) { + // fall through to mark the stream as bad + } - // Setting the stream state while exceptions are enabled may cause - // `setstate` to throw an `ios_base::failure` which would replace - // the original exception. Temporarily disable exceptions on this - // stream, set the `badbit`, then restore the exception mask. - exceptions(std::ios_base::goodbit); - setstate(std::ios_base::badbit); - exceptions(old_ex); -} + // Setting the stream state while exceptions are enabled may cause + // `setstate` to throw an `ios_base::failure` which would replace + // the original exception. Temporarily disable exceptions on this + // stream, set the `badbit`, then restore the exception mask. + exceptions(std::ios_base::goodbit); + setstate(std::ios_base::badbit); + exceptions(old_ex); + } } // namespace zlib diff --git a/src/io/stream_reader.cpp b/src/io/stream_reader.cpp index 06375ca0d5..43032b1120 100644 --- a/src/io/stream_reader.cpp +++ b/src/io/stream_reader.cpp @@ -28,92 +28,95 @@ namespace nbt { -namespace io -{ + namespace io + { -static constexpr int MAX_DEPTH = 1024; + static constexpr int MAX_DEPTH = 1024; -std::pair> read_compound(std::istream& is, endian::endian e) -{ - return stream_reader(is, e).read_compound(); -} + std::pair> + read_compound(std::istream& is, endian::endian e) + { + return stream_reader(is, e).read_compound(); + } -std::pair> read_tag(std::istream& is, endian::endian e) -{ - return stream_reader(is, e).read_tag(); -} + std::pair> read_tag(std::istream& is, + endian::endian e) + { + return stream_reader(is, e).read_tag(); + } -stream_reader::stream_reader(std::istream& is, endian::endian e) noexcept: - is(is), endian(e) -{} + stream_reader::stream_reader(std::istream& is, + endian::endian e) noexcept + : is(is), endian(e) + { + } -std::istream& stream_reader::get_istr() const -{ - return is; -} + std::istream& stream_reader::get_istr() const + { + return is; + } -endian::endian stream_reader::get_endian() const -{ - return endian; -} + endian::endian stream_reader::get_endian() const + { + return endian; + } -std::pair> stream_reader::read_compound() -{ - if(read_type() != tag_type::Compound) - { - is.setstate(std::ios::failbit); - throw input_error("Tag is not a compound"); - } - std::string key = read_string(); - auto comp = make_unique(); - comp->read_payload(*this); - return {std::move(key), std::move(comp)}; -} + std::pair> + stream_reader::read_compound() + { + if (read_type() != tag_type::Compound) { + is.setstate(std::ios::failbit); + throw input_error("Tag is not a compound"); + } + std::string key = read_string(); + auto comp = make_unique(); + comp->read_payload(*this); + return {std::move(key), std::move(comp)}; + } -std::pair> stream_reader::read_tag() -{ - tag_type type = read_type(); - std::string key = read_string(); - std::unique_ptr t = read_payload(type); - return {std::move(key), std::move(t)}; -} + std::pair> stream_reader::read_tag() + { + tag_type type = read_type(); + std::string key = read_string(); + std::unique_ptr t = read_payload(type); + return {std::move(key), std::move(t)}; + } -std::unique_ptr stream_reader::read_payload(tag_type type) -{ - if (++depth > MAX_DEPTH) - throw input_error("Too deeply nested"); - std::unique_ptr t = tag::create(type); - t->read_payload(*this); - --depth; - return t; -} + std::unique_ptr stream_reader::read_payload(tag_type type) + { + if (++depth > MAX_DEPTH) + throw input_error("Too deeply nested"); + std::unique_ptr t = tag::create(type); + t->read_payload(*this); + --depth; + return t; + } -tag_type stream_reader::read_type(bool allow_end) -{ - int type = is.get(); - if(!is) - throw input_error("Error reading tag type"); - if(!is_valid_type(type, allow_end)) - { - is.setstate(std::ios::failbit); - throw input_error("Invalid tag type: " + std::to_string(type)); - } - return static_cast(type); -} + tag_type stream_reader::read_type(bool allow_end) + { + int type = is.get(); + if (!is) + throw input_error("Error reading tag type"); + if (!is_valid_type(type, allow_end)) { + is.setstate(std::ios::failbit); + throw input_error("Invalid tag type: " + std::to_string(type)); + } + return static_cast(type); + } -std::string stream_reader::read_string() -{ - uint16_t len; - read_num(len); - if(!is) - throw input_error("Error reading string"); + std::string stream_reader::read_string() + { + uint16_t len; + read_num(len); + if (!is) + throw input_error("Error reading string"); - std::string ret(len, '\0'); - is.read(&ret[0], len); //C++11 allows us to do this - if(!is) - throw input_error("Error reading string"); - return ret; -} + std::string ret(len, '\0'); + is.read(&ret[0], len); // C++11 allows us to do this + if (!is) + throw input_error("Error reading string"); + return ret; + } -} -} + } // namespace io +} // namespace nbt diff --git a/src/io/stream_writer.cpp b/src/io/stream_writer.cpp index 0294a6d4e9..5275c5a1b4 100644 --- a/src/io/stream_writer.cpp +++ b/src/io/stream_writer.cpp @@ -26,33 +26,34 @@ namespace nbt { -namespace io -{ + namespace io + { -void write_tag(const std::string& key, const tag& t, std::ostream& os, endian::endian e) -{ - stream_writer(os, e).write_tag(key, t); -} + void write_tag(const std::string& key, const tag& t, std::ostream& os, + endian::endian e) + { + stream_writer(os, e).write_tag(key, t); + } -void stream_writer::write_tag(const std::string& key, const tag& t) -{ - write_type(t.get_type()); - write_string(key); - write_payload(t); -} + void stream_writer::write_tag(const std::string& key, const tag& t) + { + write_type(t.get_type()); + write_string(key); + write_payload(t); + } -void stream_writer::write_string(const std::string& str) -{ - if(str.size() > max_string_len) - { - os.setstate(std::ios::failbit); - std::ostringstream sstr; - sstr << "String is too long for NBT (" << str.size() << " > " << max_string_len << ")"; - throw std::length_error(sstr.str()); - } - write_num(static_cast(str.size())); - os.write(str.data(), str.size()); -} + void stream_writer::write_string(const std::string& str) + { + if (str.size() > max_string_len) { + os.setstate(std::ios::failbit); + std::ostringstream sstr; + sstr << "String is too long for NBT (" << str.size() << " > " + << max_string_len << ")"; + throw std::length_error(sstr.str()); + } + write_num(static_cast(str.size())); + os.write(str.data(), str.size()); + } -} -} + } // namespace io +} // namespace nbt diff --git a/src/tag.cpp b/src/tag.cpp index 0446dfd3f8..8e57548f2d 100644 --- a/src/tag.cpp +++ b/src/tag.cpp @@ -32,136 +32,170 @@ namespace nbt { -//Explicit instantiation definitions for tag_primitive -template class tag_primitive; -template class tag_primitive; -template class tag_primitive; -template class tag_primitive; -template class tag_primitive; -template class tag_primitive; - -static_assert(std::numeric_limits::is_iec559 && std::numeric_limits::is_iec559, - "The floating point values for NBT must conform to IEC 559/IEEE 754"); - -bool is_valid_type(int type, bool allow_end) -{ - return (allow_end ? 0 : 1) <= type && type <= 12; -} - -std::unique_ptr tag::clone() && -{ - return std::move(*this).move_clone(); -} - -namespace -{ - template - std::unique_ptr create_numeric_tag(tag_type type, T val) - { - switch(type) - { - case tag_type::Byte: return make_unique(static_cast(val)); - case tag_type::Short: return make_unique(static_cast(val)); - case tag_type::Int: return make_unique(static_cast(val)); - case tag_type::Long: return make_unique(static_cast(val)); - case tag_type::Float: return make_unique(static_cast(val)); - case tag_type::Double: return make_unique(static_cast(val)); - default: throw std::invalid_argument("Invalid numeric tag type"); - } - } -} - -std::unique_ptr tag::create(tag_type type) -{ - switch(type) - { - case tag_type::Byte: return make_unique(); - case tag_type::Short: return make_unique(); - case tag_type::Int: return make_unique(); - case tag_type::Long: return make_unique(); - case tag_type::Float: return make_unique(); - case tag_type::Double: return make_unique(); - case tag_type::Byte_Array: return make_unique(); - case tag_type::String: return make_unique(); - case tag_type::List: return make_unique(); - case tag_type::Compound: return make_unique(); - case tag_type::Int_Array: return make_unique(); - case tag_type::Long_Array: return make_unique(); - - default: throw std::invalid_argument("Invalid tag type"); - } -} - -std::unique_ptr tag::create(tag_type type, int8_t val) -{ - return create_numeric_tag(type, val); -} - -std::unique_ptr tag::create(tag_type type, int16_t val) -{ - return create_numeric_tag(type, val); -} - -std::unique_ptr tag::create(tag_type type, int32_t val) -{ - return create_numeric_tag(type, val); -} - -std::unique_ptr tag::create(tag_type type, int64_t val) -{ - return create_numeric_tag(type, val); -} - -std::unique_ptr tag::create(tag_type type, float val) -{ - return create_numeric_tag(type, val); -} - -std::unique_ptr tag::create(tag_type type, double val) -{ - return create_numeric_tag(type, val); -} - -bool operator==(const tag& lhs, const tag& rhs) -{ - if(typeid(lhs) != typeid(rhs)) - return false; - return lhs.equals(rhs); -} - -bool operator!=(const tag& lhs, const tag& rhs) -{ - return !(lhs == rhs); -} - -std::ostream& operator<<(std::ostream& os, tag_type tt) -{ - switch(tt) - { - case tag_type::End: return os << "end"; - case tag_type::Byte: return os << "byte"; - case tag_type::Short: return os << "short"; - case tag_type::Int: return os << "int"; - case tag_type::Long: return os << "long"; - case tag_type::Float: return os << "float"; - case tag_type::Double: return os << "double"; - case tag_type::Byte_Array: return os << "byte_array"; - case tag_type::String: return os << "string"; - case tag_type::List: return os << "list"; - case tag_type::Compound: return os << "compound"; - case tag_type::Int_Array: return os << "int_array"; - case tag_type::Long_Array: return os << "long_array"; - case tag_type::Null: return os << "null"; - - default: return os << "invalid"; - } -} - -std::ostream& operator<<(std::ostream& os, const tag& t) -{ - static const text::json_formatter formatter; - formatter.print(os, t); - return os; -} - -} + // Explicit instantiation definitions for tag_primitive + template class tag_primitive; + template class tag_primitive; + template class tag_primitive; + template class tag_primitive; + template class tag_primitive; + template class tag_primitive; + + static_assert( + std::numeric_limits::is_iec559 && + std::numeric_limits::is_iec559, + "The floating point values for NBT must conform to IEC 559/IEEE 754"); + + bool is_valid_type(int type, bool allow_end) + { + return (allow_end ? 0 : 1) <= type && type <= 12; + } + + std::unique_ptr tag::clone() && + { + return std::move(*this).move_clone(); + } + + namespace + { + template + std::unique_ptr create_numeric_tag(tag_type type, T val) + { + switch (type) { + case tag_type::Byte: + return make_unique(static_cast(val)); + case tag_type::Short: + return make_unique(static_cast(val)); + case tag_type::Int: + return make_unique(static_cast(val)); + case tag_type::Long: + return make_unique(static_cast(val)); + case tag_type::Float: + return make_unique(static_cast(val)); + case tag_type::Double: + return make_unique(static_cast(val)); + default: + throw std::invalid_argument("Invalid numeric tag type"); + } + } + } // namespace + + std::unique_ptr tag::create(tag_type type) + { + switch (type) { + case tag_type::Byte: + return make_unique(); + case tag_type::Short: + return make_unique(); + case tag_type::Int: + return make_unique(); + case tag_type::Long: + return make_unique(); + case tag_type::Float: + return make_unique(); + case tag_type::Double: + return make_unique(); + case tag_type::Byte_Array: + return make_unique(); + case tag_type::String: + return make_unique(); + case tag_type::List: + return make_unique(); + case tag_type::Compound: + return make_unique(); + case tag_type::Int_Array: + return make_unique(); + case tag_type::Long_Array: + return make_unique(); + + default: + throw std::invalid_argument("Invalid tag type"); + } + } + + std::unique_ptr tag::create(tag_type type, int8_t val) + { + return create_numeric_tag(type, val); + } + + std::unique_ptr tag::create(tag_type type, int16_t val) + { + return create_numeric_tag(type, val); + } + + std::unique_ptr tag::create(tag_type type, int32_t val) + { + return create_numeric_tag(type, val); + } + + std::unique_ptr tag::create(tag_type type, int64_t val) + { + return create_numeric_tag(type, val); + } + + std::unique_ptr tag::create(tag_type type, float val) + { + return create_numeric_tag(type, val); + } + + std::unique_ptr tag::create(tag_type type, double val) + { + return create_numeric_tag(type, val); + } + + bool operator==(const tag& lhs, const tag& rhs) + { + if (typeid(lhs) != typeid(rhs)) + return false; + return lhs.equals(rhs); + } + + bool operator!=(const tag& lhs, const tag& rhs) + { + return !(lhs == rhs); + } + + std::ostream& operator<<(std::ostream& os, tag_type tt) + { + switch (tt) { + case tag_type::End: + return os << "end"; + case tag_type::Byte: + return os << "byte"; + case tag_type::Short: + return os << "short"; + case tag_type::Int: + return os << "int"; + case tag_type::Long: + return os << "long"; + case tag_type::Float: + return os << "float"; + case tag_type::Double: + return os << "double"; + case tag_type::Byte_Array: + return os << "byte_array"; + case tag_type::String: + return os << "string"; + case tag_type::List: + return os << "list"; + case tag_type::Compound: + return os << "compound"; + case tag_type::Int_Array: + return os << "int_array"; + case tag_type::Long_Array: + return os << "long_array"; + case tag_type::Null: + return os << "null"; + + default: + return os << "invalid"; + } + } + + std::ostream& operator<<(std::ostream& os, const tag& t) + { + static const text::json_formatter formatter; + formatter.print(os, t); + return os; + } + +} // namespace nbt diff --git a/src/tag_compound.cpp b/src/tag_compound.cpp index c71166de95..c35ef5d53f 100644 --- a/src/tag_compound.cpp +++ b/src/tag_compound.cpp @@ -30,84 +30,80 @@ namespace nbt { -tag_compound::tag_compound(std::initializer_list> init) -{ - for(const auto& pair: init) - tags.emplace(std::move(pair.first), std::move(pair.second)); -} + tag_compound::tag_compound( + std::initializer_list> init) + { + for (const auto& pair : init) + tags.emplace(std::move(pair.first), std::move(pair.second)); + } -value& tag_compound::at(const std::string& key) -{ - return tags.at(key); -} + value& tag_compound::at(const std::string& key) + { + return tags.at(key); + } -const value& tag_compound::at(const std::string& key) const -{ - return tags.at(key); -} + const value& tag_compound::at(const std::string& key) const + { + return tags.at(key); + } -std::pair tag_compound::put(const std::string& key, value_initializer&& val) -{ - auto it = tags.find(key); - if(it != tags.end()) - { - it->second = std::move(val); - return {it, false}; - } - else - { - return tags.emplace(key, std::move(val)); - } -} + std::pair + tag_compound::put(const std::string& key, value_initializer&& val) + { + auto it = tags.find(key); + if (it != tags.end()) { + it->second = std::move(val); + return {it, false}; + } else { + return tags.emplace(key, std::move(val)); + } + } -std::pair tag_compound::insert(const std::string& key, value_initializer&& val) -{ - return tags.emplace(key, std::move(val)); -} + std::pair + tag_compound::insert(const std::string& key, value_initializer&& val) + { + return tags.emplace(key, std::move(val)); + } -bool tag_compound::erase(const std::string& key) -{ - return tags.erase(key) != 0; -} + bool tag_compound::erase(const std::string& key) + { + return tags.erase(key) != 0; + } -bool tag_compound::has_key(const std::string& key) const -{ - return tags.find(key) != tags.end(); -} + bool tag_compound::has_key(const std::string& key) const + { + return tags.find(key) != tags.end(); + } -bool tag_compound::has_key(const std::string& key, tag_type type) const -{ - auto it = tags.find(key); - return it != tags.end() && it->second.get_type() == type; -} + bool tag_compound::has_key(const std::string& key, tag_type type) const + { + auto it = tags.find(key); + return it != tags.end() && it->second.get_type() == type; + } -void tag_compound::read_payload(io::stream_reader& reader) -{ - clear(); - tag_type tt; - while((tt = reader.read_type(true)) != tag_type::End) - { - std::string key; - try - { - key = reader.read_string(); - } - catch(io::input_error& ex) - { - std::ostringstream str; - str << "Error reading key of tag_" << tt; - throw io::input_error(str.str()); - } - auto tptr = reader.read_payload(tt); - tags.emplace(std::move(key), value(std::move(tptr))); - } -} + void tag_compound::read_payload(io::stream_reader& reader) + { + clear(); + tag_type tt; + while ((tt = reader.read_type(true)) != tag_type::End) { + std::string key; + try { + key = reader.read_string(); + } catch (io::input_error& ex) { + std::ostringstream str; + str << "Error reading key of tag_" << tt; + throw io::input_error(str.str()); + } + auto tptr = reader.read_payload(tt); + tags.emplace(std::move(key), value(std::move(tptr))); + } + } -void tag_compound::write_payload(io::stream_writer& writer) const -{ - for(const auto& pair: tags) - writer.write_tag(pair.first, pair.second); - writer.write_type(tag_type::End); -} + void tag_compound::write_payload(io::stream_writer& writer) const + { + for (const auto& pair : tags) + writer.write_tag(pair.first, pair.second); + writer.write_type(tag_type::End); + } -} +} // namespace nbt diff --git a/src/tag_list.cpp b/src/tag_list.cpp index f72f86bb24..413af779ae 100644 --- a/src/tag_list.cpp +++ b/src/tag_list.cpp @@ -30,126 +30,158 @@ namespace nbt { -tag_list::tag_list(std::initializer_list il) { init(il); } -tag_list::tag_list(std::initializer_list il) { init(il); } -tag_list::tag_list(std::initializer_list il) { init(il); } -tag_list::tag_list(std::initializer_list il) { init(il); } -tag_list::tag_list(std::initializer_list il) { init(il); } -tag_list::tag_list(std::initializer_list il) { init(il); } -tag_list::tag_list(std::initializer_list il) { init(il); } -tag_list::tag_list(std::initializer_list il) { init(il); } -tag_list::tag_list(std::initializer_list il) { init(il); } -tag_list::tag_list(std::initializer_list il) { init(il); } -tag_list::tag_list(std::initializer_list il) { init(il); } -tag_list::tag_list(std::initializer_list il) { init(il); } - -tag_list::tag_list(std::initializer_list init) -{ - if(init.size() == 0) - el_type_ = tag_type::Null; - else - { - el_type_ = init.begin()->get_type(); - for(const value& val: init) - { - if(!val || val.get_type() != el_type_) - throw std::invalid_argument("The values are not all the same type"); - } - tags.assign(init.begin(), init.end()); - } -} - -value& tag_list::at(size_t i) -{ - return tags.at(i); -} - -const value& tag_list::at(size_t i) const -{ - return tags.at(i); -} - -void tag_list::set(size_t i, value&& val) -{ - if(val.get_type() != el_type_) - throw std::invalid_argument("The tag type does not match the list's content type"); - tags.at(i) = std::move(val); -} - -void tag_list::push_back(value_initializer&& val) -{ - if(!val) //don't allow null values - throw std::invalid_argument("The value must not be null"); - if(el_type_ == tag_type::Null) //set content type if undetermined - el_type_ = val.get_type(); - else if(el_type_ != val.get_type()) - throw std::invalid_argument("The tag type does not match the list's content type"); - tags.push_back(std::move(val)); -} - -void tag_list::reset(tag_type type) -{ - clear(); - el_type_ = type; -} - -void tag_list::read_payload(io::stream_reader& reader) -{ - tag_type lt = reader.read_type(true); - - int32_t length; - reader.read_num(length); - if(length < 0) - reader.get_istr().setstate(std::ios::failbit); - if(!reader.get_istr()) - throw io::input_error("Error reading length of tag_list"); - - if(lt != tag_type::End) - { - reset(lt); - tags.reserve(length); - - for(int32_t i = 0; i < length; ++i) - tags.emplace_back(reader.read_payload(lt)); - } - else - { - //In case of tag_end, ignore the length and leave the type undetermined - reset(tag_type::Null); - } -} - -void tag_list::write_payload(io::stream_writer& writer) const -{ - if(size() > io::stream_writer::max_array_len) - { - writer.get_ostr().setstate(std::ios::failbit); - throw std::length_error("List is too large for NBT"); - } - writer.write_type(el_type_ != tag_type::Null - ? el_type_ - : tag_type::End); - writer.write_num(static_cast(size())); - for(const auto& val: tags) - { - //check if the value is of the correct type - if(val.get_type() != el_type_) - { - writer.get_ostr().setstate(std::ios::failbit); - throw std::logic_error("The tags in the list do not all match the content type"); - } - writer.write_payload(val); - } -} - -bool operator==(const tag_list& lhs, const tag_list& rhs) -{ - return lhs.el_type_ == rhs.el_type_ && lhs.tags == rhs.tags; -} - -bool operator!=(const tag_list& lhs, const tag_list& rhs) -{ - return !(lhs == rhs); -} - -} + tag_list::tag_list(std::initializer_list il) + { + init(il); + } + tag_list::tag_list(std::initializer_list il) + { + init(il); + } + tag_list::tag_list(std::initializer_list il) + { + init(il); + } + tag_list::tag_list(std::initializer_list il) + { + init(il); + } + tag_list::tag_list(std::initializer_list il) + { + init(il); + } + tag_list::tag_list(std::initializer_list il) + { + init(il); + } + tag_list::tag_list(std::initializer_list il) + { + init(il); + } + tag_list::tag_list(std::initializer_list il) + { + init(il); + } + tag_list::tag_list(std::initializer_list il) + { + init(il); + } + tag_list::tag_list(std::initializer_list il) + { + init(il); + } + tag_list::tag_list(std::initializer_list il) + { + init(il); + } + tag_list::tag_list(std::initializer_list il) + { + init(il); + } + + tag_list::tag_list(std::initializer_list init) + { + if (init.size() == 0) + el_type_ = tag_type::Null; + else { + el_type_ = init.begin()->get_type(); + for (const value& val : init) { + if (!val || val.get_type() != el_type_) + throw std::invalid_argument( + "The values are not all the same type"); + } + tags.assign(init.begin(), init.end()); + } + } + + value& tag_list::at(size_t i) + { + return tags.at(i); + } + + const value& tag_list::at(size_t i) const + { + return tags.at(i); + } + + void tag_list::set(size_t i, value&& val) + { + if (val.get_type() != el_type_) + throw std::invalid_argument( + "The tag type does not match the list's content type"); + tags.at(i) = std::move(val); + } + + void tag_list::push_back(value_initializer&& val) + { + if (!val) // don't allow null values + throw std::invalid_argument("The value must not be null"); + if (el_type_ == tag_type::Null) // set content type if undetermined + el_type_ = val.get_type(); + else if (el_type_ != val.get_type()) + throw std::invalid_argument( + "The tag type does not match the list's content type"); + tags.push_back(std::move(val)); + } + + void tag_list::reset(tag_type type) + { + clear(); + el_type_ = type; + } + + void tag_list::read_payload(io::stream_reader& reader) + { + tag_type lt = reader.read_type(true); + + int32_t length; + reader.read_num(length); + if (length < 0) + reader.get_istr().setstate(std::ios::failbit); + if (!reader.get_istr()) + throw io::input_error("Error reading length of tag_list"); + + if (lt != tag_type::End) { + reset(lt); + tags.reserve(length); + + for (int32_t i = 0; i < length; ++i) + tags.emplace_back(reader.read_payload(lt)); + } else { + // In case of tag_end, ignore the length and leave the type + // undetermined + reset(tag_type::Null); + } + } + + void tag_list::write_payload(io::stream_writer& writer) const + { + if (size() > io::stream_writer::max_array_len) { + writer.get_ostr().setstate(std::ios::failbit); + throw std::length_error("List is too large for NBT"); + } + writer.write_type(el_type_ != tag_type::Null ? el_type_ + : tag_type::End); + writer.write_num(static_cast(size())); + for (const auto& val : tags) { + // check if the value is of the correct type + if (val.get_type() != el_type_) { + writer.get_ostr().setstate(std::ios::failbit); + throw std::logic_error( + "The tags in the list do not all match the content type"); + } + writer.write_payload(val); + } + } + + bool operator==(const tag_list& lhs, const tag_list& rhs) + { + return lhs.el_type_ == rhs.el_type_ && lhs.tags == rhs.tags; + } + + bool operator!=(const tag_list& lhs, const tag_list& rhs) + { + return !(lhs == rhs); + } + +} // namespace nbt diff --git a/src/tag_string.cpp b/src/tag_string.cpp index e630b1dfc4..481ad19dee 100644 --- a/src/tag_string.cpp +++ b/src/tag_string.cpp @@ -28,21 +28,18 @@ namespace nbt { -void tag_string::read_payload(io::stream_reader& reader) -{ - try - { - value = reader.read_string(); - } - catch(io::input_error& ex) - { - throw io::input_error("Error reading tag_string"); - } -} + void tag_string::read_payload(io::stream_reader& reader) + { + try { + value = reader.read_string(); + } catch (io::input_error& ex) { + throw io::input_error("Error reading tag_string"); + } + } -void tag_string::write_payload(io::stream_writer& writer) const -{ - writer.write_string(value); -} + void tag_string::write_payload(io::stream_writer& writer) const + { + writer.write_string(value); + } -} +} // namespace nbt diff --git a/src/text/json_formatter.cpp b/src/text/json_formatter.cpp index 88f3a7c58e..46d726a3c9 100644 --- a/src/text/json_formatter.cpp +++ b/src/text/json_formatter.cpp @@ -30,209 +30,227 @@ namespace nbt { -namespace text -{ + namespace text + { -namespace //anonymous -{ - ///Helper class which uses the Visitor pattern to pretty-print tags - class json_fmt_visitor : public const_nbt_visitor - { - public: - json_fmt_visitor(std::ostream& os): - os(os) - {} - - void visit(const tag_byte& b) override - { os << static_cast(b.get()) << "b"; } //We don't want to print a character - - void visit(const tag_short& s) override - { os << s.get() << "s"; } - - void visit(const tag_int& i) override - { os << i.get(); } - - void visit(const tag_long& l) override - { os << l.get() << "l"; } - - void visit(const tag_float& f) override - { - write_float(f.get()); - os << "f"; - } - - void visit(const tag_double& d) override - { - write_float(d.get()); - os << "d"; - } - - void visit(const tag_byte_array& ba) override - { os << "[" << ba.size() << " bytes]"; } - - void visit(const tag_string& s) override - { - os << '"'; - write_escaped_string(s.get()); - os << '"'; - } - - void visit(const tag_list& l) override - { - //Wrap lines for lists of lists or compounds. - //Lists of other types can usually be on one line without problem. - const bool break_lines = l.size() > 0 && - (l.el_type() == tag_type::List || l.el_type() == tag_type::Compound); - - os << "["; - if(break_lines) - { - os << "\n"; - ++indent_lvl; - for(unsigned int i = 0; i < l.size(); ++i) - { - indent(); - if(l[i]) - l[i].get().accept(*this); - else - write_null(); - if(i != l.size()-1) - os << ","; - os << "\n"; - } - --indent_lvl; - indent(); - } - else - { - for(unsigned int i = 0; i < l.size(); ++i) - { - if(l[i]) - l[i].get().accept(*this); - else - write_null(); - if(i != l.size()-1) - os << ", "; - } - } - os << "]"; - } - - void visit(const tag_compound& c) override - { - if(c.size() == 0) //No line breaks inside empty compounds please - { - os << "{}"; - return; - } - - os << "{\n"; - ++indent_lvl; - unsigned int i = 0; - for(const auto& kv: c) - { - indent(); - os << kv.first << ": "; - if(kv.second) - kv.second.get().accept(*this); - else - write_null(); - if(i != c.size()-1) - os << ","; - os << "\n"; - ++i; - } - --indent_lvl; - indent(); - os << "}"; - } - - void visit(const tag_int_array& ia) override - { - os << "["; - for(unsigned int i = 0; i < ia.size(); ++i) - { - os << ia[i]; - if(i != ia.size()-1) - os << ", "; - } - os << "]"; - } - - void visit(const tag_long_array& la) override - { - os << "["; - for(unsigned int i = 0; i < la.size(); ++i) - { - os << la[i]; - if(i != la.size()-1) - os << ", "; - } - os << "]"; - } - - private: - const std::string indent_str = " "; - - std::ostream& os; - int indent_lvl = 0; - - void indent() - { - for(int i = 0; i < indent_lvl; ++i) - os << indent_str; - } - - template - void write_float(T val, int precision = std::numeric_limits::max_digits10) - { - if(std::isfinite(val)) - os << std::setprecision(precision) << val; - else if(std::isinf(val)) - { - if(std::signbit(val)) - os << "-"; - os << "Infinity"; - } - else - os << "NaN"; - } - - void write_null() - { - os << "null"; - } - - void write_escaped_string(const std::string& str) - { - for (char c : str) { - switch (c) { - case '"': os << "\\\""; break; - case '\\': os << "\\\\"; break; - case '\b': os << "\\b"; break; - case '\f': os << "\\f"; break; - case '\n': os << "\\n"; break; - case '\r': os << "\\r"; break; - case '\t': os << "\\t"; break; - default: - if (c < 32 || c == 127) { - // Control characters, escape as \u00XX - os << "\\u00" << std::hex << std::setw(2) << std::setfill('0') << static_cast(c); - } else { - os << c; - } - break; - } - } - } - }; -} - -void json_formatter::print(std::ostream& os, const tag& t) const -{ - json_fmt_visitor v(os); - t.accept(v); -} + namespace // anonymous + { + /// Helper class which uses the Visitor pattern to pretty-print tags + class json_fmt_visitor : public const_nbt_visitor + { + public: + json_fmt_visitor(std::ostream& os) : os(os) {} + + void visit(const tag_byte& b) override + { + os << static_cast(b.get()) << "b"; + } // We don't want to print a character + + void visit(const tag_short& s) override + { + os << s.get() << "s"; + } + + void visit(const tag_int& i) override + { + os << i.get(); + } + + void visit(const tag_long& l) override + { + os << l.get() << "l"; + } + + void visit(const tag_float& f) override + { + write_float(f.get()); + os << "f"; + } + + void visit(const tag_double& d) override + { + write_float(d.get()); + os << "d"; + } + + void visit(const tag_byte_array& ba) override + { + os << "[" << ba.size() << " bytes]"; + } + + void visit(const tag_string& s) override + { + os << '"'; + write_escaped_string(s.get()); + os << '"'; + } + + void visit(const tag_list& l) override + { + // Wrap lines for lists of lists or compounds. + // Lists of other types can usually be on one line without + // problem. + const bool break_lines = + l.size() > 0 && (l.el_type() == tag_type::List || + l.el_type() == tag_type::Compound); + + os << "["; + if (break_lines) { + os << "\n"; + ++indent_lvl; + for (unsigned int i = 0; i < l.size(); ++i) { + indent(); + if (l[i]) + l[i].get().accept(*this); + else + write_null(); + if (i != l.size() - 1) + os << ","; + os << "\n"; + } + --indent_lvl; + indent(); + } else { + for (unsigned int i = 0; i < l.size(); ++i) { + if (l[i]) + l[i].get().accept(*this); + else + write_null(); + if (i != l.size() - 1) + os << ", "; + } + } + os << "]"; + } + + void visit(const tag_compound& c) override + { + if (c.size() == + 0) // No line breaks inside empty compounds please + { + os << "{}"; + return; + } + + os << "{\n"; + ++indent_lvl; + unsigned int i = 0; + for (const auto& kv : c) { + indent(); + os << kv.first << ": "; + if (kv.second) + kv.second.get().accept(*this); + else + write_null(); + if (i != c.size() - 1) + os << ","; + os << "\n"; + ++i; + } + --indent_lvl; + indent(); + os << "}"; + } + + void visit(const tag_int_array& ia) override + { + os << "["; + for (unsigned int i = 0; i < ia.size(); ++i) { + os << ia[i]; + if (i != ia.size() - 1) + os << ", "; + } + os << "]"; + } + + void visit(const tag_long_array& la) override + { + os << "["; + for (unsigned int i = 0; i < la.size(); ++i) { + os << la[i]; + if (i != la.size() - 1) + os << ", "; + } + os << "]"; + } + + private: + const std::string indent_str = " "; + + std::ostream& os; + int indent_lvl = 0; + + void indent() + { + for (int i = 0; i < indent_lvl; ++i) + os << indent_str; + } + + template + void write_float( + T val, int precision = std::numeric_limits::max_digits10) + { + if (std::isfinite(val)) + os << std::setprecision(precision) << val; + else if (std::isinf(val)) { + if (std::signbit(val)) + os << "-"; + os << "Infinity"; + } else + os << "NaN"; + } + + void write_null() + { + os << "null"; + } + + void write_escaped_string(const std::string& str) + { + for (char c : str) { + switch (c) { + case '"': + os << "\\\""; + break; + case '\\': + os << "\\\\"; + break; + case '\b': + os << "\\b"; + break; + case '\f': + os << "\\f"; + break; + case '\n': + os << "\\n"; + break; + case '\r': + os << "\\r"; + break; + case '\t': + os << "\\t"; + break; + default: + if (c < 32 || c == 127) { + // Control characters, escape as \u00XX + os << "\\u00" << std::hex << std::setw(2) + << std::setfill('0') + << static_cast(c); + } else { + os << c; + } + break; + } + } + } + }; + } // namespace + + void json_formatter::print(std::ostream& os, const tag& t) const + { + json_fmt_visitor v(os); + t.accept(v); + } -} -} + } // namespace text +} // namespace nbt diff --git a/src/value.cpp b/src/value.cpp index bf0ffcad78..3a9ccb71bc 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -32,286 +32,296 @@ namespace nbt { -value::value(tag&& t): - tag_(std::move(t).move_clone()) -{} - -value::value(const value& rhs): - tag_(rhs.tag_ ? rhs.tag_->clone() : nullptr) -{} - -value& value::operator=(const value& rhs) -{ - if(this != &rhs) - { - tag_ = rhs.tag_ ? rhs.tag_->clone() : nullptr; - } - return *this; -} - -value& value::operator=(tag&& t) -{ - set(std::move(t)); - return *this; -} - -void value::set(tag&& t) -{ - if(tag_) - tag_->assign(std::move(t)); - else - tag_ = std::move(t).move_clone(); -} - -//Primitive assignment -namespace // helper functions local to this translation unit -{ - template - void assign_numeric_impl(std::unique_ptr& tag_ptr, T val, - tag_type default_type) - { - using nbt::tag_type; - if(!tag_ptr) - { - tag_ptr = tag::create(default_type, val); - return; - } - - // Determine the incoming tag type for T - auto incoming_type = detail::get_primitive_type::value; - - // If the existing tag is of a narrower type than the incoming type, - // reject the assignment to avoid widening the stored tag type. - auto existing_type = tag_ptr->get_type(); - - if(static_cast(existing_type) < static_cast(incoming_type)) - { - throw std::bad_cast(); - } - - // Existing type is same or wider: write into the existing tag (may narrow) - switch(existing_type) - { - case tag_type::Byte: static_cast(*tag_ptr).set(static_cast(val)); break; - case tag_type::Short: static_cast(*tag_ptr).set(static_cast(val)); break; - case tag_type::Int: static_cast(*tag_ptr).set(static_cast(val)); break; - case tag_type::Long: static_cast(*tag_ptr).set(static_cast(val)); break; - case tag_type::Float: static_cast(*tag_ptr).set(static_cast(val)); break; - case tag_type::Double: static_cast(*tag_ptr).set(static_cast(val)); break; - default: throw std::bad_cast(); - } - } -} - -value& value::operator=(int8_t val) -{ - assign_numeric_impl(tag_, val, tag_type::Byte); - return *this; -} - -value& value::operator=(int16_t val) -{ - assign_numeric_impl(tag_, val, tag_type::Short); - return *this; -} - -value& value::operator=(int32_t val) -{ - assign_numeric_impl(tag_, val, tag_type::Int); - return *this; -} - -value& value::operator=(int64_t val) -{ - assign_numeric_impl(tag_, val, tag_type::Long); - return *this; -} - -value& value::operator=(float val) -{ - assign_numeric_impl(tag_, val, tag_type::Float); - return *this; -} - -value& value::operator=(double val) -{ - assign_numeric_impl(tag_, val, tag_type::Double); - return *this; -} - -//Primitive conversion -value::operator int8_t() const -{ - switch(tag_->get_type()) - { - case tag_type::Byte: - return static_cast(*tag_).get(); - - default: - throw std::bad_cast(); - } -} - -value::operator int16_t() const -{ - switch(tag_->get_type()) - { - case tag_type::Byte: - return static_cast(*tag_).get(); - case tag_type::Short: - return static_cast(*tag_).get(); - - default: - throw std::bad_cast(); - } -} - -value::operator int32_t() const -{ - switch(tag_->get_type()) - { - case tag_type::Byte: - return static_cast(*tag_).get(); - case tag_type::Short: - return static_cast(*tag_).get(); - case tag_type::Int: - return static_cast(*tag_).get(); - - default: - throw std::bad_cast(); - } -} - -value::operator int64_t() const -{ - switch(tag_->get_type()) - { - case tag_type::Byte: - return static_cast(*tag_).get(); - case tag_type::Short: - return static_cast(*tag_).get(); - case tag_type::Int: - return static_cast(*tag_).get(); - case tag_type::Long: - return static_cast(*tag_).get(); - - default: - throw std::bad_cast(); - } -} - -value::operator float() const -{ - switch(tag_->get_type()) - { - case tag_type::Byte: - return static_cast(*tag_).get(); - case tag_type::Short: - return static_cast(*tag_).get(); - case tag_type::Int: - return static_cast(*tag_).get(); - case tag_type::Long: - return static_cast(*tag_).get(); - case tag_type::Float: - return static_cast(*tag_).get(); - - default: - throw std::bad_cast(); - } -} - -value::operator double() const -{ - switch(tag_->get_type()) - { - case tag_type::Byte: - return static_cast(*tag_).get(); - case tag_type::Short: - return static_cast(*tag_).get(); - case tag_type::Int: - return static_cast(*tag_).get(); - case tag_type::Long: - return static_cast(*tag_).get(); - case tag_type::Float: - return static_cast(*tag_).get(); - case tag_type::Double: - return static_cast(*tag_).get(); - - default: - throw std::bad_cast(); - } -} - -value& value::operator=(std::string&& str) -{ - if(!tag_) - set(tag_string(std::move(str))); - else - dynamic_cast(*tag_).set(std::move(str)); - return *this; -} - -value::operator const std::string&() const -{ - return dynamic_cast(*tag_).get(); -} - -value& value::at(const std::string& key) -{ - return dynamic_cast(*tag_).at(key); -} - -const value& value::at(const std::string& key) const -{ - return dynamic_cast(*tag_).at(key); -} - -value& value::operator[](const std::string& key) -{ - return dynamic_cast(*tag_)[key]; -} - -value& value::operator[](const char* key) -{ - return (*this)[std::string(key)]; -} - -value& value::at(size_t i) -{ - return dynamic_cast(*tag_).at(i); -} - -const value& value::at(size_t i) const -{ - return dynamic_cast(*tag_).at(i); -} - -value& value::operator[](size_t i) -{ - return dynamic_cast(*tag_)[i]; -} - -const value& value::operator[](size_t i) const -{ - return dynamic_cast(*tag_)[i]; -} - -tag_type value::get_type() const -{ - return tag_ ? tag_->get_type() : tag_type::Null; -} - -bool operator==(const value& lhs, const value& rhs) -{ - if(lhs.tag_ != nullptr && rhs.tag_ != nullptr) - return *lhs.tag_ == *rhs.tag_; - else - return lhs.tag_ == nullptr && rhs.tag_ == nullptr; -} - -bool operator!=(const value& lhs, const value& rhs) -{ - return !(lhs == rhs); -} - -} + value::value(tag&& t) : tag_(std::move(t).move_clone()) {} + + value::value(const value& rhs) + : tag_(rhs.tag_ ? rhs.tag_->clone() : nullptr) + { + } + + value& value::operator=(const value& rhs) + { + if (this != &rhs) { + tag_ = rhs.tag_ ? rhs.tag_->clone() : nullptr; + } + return *this; + } + + value& value::operator=(tag&& t) + { + set(std::move(t)); + return *this; + } + + void value::set(tag&& t) + { + if (tag_) + tag_->assign(std::move(t)); + else + tag_ = std::move(t).move_clone(); + } + + // Primitive assignment + namespace // helper functions local to this translation unit + { + template + void assign_numeric_impl(std::unique_ptr& tag_ptr, T val, + tag_type default_type) + { + using nbt::tag_type; + if (!tag_ptr) { + tag_ptr = tag::create(default_type, val); + return; + } + + // Determine the incoming tag type for T + auto incoming_type = detail::get_primitive_type::value; + + // If the existing tag is of a narrower type than the incoming type, + // reject the assignment to avoid widening the stored tag type. + auto existing_type = tag_ptr->get_type(); + + if (static_cast(existing_type) < + static_cast(incoming_type)) { + throw std::bad_cast(); + } + + // Existing type is same or wider: write into the existing tag (may + // narrow) + switch (existing_type) { + case tag_type::Byte: + static_cast(*tag_ptr).set( + static_cast(val)); + break; + case tag_type::Short: + static_cast(*tag_ptr).set( + static_cast(val)); + break; + case tag_type::Int: + static_cast(*tag_ptr).set( + static_cast(val)); + break; + case tag_type::Long: + static_cast(*tag_ptr).set( + static_cast(val)); + break; + case tag_type::Float: + static_cast(*tag_ptr).set( + static_cast(val)); + break; + case tag_type::Double: + static_cast(*tag_ptr).set( + static_cast(val)); + break; + default: + throw std::bad_cast(); + } + } + } // namespace + + value& value::operator=(int8_t val) + { + assign_numeric_impl(tag_, val, tag_type::Byte); + return *this; + } + + value& value::operator=(int16_t val) + { + assign_numeric_impl(tag_, val, tag_type::Short); + return *this; + } + + value& value::operator=(int32_t val) + { + assign_numeric_impl(tag_, val, tag_type::Int); + return *this; + } + + value& value::operator=(int64_t val) + { + assign_numeric_impl(tag_, val, tag_type::Long); + return *this; + } + + value& value::operator=(float val) + { + assign_numeric_impl(tag_, val, tag_type::Float); + return *this; + } + + value& value::operator=(double val) + { + assign_numeric_impl(tag_, val, tag_type::Double); + return *this; + } + + // Primitive conversion + value::operator int8_t() const + { + switch (tag_->get_type()) { + case tag_type::Byte: + return static_cast(*tag_).get(); + + default: + throw std::bad_cast(); + } + } + + value::operator int16_t() const + { + switch (tag_->get_type()) { + case tag_type::Byte: + return static_cast(*tag_).get(); + case tag_type::Short: + return static_cast(*tag_).get(); + + default: + throw std::bad_cast(); + } + } + + value::operator int32_t() const + { + switch (tag_->get_type()) { + case tag_type::Byte: + return static_cast(*tag_).get(); + case tag_type::Short: + return static_cast(*tag_).get(); + case tag_type::Int: + return static_cast(*tag_).get(); + + default: + throw std::bad_cast(); + } + } + + value::operator int64_t() const + { + switch (tag_->get_type()) { + case tag_type::Byte: + return static_cast(*tag_).get(); + case tag_type::Short: + return static_cast(*tag_).get(); + case tag_type::Int: + return static_cast(*tag_).get(); + case tag_type::Long: + return static_cast(*tag_).get(); + + default: + throw std::bad_cast(); + } + } + + value::operator float() const + { + switch (tag_->get_type()) { + case tag_type::Byte: + return static_cast(*tag_).get(); + case tag_type::Short: + return static_cast(*tag_).get(); + case tag_type::Int: + return static_cast(*tag_).get(); + case tag_type::Long: + return static_cast(*tag_).get(); + case tag_type::Float: + return static_cast(*tag_).get(); + + default: + throw std::bad_cast(); + } + } + + value::operator double() const + { + switch (tag_->get_type()) { + case tag_type::Byte: + return static_cast(*tag_).get(); + case tag_type::Short: + return static_cast(*tag_).get(); + case tag_type::Int: + return static_cast(*tag_).get(); + case tag_type::Long: + return static_cast(*tag_).get(); + case tag_type::Float: + return static_cast(*tag_).get(); + case tag_type::Double: + return static_cast(*tag_).get(); + + default: + throw std::bad_cast(); + } + } + + value& value::operator=(std::string&& str) + { + if (!tag_) + set(tag_string(std::move(str))); + else + dynamic_cast(*tag_).set(std::move(str)); + return *this; + } + + value::operator const std::string&() const + { + return dynamic_cast(*tag_).get(); + } + + value& value::at(const std::string& key) + { + return dynamic_cast(*tag_).at(key); + } + + const value& value::at(const std::string& key) const + { + return dynamic_cast(*tag_).at(key); + } + + value& value::operator[](const std::string& key) + { + return dynamic_cast(*tag_)[key]; + } + + value& value::operator[](const char* key) + { + return (*this)[std::string(key)]; + } + + value& value::at(size_t i) + { + return dynamic_cast(*tag_).at(i); + } + + const value& value::at(size_t i) const + { + return dynamic_cast(*tag_).at(i); + } + + value& value::operator[](size_t i) + { + return dynamic_cast(*tag_)[i]; + } + + const value& value::operator[](size_t i) const + { + return dynamic_cast(*tag_)[i]; + } + + tag_type value::get_type() const + { + return tag_ ? tag_->get_type() : tag_type::Null; + } + + bool operator==(const value& lhs, const value& rhs) + { + if (lhs.tag_ != nullptr && rhs.tag_ != nullptr) + return *lhs.tag_ == *rhs.tag_; + else + return lhs.tag_ == nullptr && rhs.tag_ == nullptr; + } + + bool operator!=(const value& lhs, const value& rhs) + { + return !(lhs == rhs); + } + +} // namespace nbt diff --git a/src/value_initializer.cpp b/src/value_initializer.cpp index b0d0d3d747..ec52f544fe 100644 --- a/src/value_initializer.cpp +++ b/src/value_initializer.cpp @@ -27,14 +27,23 @@ namespace nbt { -value_initializer::value_initializer(int8_t val) : value(tag_byte(val)) {} -value_initializer::value_initializer(int16_t val) : value(tag_short(val)) {} -value_initializer::value_initializer(int32_t val) : value(tag_int(val)) {} -value_initializer::value_initializer(int64_t val) : value(tag_long(val)) {} -value_initializer::value_initializer(float val) : value(tag_float(val)) {} -value_initializer::value_initializer(double val) : value(tag_double(val)) {} -value_initializer::value_initializer(const std::string& str): value(tag_string(str)) {} -value_initializer::value_initializer(std::string&& str) : value(tag_string(std::move(str))) {} -value_initializer::value_initializer(const char* str) : value(tag_string(str)) {} + value_initializer::value_initializer(int8_t val) : value(tag_byte(val)) {} + value_initializer::value_initializer(int16_t val) : value(tag_short(val)) {} + value_initializer::value_initializer(int32_t val) : value(tag_int(val)) {} + value_initializer::value_initializer(int64_t val) : value(tag_long(val)) {} + value_initializer::value_initializer(float val) : value(tag_float(val)) {} + value_initializer::value_initializer(double val) : value(tag_double(val)) {} + value_initializer::value_initializer(const std::string& str) + : value(tag_string(str)) + { + } + value_initializer::value_initializer(std::string&& str) + : value(tag_string(std::move(str))) + { + } + value_initializer::value_initializer(const char* str) + : value(tag_string(str)) + { + } -} +} // namespace nbt diff --git a/test/endian_str_test.h b/test/endian_str_test.h index 386ba60b57..d647d563eb 100644 --- a/test/endian_str_test.h +++ b/test/endian_str_test.h @@ -33,149 +33,137 @@ using namespace endian; class endian_str_test : public CxxTest::TestSuite { -public: - void test_uint() - { - std::stringstream str(std::ios::in | std::ios::out | std::ios::binary); - - write_little(str, uint8_t (0x01)); - write_little(str, uint16_t(0x0102)); - write (str, uint32_t(0x01020304), little); - write_little(str, uint64_t(0x0102030405060708)); - - write_big (str, uint8_t (0x09)); - write_big (str, uint16_t(0x090A)); - write_big (str, uint32_t(0x090A0B0C)); - write (str, uint64_t(0x090A0B0C0D0E0F10), big); - - std::string expected{ - 1, - 2, 1, - 4, 3, 2, 1, - 8, 7, 6, 5, 4, 3, 2, 1, - - 9, - 9, 10, - 9, 10, 11, 12, - 9, 10, 11, 12, 13, 14, 15, 16 - }; - TS_ASSERT_EQUALS(str.str(), expected); - - uint8_t u8; - uint16_t u16; - uint32_t u32; - uint64_t u64; - - read_little(str, u8); - TS_ASSERT_EQUALS(u8, 0x01); - read_little(str, u16); - TS_ASSERT_EQUALS(u16, 0x0102); - read_little(str, u32); - TS_ASSERT_EQUALS(u32, 0x01020304u); - read(str, u64, little); - TS_ASSERT_EQUALS(u64, 0x0102030405060708u); - - read_big(str, u8); - TS_ASSERT_EQUALS(u8, 0x09); - read_big(str, u16); - TS_ASSERT_EQUALS(u16, 0x090A); - read(str, u32, big); - TS_ASSERT_EQUALS(u32, 0x090A0B0Cu); - read_big(str, u64); - TS_ASSERT_EQUALS(u64, 0x090A0B0C0D0E0F10u); - - TS_ASSERT(str); //Check if stream has failed - } - - void test_sint() - { - std::stringstream str(std::ios::in | std::ios::out | std::ios::binary); - - write_little(str, int8_t (-0x01)); - write_little(str, int16_t(-0x0102)); - write_little(str, int32_t(-0x01020304)); - write (str, int64_t(-0x0102030405060708), little); - - write_big (str, int8_t (-0x09)); - write_big (str, int16_t(-0x090A)); - write (str, int32_t(-0x090A0B0C), big); - write_big (str, int64_t(-0x090A0B0C0D0E0F10)); - - std::string expected{ //meh, stupid narrowing conversions - '\xFF', - '\xFE', '\xFE', - '\xFC', '\xFC', '\xFD', '\xFE', - '\xF8', '\xF8', '\xF9', '\xFA', '\xFB', '\xFC', '\xFD', '\xFE', - - '\xF7', - '\xF6', '\xF6', - '\xF6', '\xF5', '\xF4', '\xF4', - '\xF6', '\xF5', '\xF4', '\xF3', '\xF2', '\xF1', '\xF0', '\xF0' - }; - TS_ASSERT_EQUALS(str.str(), expected); - - int8_t i8; - int16_t i16; - int32_t i32; - int64_t i64; - - read_little(str, i8); - TS_ASSERT_EQUALS(i8, -0x01); - read_little(str, i16); - TS_ASSERT_EQUALS(i16, -0x0102); - read(str, i32, little); - TS_ASSERT_EQUALS(i32, -0x01020304); - read_little(str, i64); - TS_ASSERT_EQUALS(i64, -0x0102030405060708); - - read_big(str, i8); - TS_ASSERT_EQUALS(i8, -0x09); - read_big(str, i16); - TS_ASSERT_EQUALS(i16, -0x090A); - read_big(str, i32); - TS_ASSERT_EQUALS(i32, -0x090A0B0C); - read(str, i64, big); - TS_ASSERT_EQUALS(i64, -0x090A0B0C0D0E0F10); - - TS_ASSERT(str); //Check if stream has failed - } - - void test_float() - { - std::stringstream str(std::ios::in | std::ios::out | std::ios::binary); - - //C99 has hexadecimal floating point literals, C++ doesn't... - const float fconst = std::stof("-0xCDEF01p-63"); //-1.46325e-012 - const double dconst = std::stod("-0x1DEF0102030405p-375"); //-1.09484e-097 - //We will be assuming IEEE 754 here - - write_little(str, fconst); - write_little(str, dconst); - write_big (str, fconst); - write_big (str, dconst); - - std::string expected{ - '\x01', '\xEF', '\xCD', '\xAB', - '\x05', '\x04', '\x03', '\x02', '\x01', '\xEF', '\xCD', '\xAB', - - '\xAB', '\xCD', '\xEF', '\x01', - '\xAB', '\xCD', '\xEF', '\x01', '\x02', '\x03', '\x04', '\x05' - }; - TS_ASSERT_EQUALS(str.str(), expected); - - float f; - double d; - - read_little(str, f); - TS_ASSERT_EQUALS(f, fconst); - read_little(str, d); - TS_ASSERT_EQUALS(d, dconst); - - read_big(str, f); - TS_ASSERT_EQUALS(f, fconst); - read_big(str, d); - TS_ASSERT_EQUALS(d, dconst); - - TS_ASSERT(str); //Check if stream has failed - } + public: + void test_uint() + { + std::stringstream str(std::ios::in | std::ios::out | std::ios::binary); + + write_little(str, uint8_t(0x01)); + write_little(str, uint16_t(0x0102)); + write(str, uint32_t(0x01020304), little); + write_little(str, uint64_t(0x0102030405060708)); + + write_big(str, uint8_t(0x09)); + write_big(str, uint16_t(0x090A)); + write_big(str, uint32_t(0x090A0B0C)); + write(str, uint64_t(0x090A0B0C0D0E0F10), big); + + std::string expected{ + 1, 2, 1, 4, 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, 1, + + 9, 9, 10, 9, 10, 11, 12, 9, 10, 11, 12, 13, 14, 15, 16}; + TS_ASSERT_EQUALS(str.str(), expected); + + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + + read_little(str, u8); + TS_ASSERT_EQUALS(u8, 0x01); + read_little(str, u16); + TS_ASSERT_EQUALS(u16, 0x0102); + read_little(str, u32); + TS_ASSERT_EQUALS(u32, 0x01020304u); + read(str, u64, little); + TS_ASSERT_EQUALS(u64, 0x0102030405060708u); + + read_big(str, u8); + TS_ASSERT_EQUALS(u8, 0x09); + read_big(str, u16); + TS_ASSERT_EQUALS(u16, 0x090A); + read(str, u32, big); + TS_ASSERT_EQUALS(u32, 0x090A0B0Cu); + read_big(str, u64); + TS_ASSERT_EQUALS(u64, 0x090A0B0C0D0E0F10u); + + TS_ASSERT(str); // Check if stream has failed + } + + void test_sint() + { + std::stringstream str(std::ios::in | std::ios::out | std::ios::binary); + + write_little(str, int8_t(-0x01)); + write_little(str, int16_t(-0x0102)); + write_little(str, int32_t(-0x01020304)); + write(str, int64_t(-0x0102030405060708), little); + + write_big(str, int8_t(-0x09)); + write_big(str, int16_t(-0x090A)); + write(str, int32_t(-0x090A0B0C), big); + write_big(str, int64_t(-0x090A0B0C0D0E0F10)); + + std::string expected{ + // meh, stupid narrowing conversions + '\xFF', '\xFE', '\xFE', '\xFC', '\xFC', '\xFD', '\xFE', '\xF8', + '\xF8', '\xF9', '\xFA', '\xFB', '\xFC', '\xFD', '\xFE', + + '\xF7', '\xF6', '\xF6', '\xF6', '\xF5', '\xF4', '\xF4', '\xF6', + '\xF5', '\xF4', '\xF3', '\xF2', '\xF1', '\xF0', '\xF0'}; + TS_ASSERT_EQUALS(str.str(), expected); + + int8_t i8; + int16_t i16; + int32_t i32; + int64_t i64; + + read_little(str, i8); + TS_ASSERT_EQUALS(i8, -0x01); + read_little(str, i16); + TS_ASSERT_EQUALS(i16, -0x0102); + read(str, i32, little); + TS_ASSERT_EQUALS(i32, -0x01020304); + read_little(str, i64); + TS_ASSERT_EQUALS(i64, -0x0102030405060708); + + read_big(str, i8); + TS_ASSERT_EQUALS(i8, -0x09); + read_big(str, i16); + TS_ASSERT_EQUALS(i16, -0x090A); + read_big(str, i32); + TS_ASSERT_EQUALS(i32, -0x090A0B0C); + read(str, i64, big); + TS_ASSERT_EQUALS(i64, -0x090A0B0C0D0E0F10); + + TS_ASSERT(str); // Check if stream has failed + } + + void test_float() + { + std::stringstream str(std::ios::in | std::ios::out | std::ios::binary); + + // C99 has hexadecimal floating point literals, C++ doesn't... + const float fconst = std::stof("-0xCDEF01p-63"); //-1.46325e-012 + const double dconst = + std::stod("-0x1DEF0102030405p-375"); //-1.09484e-097 + // We will be assuming IEEE 754 here + + write_little(str, fconst); + write_little(str, dconst); + write_big(str, fconst); + write_big(str, dconst); + + std::string expected{'\x01', '\xEF', '\xCD', '\xAB', '\x05', '\x04', + '\x03', '\x02', '\x01', '\xEF', '\xCD', '\xAB', + + '\xAB', '\xCD', '\xEF', '\x01', '\xAB', '\xCD', + '\xEF', '\x01', '\x02', '\x03', '\x04', '\x05'}; + TS_ASSERT_EQUALS(str.str(), expected); + + float f; + double d; + + read_little(str, f); + TS_ASSERT_EQUALS(f, fconst); + read_little(str, d); + TS_ASSERT_EQUALS(d, dconst); + + read_big(str, f); + TS_ASSERT_EQUALS(f, fconst); + read_big(str, d); + TS_ASSERT_EQUALS(d, dconst); + + TS_ASSERT(str); // Check if stream has failed + } }; diff --git a/test/format_test.cpp b/test/format_test.cpp index ed8d2d1bfc..f7d95985c9 100644 --- a/test/format_test.cpp +++ b/test/format_test.cpp @@ -21,7 +21,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with libnbt++. If not, see . */ -//#include "text/json_formatter.h" +// #include "text/json_formatter.h" #include "io/stream_reader.h" #include "io/stream_writer.h" #include @@ -33,73 +33,74 @@ using namespace nbt; int main() { - // Write that into a file and read back for testing - tag_compound comp{ - {"byte", tag_byte(-128)}, - {"short", tag_short(-32768)}, - {"int", tag_int(-2147483648)}, - {"long", tag_long(-9223372036854775808U)}, + // Write that into a file and read back for testing + tag_compound comp{ + {"byte", tag_byte(-128)}, + {"short", tag_short(-32768)}, + {"int", tag_int(-2147483648)}, + {"long", tag_long(-9223372036854775808U)}, - {"float 1", 1.618034f}, - {"float 2", 6.626070e-34f}, - {"float 3", 2.273737e+29f}, - {"float 4", -std::numeric_limits::infinity()}, - {"float 5", std::numeric_limits::quiet_NaN()}, + {"float 1", 1.618034f}, + {"float 2", 6.626070e-34f}, + {"float 3", 2.273737e+29f}, + {"float 4", -std::numeric_limits::infinity()}, + {"float 5", std::numeric_limits::quiet_NaN()}, - {"double 1", 3.141592653589793}, - {"double 2", 1.749899444387479e-193}, - {"double 3", 2.850825855152578e+175}, - {"double 4", -std::numeric_limits::infinity()}, - {"double 5", std::numeric_limits::quiet_NaN()}, + {"double 1", 3.141592653589793}, + {"double 2", 1.749899444387479e-193}, + {"double 3", 2.850825855152578e+175}, + {"double 4", -std::numeric_limits::infinity()}, + {"double 5", std::numeric_limits::quiet_NaN()}, - {"string 1", "Hello World! \u00E4\u00F6\u00FC\u00DF"}, - {"string 2", "String with\nline breaks\tand tabs"}, + {"string 1", "Hello World! \u00E4\u00F6\u00FC\u00DF"}, + {"string 2", "String with\nline breaks\tand tabs"}, - {"byte array", tag_byte_array{12, 13, 14, 15, 16}}, - {"int array", tag_int_array{0x0badc0de, -0x0dedbeef, 0x1badbabe}}, - {"long array", tag_long_array{0x0badc0de0badc0de, -0x0dedbeef0dedbeef, 0x1badbabe1badbabe}}, + {"byte array", tag_byte_array{12, 13, 14, 15, 16}}, + {"int array", tag_int_array{0x0badc0de, -0x0dedbeef, 0x1badbabe}}, + {"long array", tag_long_array{0x0badc0de0badc0de, -0x0dedbeef0dedbeef, + 0x1badbabe1badbabe}}, - {"list (empty)", tag_list::of({})}, - {"list (float)", tag_list{2.0f, 1.0f, 0.5f, 0.25f}}, - {"list (list)", tag_list::of({ - {}, - {4, 5, 6}, - {tag_compound{{"egg", "ham"}}, tag_compound{{"foo", "bar"}}} - })}, - {"list (compound)", tag_list::of({ - {{"created-on", 42}, {"names", tag_list{"Compound", "tag", "#0"}}}, - {{"created-on", 45}, {"names", tag_list{"Compound", "tag", "#1"}}} - })}, + {"list (empty)", tag_list::of({})}, + {"list (float)", tag_list{2.0f, 1.0f, 0.5f, 0.25f}}, + {"list (list)", + tag_list::of( + {{}, + {4, 5, 6}, + {tag_compound{{"egg", "ham"}}, tag_compound{{"foo", "bar"}}}})}, + {"list (compound)", + tag_list::of( + {{{"created-on", 42}, + {"names", tag_list{"Compound", "tag", "#0"}}}, + {{"created-on", 45}, + {"names", tag_list{"Compound", "tag", "#1"}}}})}, - {"compound (empty)", tag_compound()}, - {"compound (nested)", tag_compound{ - {"key", "value"}, - {"key with \u00E4\u00F6\u00FC", tag_byte(-1)}, - {"key with\nnewline and\ttab", tag_compound{}} - }}, + {"compound (empty)", tag_compound()}, + {"compound (nested)", + tag_compound{{"key", "value"}, + {"key with \u00E4\u00F6\u00FC", tag_byte(-1)}, + {"key with\nnewline and\ttab", tag_compound{}}}}, - {"null", nullptr} - }; + {"null", nullptr}}; - std::cout << "----- default operator<<:\n"; - std::cout << comp; - std::cout << "\n-----" << std::endl; + std::cout << "----- default operator<<:\n"; + std::cout << comp; + std::cout << "\n-----" << std::endl; - // Write to file and read back - { - tag_compound file_comp = comp; - file_comp.erase("null"); - std::ofstream out("test_output.nbt", std::ios::binary); - nbt::io::write_tag("root", file_comp, out); - } + // Write to file and read back + { + tag_compound file_comp = comp; + file_comp.erase("null"); + std::ofstream out("test_output.nbt", std::ios::binary); + nbt::io::write_tag("root", file_comp, out); + } - { - std::ifstream in("test_output.nbt", std::ios::binary); - auto read_pair = nbt::io::read_compound(in); - std::cout << "----- read back from file:\n"; - std::cout << *read_pair.second; - std::cout << "\n-----" << std::endl; - } + { + std::ifstream in("test_output.nbt", std::ios::binary); + auto read_pair = nbt::io::read_compound(in); + std::cout << "----- read back from file:\n"; + std::cout << *read_pair.second; + std::cout << "\n-----" << std::endl; + } - return 0; + return 0; } diff --git a/test/nbttest.h b/test/nbttest.h index 4d7f2cb5de..ce660e355d 100644 --- a/test/nbttest.h +++ b/test/nbttest.h @@ -34,471 +34,539 @@ using namespace nbt; class nbttest : public CxxTest::TestSuite { -public: - void test_tag() - { - TS_ASSERT(!is_valid_type(-1)); - TS_ASSERT(!is_valid_type(0)); - TS_ASSERT(is_valid_type(0, true)); - TS_ASSERT(is_valid_type(1)); - TS_ASSERT(is_valid_type(5, false)); - TS_ASSERT(is_valid_type(7, true)); - TS_ASSERT(is_valid_type(12)); - TS_ASSERT(!is_valid_type(13)); - - //looks like TS_ASSERT_EQUALS can't handle abstract classes... - TS_ASSERT(*tag::create(tag_type::Byte) == tag_byte()); - TS_ASSERT_THROWS(tag::create(tag_type::Null), std::invalid_argument); - TS_ASSERT_THROWS(tag::create(tag_type::End), std::invalid_argument); - - tag_string tstr("foo"); - auto cl = tstr.clone(); - TS_ASSERT_EQUALS(tstr.get(), "foo"); - TS_ASSERT(tstr == *cl); - - cl = std::move(tstr).clone(); - TS_ASSERT(*cl == tag_string("foo")); - TS_ASSERT(*cl != tag_string("bar")); - - cl = std::move(*cl).move_clone(); - TS_ASSERT(*cl == tag_string("foo")); - - tstr.assign(tag_string("bar")); - TS_ASSERT_THROWS(tstr.assign(tag_int(6)), std::bad_cast); - TS_ASSERT_EQUALS(tstr.get(), "bar"); - - TS_ASSERT_EQUALS(&tstr.as(), &tstr); - TS_ASSERT_THROWS(tstr.as(), std::bad_cast); - } - - void test_get_type() - { - TS_ASSERT_EQUALS(tag_byte().get_type() , tag_type::Byte); - TS_ASSERT_EQUALS(tag_short().get_type() , tag_type::Short); - TS_ASSERT_EQUALS(tag_int().get_type() , tag_type::Int); - TS_ASSERT_EQUALS(tag_long().get_type() , tag_type::Long); - TS_ASSERT_EQUALS(tag_float().get_type() , tag_type::Float); - TS_ASSERT_EQUALS(tag_double().get_type() , tag_type::Double); - TS_ASSERT_EQUALS(tag_byte_array().get_type(), tag_type::Byte_Array); - TS_ASSERT_EQUALS(tag_string().get_type() , tag_type::String); - TS_ASSERT_EQUALS(tag_list().get_type() , tag_type::List); - TS_ASSERT_EQUALS(tag_compound().get_type() , tag_type::Compound); - TS_ASSERT_EQUALS(tag_int_array().get_type() , tag_type::Int_Array); - TS_ASSERT_EQUALS(tag_long_array().get_type(), tag_type::Long_Array); - } - - void test_tag_primitive() - { - tag_int tag(6); - TS_ASSERT_EQUALS(tag.get(), 6); - int& ref = tag; - ref = 12; - TS_ASSERT(tag == 12); - TS_ASSERT(tag != 6); - tag.set(24); - TS_ASSERT_EQUALS(ref, 24); - tag = 7; - TS_ASSERT_EQUALS(static_cast(tag), 7); - - TS_ASSERT_EQUALS(tag, tag_int(7)); - TS_ASSERT_DIFFERS(tag_float(2.5), tag_float(-2.5)); - TS_ASSERT_DIFFERS(tag_float(2.5), tag_double(2.5)); - - TS_ASSERT(tag_double() == 0.0); - - TS_ASSERT_EQUALS(tag_byte(INT8_MAX).get(), INT8_MAX); - TS_ASSERT_EQUALS(tag_byte(INT8_MIN).get(), INT8_MIN); - TS_ASSERT_EQUALS(tag_short(INT16_MAX).get(), INT16_MAX); - TS_ASSERT_EQUALS(tag_short(INT16_MIN).get(), INT16_MIN); - TS_ASSERT_EQUALS(tag_int(INT32_MAX).get(), INT32_MAX); - TS_ASSERT_EQUALS(tag_int(INT32_MIN).get(), INT32_MIN); - TS_ASSERT_EQUALS(tag_long(INT64_MAX).get(), INT64_MAX); - TS_ASSERT_EQUALS(tag_long(INT64_MIN).get(), INT64_MIN); - } - - void test_tag_string() - { - tag_string tag("foo"); - TS_ASSERT_EQUALS(tag.get(), "foo"); - std::string& ref = tag; - ref = "bar"; - TS_ASSERT_EQUALS(tag.get(), "bar"); - TS_ASSERT_DIFFERS(tag.get(), "foo"); - tag.set("baz"); - TS_ASSERT_EQUALS(ref, "baz"); - tag = "quux"; - TS_ASSERT_EQUALS("quux", static_cast(tag)); - std::string str("foo"); - tag = str; - TS_ASSERT_EQUALS(tag.get(),str); - - TS_ASSERT_EQUALS(tag_string(str).get(), "foo"); - TS_ASSERT_EQUALS(tag_string().get(), ""); - } - - void test_tag_compound() - { - tag_compound comp{ - {"foo", int16_t(12)}, - {"bar", "baz"}, - {"baz", -2.0}, - {"list", tag_list{16, 17}} - }; - - //Test assignments and conversions, and exceptions on bad conversions - TS_ASSERT_EQUALS(comp["foo"].get_type(), tag_type::Short); - TS_ASSERT_EQUALS(static_cast(comp["foo"]), 12); - TS_ASSERT_EQUALS(static_cast(comp.at("foo")), int16_t(12)); - TS_ASSERT(comp["foo"] == tag_short(12)); - TS_ASSERT_THROWS(static_cast(comp["foo"]), std::bad_cast); - TS_ASSERT_THROWS(static_cast(comp["foo"]), std::bad_cast); - - TS_ASSERT_THROWS(comp["foo"] = 32, std::bad_cast); - comp["foo"] = int8_t(32); - TS_ASSERT_EQUALS(static_cast(comp["foo"]), 32); - - TS_ASSERT_EQUALS(comp["bar"].get_type(), tag_type::String); - TS_ASSERT_EQUALS(static_cast(comp["bar"]), "baz"); - TS_ASSERT_THROWS(static_cast(comp["bar"]), std::bad_cast); - - TS_ASSERT_THROWS(comp["bar"] = -128, std::bad_cast); - comp["bar"] = "barbaz"; - TS_ASSERT_EQUALS(static_cast(comp["bar"]), "barbaz"); - - TS_ASSERT_EQUALS(comp["baz"].get_type(), tag_type::Double); - TS_ASSERT_EQUALS(static_cast(comp["baz"]), -2.0); - TS_ASSERT_THROWS(static_cast(comp["baz"]), std::bad_cast); - - //Test nested access - comp["quux"] = tag_compound{{"Hello", "World"}, {"zero", 0}}; - TS_ASSERT_EQUALS(comp.at("quux").get_type(), tag_type::Compound); - TS_ASSERT_EQUALS(static_cast(comp["quux"].at("Hello")), "World"); - TS_ASSERT_EQUALS(static_cast(comp["quux"]["Hello"]), "World"); - TS_ASSERT(comp["list"][1] == tag_int(17)); - - TS_ASSERT_THROWS(comp.at("nothing"), std::out_of_range); - - //Test equality comparisons - tag_compound comp2{ - {"foo", int16_t(32)}, - {"bar", "barbaz"}, - {"baz", -2.0}, - {"quux", tag_compound{{"Hello", "World"}, {"zero", 0}}}, - {"list", tag_list{16, 17}} - }; - TS_ASSERT(comp == comp2); - TS_ASSERT(comp != dynamic_cast(comp2["quux"].get())); - TS_ASSERT(comp != comp2["quux"]); - TS_ASSERT(dynamic_cast(comp["quux"].get()) == comp2["quux"]); - - //Test whether begin() through end() goes through all the keys and their - //values. The order of iteration is irrelevant there. - std::set keys{"bar", "baz", "foo", "list", "quux"}; - TS_ASSERT_EQUALS(comp2.size(), keys.size()); - unsigned int i = 0; - for(const std::pair& val: comp2) - { - TS_ASSERT_LESS_THAN(i, comp2.size()); - TS_ASSERT(keys.count(val.first)); - TS_ASSERT(val.second == comp2[val.first]); - ++i; - } - TS_ASSERT_EQUALS(i, comp2.size()); - - //Test erasing and has_key - TS_ASSERT_EQUALS(comp.erase("nothing"), false); - TS_ASSERT(comp.has_key("quux")); - TS_ASSERT(comp.has_key("quux", tag_type::Compound)); - TS_ASSERT(!comp.has_key("quux", tag_type::List)); - TS_ASSERT(!comp.has_key("quux", tag_type::Null)); - - TS_ASSERT_EQUALS(comp.erase("quux"), true); - TS_ASSERT(!comp.has_key("quux")); - TS_ASSERT(!comp.has_key("quux", tag_type::Compound)); - TS_ASSERT(!comp.has_key("quux", tag_type::Null)); - - comp.clear(); - TS_ASSERT(comp == tag_compound{}); - - //Test inserting values - TS_ASSERT_EQUALS(comp.put("abc", tag_double(6.0)).second, true); - TS_ASSERT_EQUALS(comp.put("abc", tag_long(-28)).second, false); - TS_ASSERT_EQUALS(comp.insert("ghi", tag_string("world")).second, true); - TS_ASSERT_EQUALS(comp.insert("abc", tag_string("hello")).second, false); - TS_ASSERT_EQUALS(comp.emplace("def", "ghi").second, true); - TS_ASSERT_EQUALS(comp.emplace("def", 4).second, false); - TS_ASSERT((comp == tag_compound{ - {"abc", tag_long(-28)}, - {"def", tag_byte(4)}, - {"ghi", tag_string("world")} - })); - } - - void test_value() - { - value val1; - value val2(make_unique(42)); - value val3(tag_int(42)); - - TS_ASSERT(!val1 && val2 && val3); - TS_ASSERT(val1 == val1); - TS_ASSERT(val1 != val2); - TS_ASSERT(val2 == val3); - TS_ASSERT(val3 == val3); - - value valstr(tag_string("foo")); - TS_ASSERT_EQUALS(static_cast(valstr), "foo"); - valstr = "bar"; - TS_ASSERT_THROWS(valstr = 5, std::bad_cast); - TS_ASSERT_EQUALS(static_cast(valstr), "bar"); - TS_ASSERT(valstr.as() == "bar"); - TS_ASSERT_EQUALS(&valstr.as(), &valstr.get()); - TS_ASSERT_THROWS(valstr.as(), std::bad_cast); - - val1 = int64_t(42); - TS_ASSERT(val2 != val1); - - TS_ASSERT_THROWS(val2 = int64_t(12), std::bad_cast); - TS_ASSERT_EQUALS(static_cast(val2), 42); - tag_int* ptr = dynamic_cast(val2.get_ptr().get()); - TS_ASSERT(*ptr == 42); - val2 = 52; - TS_ASSERT_EQUALS(static_cast(val2), 52); - TS_ASSERT(*ptr == 52); - - TS_ASSERT_THROWS(val1["foo"], std::bad_cast); - TS_ASSERT_THROWS(val1.at("foo"), std::bad_cast); - - val3 = 52; - TS_ASSERT(val2 == val3); - TS_ASSERT(val2.get_ptr() != val3.get_ptr()); - - val3 = std::move(val2); - TS_ASSERT(val3 == tag_int(52)); - TS_ASSERT(!val2); - - tag_int& tag = dynamic_cast(val3.get()); - TS_ASSERT(tag == tag_int(52)); - tag = 21; - TS_ASSERT_EQUALS(static_cast(val3), 21); - val1.set_ptr(std::move(val3.get_ptr())); - TS_ASSERT(val1.as() == 21); - - TS_ASSERT_EQUALS(val1.get_type(), tag_type::Int); - TS_ASSERT_EQUALS(val2.get_type(), tag_type::Null); - TS_ASSERT_EQUALS(val3.get_type(), tag_type::Null); - - val2 = val1; - val1 = val3; - TS_ASSERT(!val1 && val2 && !val3); - TS_ASSERT(val1.get_ptr() == nullptr); - TS_ASSERT(val2.get() == tag_int(21)); - TS_ASSERT(value(val1) == val1); - TS_ASSERT(value(val2) == val2); - val1 = val1; - val2 = val2; - TS_ASSERT(!val1); - TS_ASSERT(val1 == value_initializer(nullptr)); - TS_ASSERT(val2 == tag_int(21)); - - val3 = tag_short(2); - TS_ASSERT_THROWS(val3 = tag_string("foo"), std::bad_cast); - TS_ASSERT(val3.get() == tag_short(2)); - - val2.set_ptr(make_unique("foo")); - TS_ASSERT(val2 == tag_string("foo")); - } - - void test_tag_list() - { - tag_list list; - TS_ASSERT_EQUALS(list.el_type(), tag_type::Null); - TS_ASSERT_THROWS(list.push_back(value(nullptr)), std::invalid_argument); - - list.emplace_back("foo"); - TS_ASSERT_EQUALS(list.el_type(), tag_type::String); - list.push_back(tag_string("bar")); - TS_ASSERT_THROWS(list.push_back(tag_int(42)), std::invalid_argument); - TS_ASSERT_THROWS(list.emplace_back(), std::invalid_argument); - - TS_ASSERT((list == tag_list{"foo", "bar"})); - TS_ASSERT(list[0] == tag_string("foo")); - TS_ASSERT_EQUALS(static_cast(list.at(1)), "bar"); - - TS_ASSERT_EQUALS(list.size(), 2u); - TS_ASSERT_THROWS(list.at(2), std::out_of_range); - TS_ASSERT_THROWS(list.at(-1), std::out_of_range); - - list.set(1, value(tag_string("baz"))); - TS_ASSERT_THROWS(list.set(1, value(nullptr)), std::invalid_argument); - TS_ASSERT_THROWS(list.set(1, value(tag_int(-42))), std::invalid_argument); - TS_ASSERT_EQUALS(static_cast(list[1]), "baz"); - - TS_ASSERT_EQUALS(list.size(), 2u); - tag_string values[] = {"foo", "baz"}; - TS_ASSERT_EQUALS(list.end() - list.begin(), int(list.size())); - TS_ASSERT(std::equal(list.begin(), list.end(), values)); - - list.pop_back(); - TS_ASSERT(list == tag_list{"foo"}); - TS_ASSERT(list == tag_list::of({"foo"})); - TS_ASSERT(tag_list::of({"foo"}) == tag_list{"foo"}); - TS_ASSERT((list != tag_list{2, 3, 5, 7})); - - list.clear(); - TS_ASSERT_EQUALS(list.size(), 0u); - TS_ASSERT_EQUALS(list.el_type(), tag_type::String) - TS_ASSERT_THROWS(list.push_back(tag_short(25)), std::invalid_argument); - TS_ASSERT_THROWS(list.push_back(value(nullptr)), std::invalid_argument); - - list.reset(); - TS_ASSERT_EQUALS(list.el_type(), tag_type::Null); - list.emplace_back(17); - TS_ASSERT_EQUALS(list.el_type(), tag_type::Int); - - list.reset(tag_type::Float); - TS_ASSERT_EQUALS(list.el_type(), tag_type::Float); - list.emplace_back(17.0f); - TS_ASSERT(list == tag_list({17.0f})); - - TS_ASSERT(tag_list() != tag_list(tag_type::Int)); - TS_ASSERT(tag_list() == tag_list()); - TS_ASSERT(tag_list(tag_type::Short) != tag_list(tag_type::Int)); - TS_ASSERT(tag_list(tag_type::Short) == tag_list(tag_type::Short)); - - tag_list short_list = tag_list::of({25, 36}); - TS_ASSERT_EQUALS(short_list.el_type(), tag_type::Short); - TS_ASSERT((short_list == tag_list{int16_t(25), int16_t(36)})); - TS_ASSERT((short_list != tag_list{25, 36})); - TS_ASSERT((short_list == tag_list{value(tag_short(25)), value(tag_short(36))})); - - TS_ASSERT_THROWS((tag_list{value(tag_byte(4)), value(tag_int(5))}), std::invalid_argument); - TS_ASSERT_THROWS((tag_list{value(nullptr), value(tag_int(6))}), std::invalid_argument); - TS_ASSERT_THROWS((tag_list{value(tag_int(7)), value(tag_int(8)), value(nullptr)}), std::invalid_argument); - TS_ASSERT_EQUALS((tag_list(std::initializer_list{})).el_type(), tag_type::Null); - TS_ASSERT_EQUALS((tag_list{2, 3, 5, 7}).el_type(), tag_type::Int); - } - - void test_tag_byte_array() - { - std::vector vec{1, 2, 127, -128}; - tag_byte_array arr{1, 2, 127, -128}; - TS_ASSERT_EQUALS(arr.size(), 4u); - TS_ASSERT(arr.at(0) == 1 && arr[1] == 2 && arr[2] == 127 && arr.at(3) == -128); - TS_ASSERT_THROWS(arr.at(-1), std::out_of_range); - TS_ASSERT_THROWS(arr.at(4), std::out_of_range); - - TS_ASSERT(arr.get() == vec); - TS_ASSERT(arr == tag_byte_array(std::vector(vec))); - - arr.push_back(42); - vec.push_back(42); - - TS_ASSERT_EQUALS(arr.size(), 5u); - TS_ASSERT_EQUALS(arr.end() - arr.begin(), int(arr.size())); - TS_ASSERT(std::equal(arr.begin(), arr.end(), vec.begin())); - - arr.pop_back(); - arr.pop_back(); - TS_ASSERT_EQUALS(arr.size(), 3u); - TS_ASSERT((arr == tag_byte_array{1, 2, 127})); - TS_ASSERT((arr != tag_int_array{1, 2, 127})); - TS_ASSERT((arr != tag_long_array{1, 2, 127})); - TS_ASSERT((arr != tag_byte_array{1, 2, -1})); - - arr.clear(); - TS_ASSERT(arr == tag_byte_array()); - } - - void test_tag_int_array() - { - std::vector vec{100, 200, INT32_MAX, INT32_MIN}; - tag_int_array arr{100, 200, INT32_MAX, INT32_MIN}; - TS_ASSERT_EQUALS(arr.size(), 4u); - TS_ASSERT(arr.at(0) == 100 && arr[1] == 200 && arr[2] == INT32_MAX && arr.at(3) == INT32_MIN); - TS_ASSERT_THROWS(arr.at(-1), std::out_of_range); - TS_ASSERT_THROWS(arr.at(4), std::out_of_range); - - TS_ASSERT(arr.get() == vec); - TS_ASSERT(arr == tag_int_array(std::vector(vec))); - - arr.push_back(42); - vec.push_back(42); - - TS_ASSERT_EQUALS(arr.size(), 5u); - TS_ASSERT_EQUALS(arr.end() - arr.begin(), int(arr.size())); - TS_ASSERT(std::equal(arr.begin(), arr.end(), vec.begin())); - - arr.pop_back(); - arr.pop_back(); - TS_ASSERT_EQUALS(arr.size(), 3u); - TS_ASSERT((arr == tag_int_array{100, 200, INT32_MAX})); - TS_ASSERT((arr != tag_int_array{100, -56, -1})); - - arr.clear(); - TS_ASSERT(arr == tag_int_array()); - } - - void test_tag_long_array() - { - std::vector vec{100, 200, INT64_MAX, INT64_MIN}; - tag_long_array arr{100, 200, INT64_MAX, INT64_MIN}; - TS_ASSERT_EQUALS(arr.size(), 4u); - TS_ASSERT(arr.at(0) == 100 && arr[1] == 200 && arr[2] == INT64_MAX && arr.at(3) == INT64_MIN); - TS_ASSERT_THROWS(arr.at(-1), std::out_of_range); - TS_ASSERT_THROWS(arr.at(4), std::out_of_range); - - TS_ASSERT(arr.get() == vec); - TS_ASSERT(arr == tag_long_array(std::vector(vec))); - - arr.push_back(42); - vec.push_back(42); - - TS_ASSERT_EQUALS(arr.size(), 5u); - TS_ASSERT_EQUALS(arr.end() - arr.begin(), int(arr.size())); - TS_ASSERT(std::equal(arr.begin(), arr.end(), vec.begin())); - - arr.pop_back(); - arr.pop_back(); - TS_ASSERT_EQUALS(arr.size(), 3u); - TS_ASSERT((arr == tag_long_array{100, 200, INT64_MAX})); - TS_ASSERT((arr != tag_long_array{100, -56, -1})); - - arr.clear(); - TS_ASSERT(arr == tag_long_array()); - } - - void test_visitor() - { - struct : public nbt_visitor - { - tag* visited = nullptr; - - void visit(tag_byte& tag) { visited = &tag; } - void visit(tag_short& tag) { visited = &tag; } - void visit(tag_int& tag) { visited = &tag; } - void visit(tag_long& tag) { visited = &tag; } - void visit(tag_float& tag) { visited = &tag; } - void visit(tag_double& tag) { visited = &tag; } - void visit(tag_byte_array& tag) { visited = &tag; } - void visit(tag_string& tag) { visited = &tag; } - void visit(tag_list& tag) { visited = &tag; } - void visit(tag_compound& tag) { visited = &tag; } - void visit(tag_int_array& tag) { visited = &tag; } - void visit(tag_long_array& tag) { visited = &tag; } - } v; - - tag_byte b; b.accept(v); TS_ASSERT_EQUALS(v.visited, &b); - tag_short s; s.accept(v); TS_ASSERT_EQUALS(v.visited, &s); - tag_int i; i.accept(v); TS_ASSERT_EQUALS(v.visited, &i); - tag_long l; l.accept(v); TS_ASSERT_EQUALS(v.visited, &l); - tag_float f; f.accept(v); TS_ASSERT_EQUALS(v.visited, &f); - tag_double d; d.accept(v); TS_ASSERT_EQUALS(v.visited, &d); - tag_byte_array ba; ba.accept(v); TS_ASSERT_EQUALS(v.visited, &ba); - tag_string st; st.accept(v); TS_ASSERT_EQUALS(v.visited, &st); - tag_list ls; ls.accept(v); TS_ASSERT_EQUALS(v.visited, &ls); - tag_compound c; c.accept(v); TS_ASSERT_EQUALS(v.visited, &c); - tag_int_array ia; ia.accept(v); TS_ASSERT_EQUALS(v.visited, &ia); - tag_long_array la; la.accept(v); TS_ASSERT_EQUALS(v.visited, &la); - } + public: + void test_tag() + { + TS_ASSERT(!is_valid_type(-1)); + TS_ASSERT(!is_valid_type(0)); + TS_ASSERT(is_valid_type(0, true)); + TS_ASSERT(is_valid_type(1)); + TS_ASSERT(is_valid_type(5, false)); + TS_ASSERT(is_valid_type(7, true)); + TS_ASSERT(is_valid_type(12)); + TS_ASSERT(!is_valid_type(13)); + + // looks like TS_ASSERT_EQUALS can't handle abstract classes... + TS_ASSERT(*tag::create(tag_type::Byte) == tag_byte()); + TS_ASSERT_THROWS(tag::create(tag_type::Null), std::invalid_argument); + TS_ASSERT_THROWS(tag::create(tag_type::End), std::invalid_argument); + + tag_string tstr("foo"); + auto cl = tstr.clone(); + TS_ASSERT_EQUALS(tstr.get(), "foo"); + TS_ASSERT(tstr == *cl); + + cl = std::move(tstr).clone(); + TS_ASSERT(*cl == tag_string("foo")); + TS_ASSERT(*cl != tag_string("bar")); + + cl = std::move(*cl).move_clone(); + TS_ASSERT(*cl == tag_string("foo")); + + tstr.assign(tag_string("bar")); + TS_ASSERT_THROWS(tstr.assign(tag_int(6)), std::bad_cast); + TS_ASSERT_EQUALS(tstr.get(), "bar"); + + TS_ASSERT_EQUALS(&tstr.as(), &tstr); + TS_ASSERT_THROWS(tstr.as(), std::bad_cast); + } + + void test_get_type() + { + TS_ASSERT_EQUALS(tag_byte().get_type(), tag_type::Byte); + TS_ASSERT_EQUALS(tag_short().get_type(), tag_type::Short); + TS_ASSERT_EQUALS(tag_int().get_type(), tag_type::Int); + TS_ASSERT_EQUALS(tag_long().get_type(), tag_type::Long); + TS_ASSERT_EQUALS(tag_float().get_type(), tag_type::Float); + TS_ASSERT_EQUALS(tag_double().get_type(), tag_type::Double); + TS_ASSERT_EQUALS(tag_byte_array().get_type(), tag_type::Byte_Array); + TS_ASSERT_EQUALS(tag_string().get_type(), tag_type::String); + TS_ASSERT_EQUALS(tag_list().get_type(), tag_type::List); + TS_ASSERT_EQUALS(tag_compound().get_type(), tag_type::Compound); + TS_ASSERT_EQUALS(tag_int_array().get_type(), tag_type::Int_Array); + TS_ASSERT_EQUALS(tag_long_array().get_type(), tag_type::Long_Array); + } + + void test_tag_primitive() + { + tag_int tag(6); + TS_ASSERT_EQUALS(tag.get(), 6); + int& ref = tag; + ref = 12; + TS_ASSERT(tag == 12); + TS_ASSERT(tag != 6); + tag.set(24); + TS_ASSERT_EQUALS(ref, 24); + tag = 7; + TS_ASSERT_EQUALS(static_cast(tag), 7); + + TS_ASSERT_EQUALS(tag, tag_int(7)); + TS_ASSERT_DIFFERS(tag_float(2.5), tag_float(-2.5)); + TS_ASSERT_DIFFERS(tag_float(2.5), tag_double(2.5)); + + TS_ASSERT(tag_double() == 0.0); + + TS_ASSERT_EQUALS(tag_byte(INT8_MAX).get(), INT8_MAX); + TS_ASSERT_EQUALS(tag_byte(INT8_MIN).get(), INT8_MIN); + TS_ASSERT_EQUALS(tag_short(INT16_MAX).get(), INT16_MAX); + TS_ASSERT_EQUALS(tag_short(INT16_MIN).get(), INT16_MIN); + TS_ASSERT_EQUALS(tag_int(INT32_MAX).get(), INT32_MAX); + TS_ASSERT_EQUALS(tag_int(INT32_MIN).get(), INT32_MIN); + TS_ASSERT_EQUALS(tag_long(INT64_MAX).get(), INT64_MAX); + TS_ASSERT_EQUALS(tag_long(INT64_MIN).get(), INT64_MIN); + } + + void test_tag_string() + { + tag_string tag("foo"); + TS_ASSERT_EQUALS(tag.get(), "foo"); + std::string& ref = tag; + ref = "bar"; + TS_ASSERT_EQUALS(tag.get(), "bar"); + TS_ASSERT_DIFFERS(tag.get(), "foo"); + tag.set("baz"); + TS_ASSERT_EQUALS(ref, "baz"); + tag = "quux"; + TS_ASSERT_EQUALS("quux", static_cast(tag)); + std::string str("foo"); + tag = str; + TS_ASSERT_EQUALS(tag.get(), str); + + TS_ASSERT_EQUALS(tag_string(str).get(), "foo"); + TS_ASSERT_EQUALS(tag_string().get(), ""); + } + + void test_tag_compound() + { + tag_compound comp{{"foo", int16_t(12)}, + {"bar", "baz"}, + {"baz", -2.0}, + {"list", tag_list{16, 17}}}; + + // Test assignments and conversions, and exceptions on bad conversions + TS_ASSERT_EQUALS(comp["foo"].get_type(), tag_type::Short); + TS_ASSERT_EQUALS(static_cast(comp["foo"]), 12); + TS_ASSERT_EQUALS(static_cast(comp.at("foo")), int16_t(12)); + TS_ASSERT(comp["foo"] == tag_short(12)); + TS_ASSERT_THROWS(static_cast(comp["foo"]), std::bad_cast); + TS_ASSERT_THROWS(static_cast(comp["foo"]), std::bad_cast); + + TS_ASSERT_THROWS(comp["foo"] = 32, std::bad_cast); + comp["foo"] = int8_t(32); + TS_ASSERT_EQUALS(static_cast(comp["foo"]), 32); + + TS_ASSERT_EQUALS(comp["bar"].get_type(), tag_type::String); + TS_ASSERT_EQUALS(static_cast(comp["bar"]), "baz"); + TS_ASSERT_THROWS(static_cast(comp["bar"]), std::bad_cast); + + TS_ASSERT_THROWS(comp["bar"] = -128, std::bad_cast); + comp["bar"] = "barbaz"; + TS_ASSERT_EQUALS(static_cast(comp["bar"]), "barbaz"); + + TS_ASSERT_EQUALS(comp["baz"].get_type(), tag_type::Double); + TS_ASSERT_EQUALS(static_cast(comp["baz"]), -2.0); + TS_ASSERT_THROWS(static_cast(comp["baz"]), std::bad_cast); + + // Test nested access + comp["quux"] = tag_compound{{"Hello", "World"}, {"zero", 0}}; + TS_ASSERT_EQUALS(comp.at("quux").get_type(), tag_type::Compound); + TS_ASSERT_EQUALS(static_cast(comp["quux"].at("Hello")), + "World"); + TS_ASSERT_EQUALS(static_cast(comp["quux"]["Hello"]), + "World"); + TS_ASSERT(comp["list"][1] == tag_int(17)); + + TS_ASSERT_THROWS(comp.at("nothing"), std::out_of_range); + + // Test equality comparisons + tag_compound comp2{ + {"foo", int16_t(32)}, + {"bar", "barbaz"}, + {"baz", -2.0}, + {"quux", tag_compound{{"Hello", "World"}, {"zero", 0}}}, + {"list", tag_list{16, 17}}}; + TS_ASSERT(comp == comp2); + TS_ASSERT(comp != + dynamic_cast(comp2["quux"].get())); + TS_ASSERT(comp != comp2["quux"]); + TS_ASSERT(dynamic_cast(comp["quux"].get()) == + comp2["quux"]); + + // Test whether begin() through end() goes through all the keys and + // their values. The order of iteration is irrelevant there. + std::set keys{"bar", "baz", "foo", "list", "quux"}; + TS_ASSERT_EQUALS(comp2.size(), keys.size()); + unsigned int i = 0; + for (const std::pair& val : comp2) { + TS_ASSERT_LESS_THAN(i, comp2.size()); + TS_ASSERT(keys.count(val.first)); + TS_ASSERT(val.second == comp2[val.first]); + ++i; + } + TS_ASSERT_EQUALS(i, comp2.size()); + + // Test erasing and has_key + TS_ASSERT_EQUALS(comp.erase("nothing"), false); + TS_ASSERT(comp.has_key("quux")); + TS_ASSERT(comp.has_key("quux", tag_type::Compound)); + TS_ASSERT(!comp.has_key("quux", tag_type::List)); + TS_ASSERT(!comp.has_key("quux", tag_type::Null)); + + TS_ASSERT_EQUALS(comp.erase("quux"), true); + TS_ASSERT(!comp.has_key("quux")); + TS_ASSERT(!comp.has_key("quux", tag_type::Compound)); + TS_ASSERT(!comp.has_key("quux", tag_type::Null)); + + comp.clear(); + TS_ASSERT(comp == tag_compound{}); + + // Test inserting values + TS_ASSERT_EQUALS(comp.put("abc", tag_double(6.0)).second, true); + TS_ASSERT_EQUALS(comp.put("abc", tag_long(-28)).second, false); + TS_ASSERT_EQUALS(comp.insert("ghi", tag_string("world")).second, true); + TS_ASSERT_EQUALS(comp.insert("abc", tag_string("hello")).second, false); + TS_ASSERT_EQUALS(comp.emplace("def", "ghi").second, true); + TS_ASSERT_EQUALS(comp.emplace("def", 4).second, false); + TS_ASSERT((comp == tag_compound{{"abc", tag_long(-28)}, + {"def", tag_byte(4)}, + {"ghi", tag_string("world")}})); + } + + void test_value() + { + value val1; + value val2(make_unique(42)); + value val3(tag_int(42)); + + TS_ASSERT(!val1 && val2 && val3); + TS_ASSERT(val1 == val1); + TS_ASSERT(val1 != val2); + TS_ASSERT(val2 == val3); + TS_ASSERT(val3 == val3); + + value valstr(tag_string("foo")); + TS_ASSERT_EQUALS(static_cast(valstr), "foo"); + valstr = "bar"; + TS_ASSERT_THROWS(valstr = 5, std::bad_cast); + TS_ASSERT_EQUALS(static_cast(valstr), "bar"); + TS_ASSERT(valstr.as() == "bar"); + TS_ASSERT_EQUALS(&valstr.as(), &valstr.get()); + TS_ASSERT_THROWS(valstr.as(), std::bad_cast); + + val1 = int64_t(42); + TS_ASSERT(val2 != val1); + + TS_ASSERT_THROWS(val2 = int64_t(12), std::bad_cast); + TS_ASSERT_EQUALS(static_cast(val2), 42); + tag_int* ptr = dynamic_cast(val2.get_ptr().get()); + TS_ASSERT(*ptr == 42); + val2 = 52; + TS_ASSERT_EQUALS(static_cast(val2), 52); + TS_ASSERT(*ptr == 52); + + TS_ASSERT_THROWS(val1["foo"], std::bad_cast); + TS_ASSERT_THROWS(val1.at("foo"), std::bad_cast); + + val3 = 52; + TS_ASSERT(val2 == val3); + TS_ASSERT(val2.get_ptr() != val3.get_ptr()); + + val3 = std::move(val2); + TS_ASSERT(val3 == tag_int(52)); + TS_ASSERT(!val2); + + tag_int& tag = dynamic_cast(val3.get()); + TS_ASSERT(tag == tag_int(52)); + tag = 21; + TS_ASSERT_EQUALS(static_cast(val3), 21); + val1.set_ptr(std::move(val3.get_ptr())); + TS_ASSERT(val1.as() == 21); + + TS_ASSERT_EQUALS(val1.get_type(), tag_type::Int); + TS_ASSERT_EQUALS(val2.get_type(), tag_type::Null); + TS_ASSERT_EQUALS(val3.get_type(), tag_type::Null); + + val2 = val1; + val1 = val3; + TS_ASSERT(!val1 && val2 && !val3); + TS_ASSERT(val1.get_ptr() == nullptr); + TS_ASSERT(val2.get() == tag_int(21)); + TS_ASSERT(value(val1) == val1); + TS_ASSERT(value(val2) == val2); + val1 = val1; + val2 = val2; + TS_ASSERT(!val1); + TS_ASSERT(val1 == value_initializer(nullptr)); + TS_ASSERT(val2 == tag_int(21)); + + val3 = tag_short(2); + TS_ASSERT_THROWS(val3 = tag_string("foo"), std::bad_cast); + TS_ASSERT(val3.get() == tag_short(2)); + + val2.set_ptr(make_unique("foo")); + TS_ASSERT(val2 == tag_string("foo")); + } + + void test_tag_list() + { + tag_list list; + TS_ASSERT_EQUALS(list.el_type(), tag_type::Null); + TS_ASSERT_THROWS(list.push_back(value(nullptr)), std::invalid_argument); + + list.emplace_back("foo"); + TS_ASSERT_EQUALS(list.el_type(), tag_type::String); + list.push_back(tag_string("bar")); + TS_ASSERT_THROWS(list.push_back(tag_int(42)), std::invalid_argument); + TS_ASSERT_THROWS(list.emplace_back(), + std::invalid_argument); + + TS_ASSERT((list == tag_list{"foo", "bar"})); + TS_ASSERT(list[0] == tag_string("foo")); + TS_ASSERT_EQUALS(static_cast(list.at(1)), "bar"); + + TS_ASSERT_EQUALS(list.size(), 2u); + TS_ASSERT_THROWS(list.at(2), std::out_of_range); + TS_ASSERT_THROWS(list.at(-1), std::out_of_range); + + list.set(1, value(tag_string("baz"))); + TS_ASSERT_THROWS(list.set(1, value(nullptr)), std::invalid_argument); + TS_ASSERT_THROWS(list.set(1, value(tag_int(-42))), + std::invalid_argument); + TS_ASSERT_EQUALS(static_cast(list[1]), "baz"); + + TS_ASSERT_EQUALS(list.size(), 2u); + tag_string values[] = {"foo", "baz"}; + TS_ASSERT_EQUALS(list.end() - list.begin(), int(list.size())); + TS_ASSERT(std::equal(list.begin(), list.end(), values)); + + list.pop_back(); + TS_ASSERT(list == tag_list{"foo"}); + TS_ASSERT(list == tag_list::of({"foo"})); + TS_ASSERT(tag_list::of({"foo"}) == tag_list{"foo"}); + TS_ASSERT((list != tag_list{2, 3, 5, 7})); + + list.clear(); + TS_ASSERT_EQUALS(list.size(), 0u); + TS_ASSERT_EQUALS(list.el_type(), tag_type::String) + TS_ASSERT_THROWS(list.push_back(tag_short(25)), std::invalid_argument); + TS_ASSERT_THROWS(list.push_back(value(nullptr)), std::invalid_argument); + + list.reset(); + TS_ASSERT_EQUALS(list.el_type(), tag_type::Null); + list.emplace_back(17); + TS_ASSERT_EQUALS(list.el_type(), tag_type::Int); + + list.reset(tag_type::Float); + TS_ASSERT_EQUALS(list.el_type(), tag_type::Float); + list.emplace_back(17.0f); + TS_ASSERT(list == tag_list({17.0f})); + + TS_ASSERT(tag_list() != tag_list(tag_type::Int)); + TS_ASSERT(tag_list() == tag_list()); + TS_ASSERT(tag_list(tag_type::Short) != tag_list(tag_type::Int)); + TS_ASSERT(tag_list(tag_type::Short) == tag_list(tag_type::Short)); + + tag_list short_list = tag_list::of({25, 36}); + TS_ASSERT_EQUALS(short_list.el_type(), tag_type::Short); + TS_ASSERT((short_list == tag_list{int16_t(25), int16_t(36)})); + TS_ASSERT((short_list != tag_list{25, 36})); + TS_ASSERT((short_list == + tag_list{value(tag_short(25)), value(tag_short(36))})); + + TS_ASSERT_THROWS((tag_list{value(tag_byte(4)), value(tag_int(5))}), + std::invalid_argument); + TS_ASSERT_THROWS((tag_list{value(nullptr), value(tag_int(6))}), + std::invalid_argument); + TS_ASSERT_THROWS( + (tag_list{value(tag_int(7)), value(tag_int(8)), value(nullptr)}), + std::invalid_argument); + TS_ASSERT_EQUALS((tag_list(std::initializer_list{})).el_type(), + tag_type::Null); + TS_ASSERT_EQUALS((tag_list{2, 3, 5, 7}).el_type(), tag_type::Int); + } + + void test_tag_byte_array() + { + std::vector vec{1, 2, 127, -128}; + tag_byte_array arr{1, 2, 127, -128}; + TS_ASSERT_EQUALS(arr.size(), 4u); + TS_ASSERT(arr.at(0) == 1 && arr[1] == 2 && arr[2] == 127 && + arr.at(3) == -128); + TS_ASSERT_THROWS(arr.at(-1), std::out_of_range); + TS_ASSERT_THROWS(arr.at(4), std::out_of_range); + + TS_ASSERT(arr.get() == vec); + TS_ASSERT(arr == tag_byte_array(std::vector(vec))); + + arr.push_back(42); + vec.push_back(42); + + TS_ASSERT_EQUALS(arr.size(), 5u); + TS_ASSERT_EQUALS(arr.end() - arr.begin(), int(arr.size())); + TS_ASSERT(std::equal(arr.begin(), arr.end(), vec.begin())); + + arr.pop_back(); + arr.pop_back(); + TS_ASSERT_EQUALS(arr.size(), 3u); + TS_ASSERT((arr == tag_byte_array{1, 2, 127})); + TS_ASSERT((arr != tag_int_array{1, 2, 127})); + TS_ASSERT((arr != tag_long_array{1, 2, 127})); + TS_ASSERT((arr != tag_byte_array{1, 2, -1})); + + arr.clear(); + TS_ASSERT(arr == tag_byte_array()); + } + + void test_tag_int_array() + { + std::vector vec{100, 200, INT32_MAX, INT32_MIN}; + tag_int_array arr{100, 200, INT32_MAX, INT32_MIN}; + TS_ASSERT_EQUALS(arr.size(), 4u); + TS_ASSERT(arr.at(0) == 100 && arr[1] == 200 && arr[2] == INT32_MAX && + arr.at(3) == INT32_MIN); + TS_ASSERT_THROWS(arr.at(-1), std::out_of_range); + TS_ASSERT_THROWS(arr.at(4), std::out_of_range); + + TS_ASSERT(arr.get() == vec); + TS_ASSERT(arr == tag_int_array(std::vector(vec))); + + arr.push_back(42); + vec.push_back(42); + + TS_ASSERT_EQUALS(arr.size(), 5u); + TS_ASSERT_EQUALS(arr.end() - arr.begin(), int(arr.size())); + TS_ASSERT(std::equal(arr.begin(), arr.end(), vec.begin())); + + arr.pop_back(); + arr.pop_back(); + TS_ASSERT_EQUALS(arr.size(), 3u); + TS_ASSERT((arr == tag_int_array{100, 200, INT32_MAX})); + TS_ASSERT((arr != tag_int_array{100, -56, -1})); + + arr.clear(); + TS_ASSERT(arr == tag_int_array()); + } + + void test_tag_long_array() + { + std::vector vec{100, 200, INT64_MAX, INT64_MIN}; + tag_long_array arr{100, 200, INT64_MAX, INT64_MIN}; + TS_ASSERT_EQUALS(arr.size(), 4u); + TS_ASSERT(arr.at(0) == 100 && arr[1] == 200 && arr[2] == INT64_MAX && + arr.at(3) == INT64_MIN); + TS_ASSERT_THROWS(arr.at(-1), std::out_of_range); + TS_ASSERT_THROWS(arr.at(4), std::out_of_range); + + TS_ASSERT(arr.get() == vec); + TS_ASSERT(arr == tag_long_array(std::vector(vec))); + + arr.push_back(42); + vec.push_back(42); + + TS_ASSERT_EQUALS(arr.size(), 5u); + TS_ASSERT_EQUALS(arr.end() - arr.begin(), int(arr.size())); + TS_ASSERT(std::equal(arr.begin(), arr.end(), vec.begin())); + + arr.pop_back(); + arr.pop_back(); + TS_ASSERT_EQUALS(arr.size(), 3u); + TS_ASSERT((arr == tag_long_array{100, 200, INT64_MAX})); + TS_ASSERT((arr != tag_long_array{100, -56, -1})); + + arr.clear(); + TS_ASSERT(arr == tag_long_array()); + } + + void test_visitor() + { + struct : public nbt_visitor { + tag* visited = nullptr; + + void visit(tag_byte& tag) + { + visited = &tag; + } + void visit(tag_short& tag) + { + visited = &tag; + } + void visit(tag_int& tag) + { + visited = &tag; + } + void visit(tag_long& tag) + { + visited = &tag; + } + void visit(tag_float& tag) + { + visited = &tag; + } + void visit(tag_double& tag) + { + visited = &tag; + } + void visit(tag_byte_array& tag) + { + visited = &tag; + } + void visit(tag_string& tag) + { + visited = &tag; + } + void visit(tag_list& tag) + { + visited = &tag; + } + void visit(tag_compound& tag) + { + visited = &tag; + } + void visit(tag_int_array& tag) + { + visited = &tag; + } + void visit(tag_long_array& tag) + { + visited = &tag; + } + } v; + + tag_byte b; + b.accept(v); + TS_ASSERT_EQUALS(v.visited, &b); + tag_short s; + s.accept(v); + TS_ASSERT_EQUALS(v.visited, &s); + tag_int i; + i.accept(v); + TS_ASSERT_EQUALS(v.visited, &i); + tag_long l; + l.accept(v); + TS_ASSERT_EQUALS(v.visited, &l); + tag_float f; + f.accept(v); + TS_ASSERT_EQUALS(v.visited, &f); + tag_double d; + d.accept(v); + TS_ASSERT_EQUALS(v.visited, &d); + tag_byte_array ba; + ba.accept(v); + TS_ASSERT_EQUALS(v.visited, &ba); + tag_string st; + st.accept(v); + TS_ASSERT_EQUALS(v.visited, &st); + tag_list ls; + ls.accept(v); + TS_ASSERT_EQUALS(v.visited, &ls); + tag_compound c; + c.accept(v); + TS_ASSERT_EQUALS(v.visited, &c); + tag_int_array ia; + ia.accept(v); + TS_ASSERT_EQUALS(v.visited, &ia); + tag_long_array la; + la.accept(v); + TS_ASSERT_EQUALS(v.visited, &la); + } }; diff --git a/test/read_test.h b/test/read_test.h index d8750f5cb6..a4e74ac248 100644 --- a/test/read_test.h +++ b/test/read_test.h @@ -39,218 +39,242 @@ using namespace nbt; class read_test : public CxxTest::TestSuite { -public: - void test_stream_reader_big() - { - std::string input{ - 1, //tag_type::Byte - 0, //tag_type::End - 11, //tag_type::Int_Array - - 0x0a, 0x0b, 0x0c, 0x0d, //0x0a0b0c0d in Big Endian - - 0x00, 0x06, //String length in Big Endian - 'f', 'o', 'o', 'b', 'a', 'r', - - 0 //tag_type::End (invalid with allow_end = false) - }; - std::istringstream is(input); - nbt::io::stream_reader reader(is); - - TS_ASSERT_EQUALS(&reader.get_istr(), &is); - TS_ASSERT_EQUALS(reader.get_endian(), endian::big); - - TS_ASSERT_EQUALS(reader.read_type(), tag_type::Byte); - TS_ASSERT_EQUALS(reader.read_type(true), tag_type::End); - TS_ASSERT_EQUALS(reader.read_type(false), tag_type::Int_Array); - - int32_t i; - reader.read_num(i); - TS_ASSERT_EQUALS(i, 0x0a0b0c0d); - - TS_ASSERT_EQUALS(reader.read_string(), "foobar"); - - TS_ASSERT_THROWS(reader.read_type(false), io::input_error); - TS_ASSERT(!is); - is.clear(); - - //Test for invalid tag type 13 - is.str("\x0d"); - TS_ASSERT_THROWS(reader.read_type(), io::input_error); - TS_ASSERT(!is); - is.clear(); - - //Test for unexpcted EOF on numbers (input too short for int32_t) - is.str("\x03\x04"); - reader.read_num(i); - TS_ASSERT(!is); - } - - void test_stream_reader_little() - { - std::string input{ - 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, //0x0d0c0b0a09080706 in Little Endian - - 0x06, 0x00, //String length in Little Endian - 'f', 'o', 'o', 'b', 'a', 'r', - - 0x10, 0x00, //String length (intentionally too large) - 'a', 'b', 'c', 'd' //unexpected EOF - }; - std::istringstream is(input); - nbt::io::stream_reader reader(is, endian::little); - - TS_ASSERT_EQUALS(reader.get_endian(), endian::little); - - int64_t i; - reader.read_num(i); - TS_ASSERT_EQUALS(i, 0x0d0c0b0a09080706); - - TS_ASSERT_EQUALS(reader.read_string(), "foobar"); - - TS_ASSERT_THROWS(reader.read_string(), io::input_error); - TS_ASSERT(!is); - } - - //Tests if comp equals an extended variant of Notch's bigtest NBT - void verify_bigtest_structure(const tag_compound& comp) - { - TS_ASSERT_EQUALS(comp.size(), 13u); - - TS_ASSERT(comp.at("byteTest") == tag_byte(127)); - TS_ASSERT(comp.at("shortTest") == tag_short(32767)); - TS_ASSERT(comp.at("intTest") == tag_int(2147483647)); - TS_ASSERT(comp.at("longTest") == tag_long(9223372036854775807)); - TS_ASSERT(comp.at("floatTest") == tag_float(std::stof("0xff1832p-25"))); //0.4982315 - TS_ASSERT(comp.at("doubleTest") == tag_double(std::stod("0x1f8f6bbbff6a5ep-54"))); //0.493128713218231 - - //From bigtest.nbt: "the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...)" - tag_byte_array byteArrayTest; - for(int n = 0; n < 1000; ++n) - byteArrayTest.push_back((n*n*255 + n*7) % 100); - TS_ASSERT(comp.at("byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...))") == byteArrayTest); - - TS_ASSERT(comp.at("stringTest") == tag_string("HELLO WORLD THIS IS A TEST STRING \u00C5\u00C4\u00D6!")); - - TS_ASSERT(comp.at("listTest (compound)") == tag_list::of({ - {{"created-on", tag_long(1264099775885)}, {"name", "Compound tag #0"}}, - {{"created-on", tag_long(1264099775885)}, {"name", "Compound tag #1"}} - })); - TS_ASSERT(comp.at("listTest (long)") == tag_list::of({11, 12, 13, 14, 15})); - TS_ASSERT(comp.at("listTest (end)") == tag_list()); - - TS_ASSERT((comp.at("nested compound test") == tag_compound{ - {"egg", tag_compound{{"value", 0.5f}, {"name", "Eggbert"}}}, - {"ham", tag_compound{{"value", 0.75f}, {"name", "Hampus"}}} - })); - - TS_ASSERT(comp.at("intArrayTest") == tag_int_array( - {0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f})); - } - - void test_read_bigtest() - { - //Uses an extended variant of Notch's original bigtest file - std::string input(__binary_bigtest_uncompr_start, __binary_bigtest_uncompr_end); - std::istringstream file(input, std::ios::binary); - - auto pair = nbt::io::read_compound(file); - TS_ASSERT_EQUALS(pair.first, "Level"); - verify_bigtest_structure(*pair.second); - } - - void test_read_littletest() - { - //Same as bigtest, but little endian - std::string input(__binary_littletest_uncompr_start, __binary_littletest_uncompr_end); - std::istringstream file(input, std::ios::binary); - - auto pair = nbt::io::read_compound(file, endian::little); - TS_ASSERT_EQUALS(pair.first, "Level"); - TS_ASSERT_EQUALS(pair.second->get_type(), tag_type::Compound); - verify_bigtest_structure(*pair.second); - } - - void test_read_eof1() - { - std::string input(__binary_errortest_eof1_start, __binary_errortest_eof1_end); - std::istringstream file(input, std::ios::binary); - nbt::io::stream_reader reader(file); - - //EOF within a tag_double payload - TS_ASSERT(file); - TS_ASSERT_THROWS(reader.read_tag(), io::input_error); - TS_ASSERT(!file); - } - - void test_read_eof2() - { - std::string input(__binary_errortest_eof2_start, __binary_errortest_eof2_end); - std::istringstream file(input, std::ios::binary); - nbt::io::stream_reader reader(file); - - //EOF within a key in a compound - TS_ASSERT(file); - TS_ASSERT_THROWS(reader.read_tag(), io::input_error); - TS_ASSERT(!file); - } - - void test_read_errortest_noend() - { - std::string input(__binary_errortest_noend_start, __binary_errortest_noend_end); - std::istringstream file(input, std::ios::binary); - nbt::io::stream_reader reader(file); - - //Missing tag_end - TS_ASSERT(file); - TS_ASSERT_THROWS(reader.read_tag(), io::input_error); - TS_ASSERT(!file); - } - - void test_read_errortest_neg_length() - { - std::string input(__binary_errortest_neg_length_start, __binary_errortest_neg_length_end); - std::istringstream file(input, std::ios::binary); - nbt::io::stream_reader reader(file); - - //Negative list length - TS_ASSERT(file); - TS_ASSERT_THROWS(reader.read_tag(), io::input_error); - TS_ASSERT(!file); - } - - void test_read_misc() - { - std::string input(__binary_toplevel_string_start, __binary_toplevel_string_end); - std::istringstream file(input, std::ios::binary); - nbt::io::stream_reader reader(file); - - //Toplevel tag other than compound - TS_ASSERT(file); - TS_ASSERT_THROWS(reader.read_compound(), io::input_error); - TS_ASSERT(!file); - - //Rewind and try again with read_tag - file.clear(); - TS_ASSERT(file.seekg(0)); - auto pair = reader.read_tag(); - TS_ASSERT_EQUALS(pair.first, "Test (toplevel tag_string)"); - TS_ASSERT(*pair.second == tag_string( - "Even though unprovided for by NBT, the library should also handle " - "the case where the file consists of something else than tag_compound")); - } - void test_read_gzip() - { + public: + void test_stream_reader_big() + { + std::string input{ + 1, // tag_type::Byte + 0, // tag_type::End + 11, // tag_type::Int_Array + + 0x0a, 0x0b, 0x0c, 0x0d, // 0x0a0b0c0d in Big Endian + + 0x00, 0x06, // String length in Big Endian + 'f', 'o', 'o', 'b', 'a', 'r', + + 0 // tag_type::End (invalid with allow_end = false) + }; + std::istringstream is(input); + nbt::io::stream_reader reader(is); + + TS_ASSERT_EQUALS(&reader.get_istr(), &is); + TS_ASSERT_EQUALS(reader.get_endian(), endian::big); + + TS_ASSERT_EQUALS(reader.read_type(), tag_type::Byte); + TS_ASSERT_EQUALS(reader.read_type(true), tag_type::End); + TS_ASSERT_EQUALS(reader.read_type(false), tag_type::Int_Array); + + int32_t i; + reader.read_num(i); + TS_ASSERT_EQUALS(i, 0x0a0b0c0d); + + TS_ASSERT_EQUALS(reader.read_string(), "foobar"); + + TS_ASSERT_THROWS(reader.read_type(false), io::input_error); + TS_ASSERT(!is); + is.clear(); + + // Test for invalid tag type 13 + is.str("\x0d"); + TS_ASSERT_THROWS(reader.read_type(), io::input_error); + TS_ASSERT(!is); + is.clear(); + + // Test for unexpcted EOF on numbers (input too short for int32_t) + is.str("\x03\x04"); + reader.read_num(i); + TS_ASSERT(!is); + } + + void test_stream_reader_little() + { + std::string input{ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, // 0x0d0c0b0a09080706 in Little Endian + + 0x06, 0x00, // String length in Little Endian + 'f', 'o', 'o', 'b', 'a', 'r', + + 0x10, 0x00, // String length (intentionally too large) + 'a', 'b', 'c', 'd' // unexpected EOF + }; + std::istringstream is(input); + nbt::io::stream_reader reader(is, endian::little); + + TS_ASSERT_EQUALS(reader.get_endian(), endian::little); + + int64_t i; + reader.read_num(i); + TS_ASSERT_EQUALS(i, 0x0d0c0b0a09080706); + + TS_ASSERT_EQUALS(reader.read_string(), "foobar"); + + TS_ASSERT_THROWS(reader.read_string(), io::input_error); + TS_ASSERT(!is); + } + + // Tests if comp equals an extended variant of Notch's bigtest NBT + void verify_bigtest_structure(const tag_compound& comp) + { + TS_ASSERT_EQUALS(comp.size(), 13u); + + TS_ASSERT(comp.at("byteTest") == tag_byte(127)); + TS_ASSERT(comp.at("shortTest") == tag_short(32767)); + TS_ASSERT(comp.at("intTest") == tag_int(2147483647)); + TS_ASSERT(comp.at("longTest") == tag_long(9223372036854775807)); + TS_ASSERT(comp.at("floatTest") == + tag_float(std::stof("0xff1832p-25"))); // 0.4982315 + TS_ASSERT( + comp.at("doubleTest") == + tag_double(std::stod("0x1f8f6bbbff6a5ep-54"))); // 0.493128713218231 + + // From bigtest.nbt: "the first 1000 values of (n*n*255+n*7)%100, + // starting with n=0 (0, 62, 34, 16, 8, ...)" + tag_byte_array byteArrayTest; + for (int n = 0; n < 1000; ++n) + byteArrayTest.push_back((n * n * 255 + n * 7) % 100); + TS_ASSERT( + comp.at( + "byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, " + "starting with n=0 (0, 62, 34, 16, 8, ...))") == byteArrayTest); + + TS_ASSERT(comp.at("stringTest") == + tag_string( + "HELLO WORLD THIS IS A TEST STRING \u00C5\u00C4\u00D6!")); + + TS_ASSERT(comp.at("listTest (compound)") == + tag_list::of( + {{{"created-on", tag_long(1264099775885)}, + {"name", "Compound tag #0"}}, + {{"created-on", tag_long(1264099775885)}, + {"name", "Compound tag #1"}}})); + TS_ASSERT(comp.at("listTest (long)") == + tag_list::of({11, 12, 13, 14, 15})); + TS_ASSERT(comp.at("listTest (end)") == tag_list()); + + TS_ASSERT( + (comp.at("nested compound test") == + tag_compound{ + {"egg", tag_compound{{"value", 0.5f}, {"name", "Eggbert"}}}, + {"ham", tag_compound{{"value", 0.75f}, {"name", "Hampus"}}}})); + + TS_ASSERT( + comp.at("intArrayTest") == + tag_int_array({0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f})); + } + + void test_read_bigtest() + { + // Uses an extended variant of Notch's original bigtest file + std::string input(__binary_bigtest_uncompr_start, + __binary_bigtest_uncompr_end); + std::istringstream file(input, std::ios::binary); + + auto pair = nbt::io::read_compound(file); + TS_ASSERT_EQUALS(pair.first, "Level"); + verify_bigtest_structure(*pair.second); + } + + void test_read_littletest() + { + // Same as bigtest, but little endian + std::string input(__binary_littletest_uncompr_start, + __binary_littletest_uncompr_end); + std::istringstream file(input, std::ios::binary); + + auto pair = nbt::io::read_compound(file, endian::little); + TS_ASSERT_EQUALS(pair.first, "Level"); + TS_ASSERT_EQUALS(pair.second->get_type(), tag_type::Compound); + verify_bigtest_structure(*pair.second); + } + + void test_read_eof1() + { + std::string input(__binary_errortest_eof1_start, + __binary_errortest_eof1_end); + std::istringstream file(input, std::ios::binary); + nbt::io::stream_reader reader(file); + + // EOF within a tag_double payload + TS_ASSERT(file); + TS_ASSERT_THROWS(reader.read_tag(), io::input_error); + TS_ASSERT(!file); + } + + void test_read_eof2() + { + std::string input(__binary_errortest_eof2_start, + __binary_errortest_eof2_end); + std::istringstream file(input, std::ios::binary); + nbt::io::stream_reader reader(file); + + // EOF within a key in a compound + TS_ASSERT(file); + TS_ASSERT_THROWS(reader.read_tag(), io::input_error); + TS_ASSERT(!file); + } + + void test_read_errortest_noend() + { + std::string input(__binary_errortest_noend_start, + __binary_errortest_noend_end); + std::istringstream file(input, std::ios::binary); + nbt::io::stream_reader reader(file); + + // Missing tag_end + TS_ASSERT(file); + TS_ASSERT_THROWS(reader.read_tag(), io::input_error); + TS_ASSERT(!file); + } + + void test_read_errortest_neg_length() + { + std::string input(__binary_errortest_neg_length_start, + __binary_errortest_neg_length_end); + std::istringstream file(input, std::ios::binary); + nbt::io::stream_reader reader(file); + + // Negative list length + TS_ASSERT(file); + TS_ASSERT_THROWS(reader.read_tag(), io::input_error); + TS_ASSERT(!file); + } + + void test_read_misc() + { + std::string input(__binary_toplevel_string_start, + __binary_toplevel_string_end); + std::istringstream file(input, std::ios::binary); + nbt::io::stream_reader reader(file); + + // Toplevel tag other than compound + TS_ASSERT(file); + TS_ASSERT_THROWS(reader.read_compound(), io::input_error); + TS_ASSERT(!file); + + // Rewind and try again with read_tag + file.clear(); + TS_ASSERT(file.seekg(0)); + auto pair = reader.read_tag(); + TS_ASSERT_EQUALS(pair.first, "Test (toplevel tag_string)"); + TS_ASSERT(*pair.second == + tag_string("Even though unprovided for by NBT, the library " + "should also handle " + "the case where the file consists of something " + "else than tag_compound")); + } + void test_read_gzip() + { #ifdef NBT_HAVE_ZLIB - std::string input(__binary_bigtest_nbt_start, __binary_bigtest_nbt_end); - std::istringstream file(input, std::ios::binary); - zlib::izlibstream igzs(file); - TS_ASSERT(file && igzs); - - auto pair = nbt::io::read_compound(igzs); - TS_ASSERT(igzs); - TS_ASSERT_EQUALS(pair.first, "Level"); - verify_bigtest_structure(*pair.second); + std::string input(__binary_bigtest_nbt_start, __binary_bigtest_nbt_end); + std::istringstream file(input, std::ios::binary); + zlib::izlibstream igzs(file); + TS_ASSERT(file && igzs); + + auto pair = nbt::io::read_compound(igzs); + TS_ASSERT(igzs); + TS_ASSERT_EQUALS(pair.first, "Level"); + verify_bigtest_structure(*pair.second); #endif - } + } }; diff --git a/test/test_value.h b/test/test_value.h index f63d051d28..590c5bf2cc 100644 --- a/test/test_value.h +++ b/test/test_value.h @@ -6,31 +6,31 @@ using namespace nbt; class value_assignment_test : public CxxTest::TestSuite { -public: - void test_numeric_assignments() - { - value v; + public: + void test_numeric_assignments() + { + value v; - v = int8_t(-5); - TS_ASSERT_EQUALS(int32_t(v), int32_t(-5)); - TS_ASSERT_EQUALS(double(v), 5.); + v = int8_t(-5); + TS_ASSERT_EQUALS(int32_t(v), int32_t(-5)); + TS_ASSERT_EQUALS(double(v), 5.); - v = value(); - v = int16_t(12345); - TS_ASSERT_EQUALS(int32_t(v), int32_t(12345)); - TS_ASSERT_EQUALS(double(v), 12345.); + v = value(); + v = int16_t(12345); + TS_ASSERT_EQUALS(int32_t(v), int32_t(12345)); + TS_ASSERT_EQUALS(double(v), 12345.); - v = value(); - v = int32_t(100000); - TS_ASSERT_EQUALS(int64_t(v), int64_t(100000)); - TS_ASSERT_EQUALS(double(v), 100000.); + v = value(); + v = int32_t(100000); + TS_ASSERT_EQUALS(int64_t(v), int64_t(100000)); + TS_ASSERT_EQUALS(double(v), 100000.); - v = value(); - v = float(3.14f); - TS_ASSERT_EQUALS(double(v), 3.14); + v = value(); + v = float(3.14f); + TS_ASSERT_EQUALS(double(v), 3.14); - v = value(); - v = double(2.718281828); - TS_ASSERT_EQUALS(double(v), 2.718281828); - } + v = value(); + v = double(2.718281828); + TS_ASSERT_EQUALS(double(v), 2.718281828); + } }; diff --git a/test/write_test.h b/test/write_test.h index 376eafa59d..fa41a6092d 100644 --- a/test/write_test.h +++ b/test/write_test.h @@ -41,238 +41,227 @@ using namespace nbt; class read_test : public CxxTest::TestSuite { -public: - void test_stream_writer_big() - { - std::ostringstream os; - nbt::io::stream_writer writer(os); - - TS_ASSERT_EQUALS(&writer.get_ostr(), &os); - TS_ASSERT_EQUALS(writer.get_endian(), endian::big); - - writer.write_type(tag_type::End); - writer.write_type(tag_type::Long); - writer.write_type(tag_type::Int_Array); - - writer.write_num(int64_t(0x0102030405060708)); - - writer.write_string("foobar"); - - TS_ASSERT(os); - std::string expected{ - 0, //tag_type::End - 4, //tag_type::Long - 11, //tag_type::Int_Array - - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, //0x0102030405060708 in Big Endian - - 0x00, 0x06, //string length in Big Endian - 'f', 'o', 'o', 'b', 'a', 'r' - }; - TS_ASSERT_EQUALS(os.str(), expected); - - //too long for NBT - TS_ASSERT_THROWS(writer.write_string(std::string(65536, '.')), std::length_error); - TS_ASSERT(!os); - } - - void test_stream_writer_little() - { - std::ostringstream os; - nbt::io::stream_writer writer(os, endian::little); - - TS_ASSERT_EQUALS(writer.get_endian(), endian::little); - - writer.write_num(int32_t(0x0a0b0c0d)); - - writer.write_string("foobar"); - - TS_ASSERT(os); - std::string expected{ - 0x0d, 0x0c, 0x0b, 0x0a, //0x0a0b0c0d in Little Endian - - 0x06, 0x00, //string length in Little Endian - 'f', 'o', 'o', 'b', 'a', 'r' - }; - TS_ASSERT_EQUALS(os.str(), expected); - - TS_ASSERT_THROWS(writer.write_string(std::string(65536, '.')), std::length_error); - TS_ASSERT(!os); - } - - void test_write_payload_big() - { - std::ostringstream os; - nbt::io::stream_writer writer(os); - - //tag_primitive - writer.write_payload(tag_byte(127)); - writer.write_payload(tag_short(32767)); - writer.write_payload(tag_int(2147483647)); - writer.write_payload(tag_long(9223372036854775807)); - - //Same values as in endian_str_test - writer.write_payload(tag_float(std::stof("-0xCDEF01p-63"))); - writer.write_payload(tag_double(std::stod("-0x1DEF0102030405p-375"))); - - TS_ASSERT_EQUALS(os.str(), (std::string{ - '\x7F', - '\x7F', '\xFF', - '\x7F', '\xFF', '\xFF', '\xFF', - '\x7F', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', - - '\xAB', '\xCD', '\xEF', '\x01', - '\xAB', '\xCD', '\xEF', '\x01', '\x02', '\x03', '\x04', '\x05' - })); - os.str(""); //clear and reuse the stream - - //tag_string - writer.write_payload(tag_string("barbaz")); - TS_ASSERT_EQUALS(os.str(), (std::string{ - 0x00, 0x06, //string length in Big Endian - 'b', 'a', 'r', 'b', 'a', 'z' - })); - TS_ASSERT_THROWS(writer.write_payload(tag_string(std::string(65536, '.'))), std::length_error); - TS_ASSERT(!os); - os.clear(); - - //tag_byte_array - os.str(""); - writer.write_payload(tag_byte_array{0, 1, 127, -128, -127}); - TS_ASSERT_EQUALS(os.str(), (std::string{ - 0x00, 0x00, 0x00, 0x05, //length in Big Endian - 0, 1, 127, -128, -127 - })); - os.str(""); - - //tag_int_array - writer.write_payload(tag_int_array{0x01020304, 0x05060708, 0x090a0b0c}); - TS_ASSERT_EQUALS(os.str(), (std::string{ - 0x00, 0x00, 0x00, 0x03, //length in Big Endian - 0x01, 0x02, 0x03, 0x04, - 0x05, 0x06, 0x07, 0x08, - 0x09, 0x0a, 0x0b, 0x0c - })); - os.str(""); - - //tag_list - writer.write_payload(tag_list()); //empty list with undetermined type, should be written as list of tag_end - writer.write_payload(tag_list(tag_type::Int)); //empty list of tag_int - writer.write_payload(tag_list{ //nested list - tag_list::of({0x3456, 0x789a}), - tag_list::of({0x0a, 0x0b, 0x0c, 0x0d}) - }); - TS_ASSERT_EQUALS(os.str(), (std::string{ - 0, //tag_type::End - 0x00, 0x00, 0x00, 0x00, //length - - 3, //tag_type::Int - 0x00, 0x00, 0x00, 0x00, //length - - 9, //tag_type::List - 0x00, 0x00, 0x00, 0x02, //length - //list 0 - 2, //tag_type::Short - 0x00, 0x00, 0x00, 0x02, //length - '\x34', '\x56', - '\x78', '\x9a', - //list 1 - 1, //tag_type::Byte - 0x00, 0x00, 0x00, 0x04, //length - 0x0a, - 0x0b, - 0x0c, - 0x0d - })); - os.str(""); - - //tag_compound - /* Testing if writing compounds works properly is problematic because the - order of the tags is not guaranteed. However with only two tags in a - compound we only have two possible orderings. - See below for a more thorough test that uses writing and re-reading. */ - writer.write_payload(tag_compound{}); - writer.write_payload(tag_compound{ - {"foo", "quux"}, - {"bar", tag_int(0x789abcde)} - }); - - std::string endtag{0x00}; - std::string subtag1{ - 8, //tag_type::String - 0x00, 0x03, //key length - 'f', 'o', 'o', - 0x00, 0x04, //string length - 'q', 'u', 'u', 'x' - }; - std::string subtag2{ - 3, //tag_type::Int - 0x00, 0x03, //key length - 'b', 'a', 'r', - '\x78', '\x9A', '\xBC', '\xDE' - }; - - TS_ASSERT(os.str() == endtag + subtag1 + subtag2 + endtag - || os.str() == endtag + subtag2 + subtag1 + endtag); - - //Now for write_tag: - os.str(""); - writer.write_tag("foo", tag_string("quux")); - TS_ASSERT_EQUALS(os.str(), subtag1); - TS_ASSERT(os); - - //too long key for NBT - TS_ASSERT_THROWS(writer.write_tag(std::string(65536, '.'), tag_long(-1)), std::length_error); - TS_ASSERT(!os); - } - - void test_write_bigtest() - { - /* Like already stated above, because no order is guaranteed for - tag_compound, we cannot simply test it by writing into a stream and directly - comparing the output to a reference value. - Instead, we assume that reading already works correctly and re-read the - written tag. - Smaller-grained tests are already done above. */ - std::string input(__binary_bigtest_uncompr_start, __binary_bigtest_uncompr_end); - std::istringstream file(input, std::ios::binary); - - const auto orig_pair = io::read_compound(file); - std::stringstream sstr; - - //Write into stream in Big Endian - io::write_tag(orig_pair.first, *orig_pair.second, sstr); - TS_ASSERT(sstr); - - //Read from stream in Big Endian and compare - auto written_pair = io::read_compound(sstr); - TS_ASSERT_EQUALS(orig_pair.first, written_pair.first); - TS_ASSERT(*orig_pair.second == *written_pair.second); - - sstr.str(""); //Reset and reuse stream - //Write into stream in Little Endian - io::write_tag(orig_pair.first, *orig_pair.second, sstr, endian::little); - TS_ASSERT(sstr); - - //Read from stream in Little Endian and compare - written_pair = io::read_compound(sstr, endian::little); - TS_ASSERT_EQUALS(orig_pair.first, written_pair.first); - TS_ASSERT(*orig_pair.second == *written_pair.second); + public: + void test_stream_writer_big() + { + std::ostringstream os; + nbt::io::stream_writer writer(os); + + TS_ASSERT_EQUALS(&writer.get_ostr(), &os); + TS_ASSERT_EQUALS(writer.get_endian(), endian::big); + + writer.write_type(tag_type::End); + writer.write_type(tag_type::Long); + writer.write_type(tag_type::Int_Array); + + writer.write_num(int64_t(0x0102030405060708)); + + writer.write_string("foobar"); + + TS_ASSERT(os); + std::string expected{0, // tag_type::End + 4, // tag_type::Long + 11, // tag_type::Int_Array + + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, // 0x0102030405060708 in Big Endian + + 0x00, 0x06, // string length in Big Endian + 'f', 'o', 'o', 'b', 'a', 'r'}; + TS_ASSERT_EQUALS(os.str(), expected); + + // too long for NBT + TS_ASSERT_THROWS(writer.write_string(std::string(65536, '.')), + std::length_error); + TS_ASSERT(!os); + } + + void test_stream_writer_little() + { + std::ostringstream os; + nbt::io::stream_writer writer(os, endian::little); + + TS_ASSERT_EQUALS(writer.get_endian(), endian::little); + + writer.write_num(int32_t(0x0a0b0c0d)); + + writer.write_string("foobar"); + + TS_ASSERT(os); + std::string expected{ + 0x0d, 0x0c, 0x0b, 0x0a, // 0x0a0b0c0d in Little Endian + + 0x06, 0x00, // string length in Little Endian + 'f', 'o', 'o', 'b', 'a', 'r'}; + TS_ASSERT_EQUALS(os.str(), expected); + + TS_ASSERT_THROWS(writer.write_string(std::string(65536, '.')), + std::length_error); + TS_ASSERT(!os); + } + + void test_write_payload_big() + { + std::ostringstream os; + nbt::io::stream_writer writer(os); + + // tag_primitive + writer.write_payload(tag_byte(127)); + writer.write_payload(tag_short(32767)); + writer.write_payload(tag_int(2147483647)); + writer.write_payload(tag_long(9223372036854775807)); + + // Same values as in endian_str_test + writer.write_payload(tag_float(std::stof("-0xCDEF01p-63"))); + writer.write_payload(tag_double(std::stod("-0x1DEF0102030405p-375"))); + + TS_ASSERT_EQUALS( + os.str(), + (std::string{'\x7F', '\x7F', '\xFF', '\x7F', '\xFF', '\xFF', + '\xFF', '\x7F', '\xFF', '\xFF', '\xFF', '\xFF', + '\xFF', '\xFF', '\xFF', + + '\xAB', '\xCD', '\xEF', '\x01', '\xAB', '\xCD', + '\xEF', '\x01', '\x02', '\x03', '\x04', '\x05'})); + os.str(""); // clear and reuse the stream + + // tag_string + writer.write_payload(tag_string("barbaz")); + TS_ASSERT_EQUALS(os.str(), + (std::string{0x00, 0x06, // string length in Big Endian + 'b', 'a', 'r', 'b', 'a', 'z'})); + TS_ASSERT_THROWS( + writer.write_payload(tag_string(std::string(65536, '.'))), + std::length_error); + TS_ASSERT(!os); + os.clear(); + + // tag_byte_array + os.str(""); + writer.write_payload(tag_byte_array{0, 1, 127, -128, -127}); + TS_ASSERT_EQUALS(os.str(), (std::string{0x00, 0x00, 0x00, + 0x05, // length in Big Endian + 0, 1, 127, -128, -127})); + os.str(""); + + // tag_int_array + writer.write_payload(tag_int_array{0x01020304, 0x05060708, 0x090a0b0c}); + TS_ASSERT_EQUALS( + os.str(), + (std::string{0x00, 0x00, 0x00, 0x03, // length in Big Endian + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c})); + os.str(""); + + // tag_list + writer.write_payload( + tag_list()); // empty list with undetermined type, should be written + // as list of tag_end + writer.write_payload(tag_list(tag_type::Int)); // empty list of tag_int + writer.write_payload( + tag_list{// nested list + tag_list::of({0x3456, 0x789a}), + tag_list::of({0x0a, 0x0b, 0x0c, 0x0d})}); + TS_ASSERT_EQUALS(os.str(), + (std::string{0, // tag_type::End + 0x00, 0x00, 0x00, 0x00, // length + + 3, // tag_type::Int + 0x00, 0x00, 0x00, 0x00, // length + + 9, // tag_type::List + 0x00, 0x00, 0x00, 0x02, // length + // list 0 + 2, // tag_type::Short + 0x00, 0x00, 0x00, 0x02, // length + '\x34', '\x56', '\x78', '\x9a', + // list 1 + 1, // tag_type::Byte + 0x00, 0x00, 0x00, 0x04, // length + 0x0a, 0x0b, 0x0c, 0x0d})); + os.str(""); + + // tag_compound + /* Testing if writing compounds works properly is problematic because + the order of the tags is not guaranteed. However with only two tags in a + compound we only have two possible orderings. + See below for a more thorough test that uses writing and re-reading. */ + writer.write_payload(tag_compound{}); + writer.write_payload( + tag_compound{{"foo", "quux"}, {"bar", tag_int(0x789abcde)}}); + + std::string endtag{0x00}; + std::string subtag1{8, // tag_type::String + 0x00, 0x03, // key length + 'f', 'o', 'o', 0x00, 0x04, // string length + 'q', 'u', 'u', 'x'}; + std::string subtag2{3, // tag_type::Int + 0x00, 0x03, // key length + 'b', 'a', 'r', '\x78', '\x9A', '\xBC', '\xDE'}; + + TS_ASSERT(os.str() == endtag + subtag1 + subtag2 + endtag || + os.str() == endtag + subtag2 + subtag1 + endtag); + + // Now for write_tag: + os.str(""); + writer.write_tag("foo", tag_string("quux")); + TS_ASSERT_EQUALS(os.str(), subtag1); + TS_ASSERT(os); + + // too long key for NBT + TS_ASSERT_THROWS( + writer.write_tag(std::string(65536, '.'), tag_long(-1)), + std::length_error); + TS_ASSERT(!os); + } + + void test_write_bigtest() + { + /* Like already stated above, because no order is guaranteed for + tag_compound, we cannot simply test it by writing into a stream and + directly comparing the output to a reference value. Instead, we assume + that reading already works correctly and re-read the written tag. + Smaller-grained tests are already done above. */ + std::string input(__binary_bigtest_uncompr_start, + __binary_bigtest_uncompr_end); + std::istringstream file(input, std::ios::binary); + + const auto orig_pair = io::read_compound(file); + std::stringstream sstr; + + // Write into stream in Big Endian + io::write_tag(orig_pair.first, *orig_pair.second, sstr); + TS_ASSERT(sstr); + + // Read from stream in Big Endian and compare + auto written_pair = io::read_compound(sstr); + TS_ASSERT_EQUALS(orig_pair.first, written_pair.first); + TS_ASSERT(*orig_pair.second == *written_pair.second); + + sstr.str(""); // Reset and reuse stream + // Write into stream in Little Endian + io::write_tag(orig_pair.first, *orig_pair.second, sstr, endian::little); + TS_ASSERT(sstr); + + // Read from stream in Little Endian and compare + written_pair = io::read_compound(sstr, endian::little); + TS_ASSERT_EQUALS(orig_pair.first, written_pair.first); + TS_ASSERT(*orig_pair.second == *written_pair.second); #ifdef NBT_HAVE_ZLIB - //Now with gzip compression - sstr.str(""); - zlib::ozlibstream ogzs(sstr, -1, true); - io::write_tag(orig_pair.first, *orig_pair.second, ogzs); - ogzs.close(); - TS_ASSERT(ogzs); - TS_ASSERT(sstr); - //Read and compare - zlib::izlibstream igzs(sstr); - written_pair = io::read_compound(igzs); - TS_ASSERT(igzs); - TS_ASSERT_EQUALS(orig_pair.first, written_pair.first); - TS_ASSERT(*orig_pair.second == *written_pair.second); + // Now with gzip compression + sstr.str(""); + zlib::ozlibstream ogzs(sstr, -1, true); + io::write_tag(orig_pair.first, *orig_pair.second, ogzs); + ogzs.close(); + TS_ASSERT(ogzs); + TS_ASSERT(sstr); + // Read and compare + zlib::izlibstream igzs(sstr); + written_pair = io::read_compound(igzs); + TS_ASSERT(igzs); + TS_ASSERT_EQUALS(orig_pair.first, written_pair.first); + TS_ASSERT(*orig_pair.second == *written_pair.second); #endif - } + } }; diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h index 67a608e2ad..c96797ce90 100644 --- a/test/zlibstream_test.h +++ b/test/zlibstream_test.h @@ -35,249 +35,260 @@ using namespace zlib; class zlibstream_test : public CxxTest::TestSuite { -private: - std::string bigtest; + private: + std::string bigtest; -public: - zlibstream_test() - { - std::string input(__binary_bigtest_uncompr_start, __binary_bigtest_uncompr_end); - std::istringstream bigtest_f(input, std::ios::binary); - std::stringbuf bigtest_b; - bigtest_f >> &bigtest_b; - bigtest = bigtest_b.str(); - if(!bigtest_f || bigtest.size() == 0) - throw std::runtime_error("Could not read bigtest_uncompr file"); - } + public: + zlibstream_test() + { + std::string input(__binary_bigtest_uncompr_start, + __binary_bigtest_uncompr_end); + std::istringstream bigtest_f(input, std::ios::binary); + std::stringbuf bigtest_b; + bigtest_f >> &bigtest_b; + bigtest = bigtest_b.str(); + if (!bigtest_f || bigtest.size() == 0) + throw std::runtime_error("Could not read bigtest_uncompr file"); + } - void test_inflate_gzip() - { - std::string input(__binary_bigtest_nbt_start, __binary_bigtest_nbt_end); - std::istringstream gzip_in(input, std::ios::binary); - TS_ASSERT(gzip_in); + void test_inflate_gzip() + { + std::string input(__binary_bigtest_nbt_start, __binary_bigtest_nbt_end); + std::istringstream gzip_in(input, std::ios::binary); + TS_ASSERT(gzip_in); - std::stringbuf data; - //Small buffer so not all fits at once (the compressed file is 561 bytes) - { - izlibstream igzs(gzip_in, 256); - igzs.exceptions(std::ios::failbit | std::ios::badbit); - TS_ASSERT(igzs.good()); + std::stringbuf data; + // Small buffer so not all fits at once (the compressed file is 561 + // bytes) + { + izlibstream igzs(gzip_in, 256); + igzs.exceptions(std::ios::failbit | std::ios::badbit); + TS_ASSERT(igzs.good()); - TS_ASSERT_THROWS_NOTHING(igzs >> &data); - TS_ASSERT(igzs); - TS_ASSERT(igzs.eof()); - TS_ASSERT_EQUALS(data.str(), bigtest); - } + TS_ASSERT_THROWS_NOTHING(igzs >> &data); + TS_ASSERT(igzs); + TS_ASSERT(igzs.eof()); + TS_ASSERT_EQUALS(data.str(), bigtest); + } - //Clear and reuse buffers - data.str(""); - gzip_in.clear(); - gzip_in.seekg(0); - //Now try the same with larger buffer (but not large enough for all output, uncompressed size 1561 bytes) - { - izlibstream igzs(gzip_in, 1000); - igzs.exceptions(std::ios::failbit | std::ios::badbit); - TS_ASSERT(igzs.good()); + // Clear and reuse buffers + data.str(""); + gzip_in.clear(); + gzip_in.seekg(0); + // Now try the same with larger buffer (but not large enough for all + // output, uncompressed size 1561 bytes) + { + izlibstream igzs(gzip_in, 1000); + igzs.exceptions(std::ios::failbit | std::ios::badbit); + TS_ASSERT(igzs.good()); - TS_ASSERT_THROWS_NOTHING(igzs >> &data); - TS_ASSERT(igzs); - TS_ASSERT(igzs.eof()); - TS_ASSERT_EQUALS(data.str(), bigtest); - } + TS_ASSERT_THROWS_NOTHING(igzs >> &data); + TS_ASSERT(igzs); + TS_ASSERT(igzs.eof()); + TS_ASSERT_EQUALS(data.str(), bigtest); + } - data.str(""); - gzip_in.clear(); - gzip_in.seekg(0); - //Now with large buffer - { - izlibstream igzs(gzip_in, 4000); - igzs.exceptions(std::ios::failbit | std::ios::badbit); - TS_ASSERT(igzs.good()); + data.str(""); + gzip_in.clear(); + gzip_in.seekg(0); + // Now with large buffer + { + izlibstream igzs(gzip_in, 4000); + igzs.exceptions(std::ios::failbit | std::ios::badbit); + TS_ASSERT(igzs.good()); - TS_ASSERT_THROWS_NOTHING(igzs >> &data); - TS_ASSERT(igzs); - TS_ASSERT(igzs.eof()); - TS_ASSERT_EQUALS(data.str(), bigtest); - } - } + TS_ASSERT_THROWS_NOTHING(igzs >> &data); + TS_ASSERT(igzs); + TS_ASSERT(igzs.eof()); + TS_ASSERT_EQUALS(data.str(), bigtest); + } + } - void test_inflate_zlib() - { - std::string input(__binary_bigtest_zlib_start, __binary_bigtest_zlib_end); - std::istringstream zlib_in(input, std::ios::binary); - TS_ASSERT(zlib_in); + void test_inflate_zlib() + { + std::string input(__binary_bigtest_zlib_start, + __binary_bigtest_zlib_end); + std::istringstream zlib_in(input, std::ios::binary); + TS_ASSERT(zlib_in); - std::stringbuf data; - izlibstream izls(zlib_in, 256); - izls.exceptions(std::ios::failbit | std::ios::badbit); - TS_ASSERT(izls.good()); + std::stringbuf data; + izlibstream izls(zlib_in, 256); + izls.exceptions(std::ios::failbit | std::ios::badbit); + TS_ASSERT(izls.good()); - TS_ASSERT_THROWS_NOTHING(izls >> &data); - TS_ASSERT(izls); - TS_ASSERT(izls.eof()); - TS_ASSERT_EQUALS(data.str(), bigtest); - } + TS_ASSERT_THROWS_NOTHING(izls >> &data); + TS_ASSERT(izls); + TS_ASSERT(izls.eof()); + TS_ASSERT_EQUALS(data.str(), bigtest); + } - void test_inflate_corrupt() - { - std::string input(__binary_bigtest_corrupt_nbt_start, __binary_bigtest_corrupt_nbt_end); - std::istringstream gzip_in(input, std::ios::binary); - TS_ASSERT(gzip_in); + void test_inflate_corrupt() + { + std::string input(__binary_bigtest_corrupt_nbt_start, + __binary_bigtest_corrupt_nbt_end); + std::istringstream gzip_in(input, std::ios::binary); + TS_ASSERT(gzip_in); - std::vector buf(bigtest.size()); - izlibstream igzs(gzip_in); - igzs.exceptions(std::ios::failbit | std::ios::badbit); - TS_ASSERT_THROWS(igzs.read(buf.data(), buf.size()), zlib_error); - TS_ASSERT(igzs.bad()); - } + std::vector buf(bigtest.size()); + izlibstream igzs(gzip_in); + igzs.exceptions(std::ios::failbit | std::ios::badbit); + TS_ASSERT_THROWS(igzs.read(buf.data(), buf.size()), zlib_error); + TS_ASSERT(igzs.bad()); + } - void test_inflate_eof() - { - std::string input(__binary_bigtest_eof_nbt_start, __binary_bigtest_eof_nbt_end); - std::istringstream gzip_in(input, std::ios::binary); - TS_ASSERT(gzip_in); + void test_inflate_eof() + { + std::string input(__binary_bigtest_eof_nbt_start, + __binary_bigtest_eof_nbt_end); + std::istringstream gzip_in(input, std::ios::binary); + TS_ASSERT(gzip_in); - std::vector buf(bigtest.size()); - izlibstream igzs(gzip_in); - igzs.exceptions(std::ios::failbit | std::ios::badbit); - TS_ASSERT_THROWS(igzs.read(buf.data(), buf.size()), zlib_error); - TS_ASSERT(igzs.bad()); - } + std::vector buf(bigtest.size()); + izlibstream igzs(gzip_in); + igzs.exceptions(std::ios::failbit | std::ios::badbit); + TS_ASSERT_THROWS(igzs.read(buf.data(), buf.size()), zlib_error); + TS_ASSERT(igzs.bad()); + } - void test_inflate_trailing() - { - //This file contains additional uncompressed data after the zlib-compressed data - std::string input(__binary_trailing_data_zlib_start, __binary_trailing_data_zlib_end); - std::istringstream file(input, std::ios::binary); - izlibstream izls(file, 32); - TS_ASSERT(file && izls); + void test_inflate_trailing() + { + // This file contains additional uncompressed data after the + // zlib-compressed data + std::string input(__binary_trailing_data_zlib_start, + __binary_trailing_data_zlib_end); + std::istringstream file(input, std::ios::binary); + izlibstream izls(file, 32); + TS_ASSERT(file && izls); - std::string str; - izls >> str; - TS_ASSERT(izls); - TS_ASSERT(izls.eof()); - TS_ASSERT_EQUALS(str, "foobar"); + std::string str; + izls >> str; + TS_ASSERT(izls); + TS_ASSERT(izls.eof()); + TS_ASSERT_EQUALS(str, "foobar"); - //Now read the uncompressed data - TS_ASSERT(file); - TS_ASSERT(!file.eof()); - file >> str; - TS_ASSERT(!file.bad()); - TS_ASSERT_EQUALS(str, "barbaz"); - } + // Now read the uncompressed data + TS_ASSERT(file); + TS_ASSERT(!file.eof()); + file >> str; + TS_ASSERT(!file.bad()); + TS_ASSERT_EQUALS(str, "barbaz"); + } - void test_deflate_zlib() - { - //Here we assume that inflating works and has already been tested - std::stringstream str; - std::stringbuf output; - //Small buffer - { - ozlibstream ozls(str, -1, false, 256); - ozls.exceptions(std::ios::failbit | std::ios::badbit); - TS_ASSERT_THROWS_NOTHING(ozls << bigtest); - TS_ASSERT(ozls.good()); - TS_ASSERT_THROWS_NOTHING(ozls.close()); - TS_ASSERT(ozls.good()); - } - TS_ASSERT(str.good()); - { - izlibstream izls(str); - TS_ASSERT_THROWS_NOTHING(izls >> &output); - TS_ASSERT(izls); - } - TS_ASSERT_EQUALS(output.str(), bigtest); + void test_deflate_zlib() + { + // Here we assume that inflating works and has already been tested + std::stringstream str; + std::stringbuf output; + // Small buffer + { + ozlibstream ozls(str, -1, false, 256); + ozls.exceptions(std::ios::failbit | std::ios::badbit); + TS_ASSERT_THROWS_NOTHING(ozls << bigtest); + TS_ASSERT(ozls.good()); + TS_ASSERT_THROWS_NOTHING(ozls.close()); + TS_ASSERT(ozls.good()); + } + TS_ASSERT(str.good()); + { + izlibstream izls(str); + TS_ASSERT_THROWS_NOTHING(izls >> &output); + TS_ASSERT(izls); + } + TS_ASSERT_EQUALS(output.str(), bigtest); - str.clear(); str.str(""); - output.str(""); - //Medium sized buffer - //Write first half, then flush and write second half - { - ozlibstream ozls(str, 9, false, 512); - ozls.exceptions(std::ios::failbit | std::ios::badbit); + str.clear(); + str.str(""); + output.str(""); + // Medium sized buffer + // Write first half, then flush and write second half + { + ozlibstream ozls(str, 9, false, 512); + ozls.exceptions(std::ios::failbit | std::ios::badbit); - std::string half1 = bigtest.substr(0, bigtest.size()/2); - std::string half2 = bigtest.substr(bigtest.size()/2); - TS_ASSERT_THROWS_NOTHING(ozls << half1 << std::flush << half2); - TS_ASSERT(ozls.good()); - TS_ASSERT_THROWS_NOTHING(ozls.close()); - TS_ASSERT(ozls.good()); - } - TS_ASSERT(str.good()); - { - izlibstream izls(str); - izls >> &output; - TS_ASSERT(izls); - } - TS_ASSERT_EQUALS(output.str(), bigtest); + std::string half1 = bigtest.substr(0, bigtest.size() / 2); + std::string half2 = bigtest.substr(bigtest.size() / 2); + TS_ASSERT_THROWS_NOTHING(ozls << half1 << std::flush << half2); + TS_ASSERT(ozls.good()); + TS_ASSERT_THROWS_NOTHING(ozls.close()); + TS_ASSERT(ozls.good()); + } + TS_ASSERT(str.good()); + { + izlibstream izls(str); + izls >> &output; + TS_ASSERT(izls); + } + TS_ASSERT_EQUALS(output.str(), bigtest); - str.clear(); str.str(""); - output.str(""); - //Large buffer - { - ozlibstream ozls(str, 1, false, 4000); - ozls.exceptions(std::ios::failbit | std::ios::badbit); - TS_ASSERT_THROWS_NOTHING(ozls << bigtest); - TS_ASSERT(ozls.good()); - TS_ASSERT_THROWS_NOTHING(ozls.close()); - TS_ASSERT_THROWS_NOTHING(ozls.close()); //closing twice shouldn't be a problem - TS_ASSERT(ozls.good()); - } - TS_ASSERT(str.good()); - { - izlibstream izls(str); - izls >> &output; - TS_ASSERT(izls); - } - TS_ASSERT_EQUALS(output.str(), bigtest); - } + str.clear(); + str.str(""); + output.str(""); + // Large buffer + { + ozlibstream ozls(str, 1, false, 4000); + ozls.exceptions(std::ios::failbit | std::ios::badbit); + TS_ASSERT_THROWS_NOTHING(ozls << bigtest); + TS_ASSERT(ozls.good()); + TS_ASSERT_THROWS_NOTHING(ozls.close()); + TS_ASSERT_THROWS_NOTHING( + ozls.close()); // closing twice shouldn't be a problem + TS_ASSERT(ozls.good()); + } + TS_ASSERT(str.good()); + { + izlibstream izls(str); + izls >> &output; + TS_ASSERT(izls); + } + TS_ASSERT_EQUALS(output.str(), bigtest); + } - void test_deflate_gzip() - { - std::stringstream str; - std::stringbuf output; - { - ozlibstream ozls(str, -1, true); - ozls.exceptions(std::ios::failbit | std::ios::badbit); - TS_ASSERT_THROWS_NOTHING(ozls << bigtest); - TS_ASSERT(ozls.good()); - TS_ASSERT_THROWS_NOTHING(ozls.close()); - TS_ASSERT(ozls.good()); - } - TS_ASSERT(str.good()); - { - izlibstream izls(str); - izls >> &output; - TS_ASSERT(izls); - } - TS_ASSERT_EQUALS(output.str(), bigtest); - } + void test_deflate_gzip() + { + std::stringstream str; + std::stringbuf output; + { + ozlibstream ozls(str, -1, true); + ozls.exceptions(std::ios::failbit | std::ios::badbit); + TS_ASSERT_THROWS_NOTHING(ozls << bigtest); + TS_ASSERT(ozls.good()); + TS_ASSERT_THROWS_NOTHING(ozls.close()); + TS_ASSERT(ozls.good()); + } + TS_ASSERT(str.good()); + { + izlibstream izls(str); + izls >> &output; + TS_ASSERT(izls); + } + TS_ASSERT_EQUALS(output.str(), bigtest); + } - void test_deflate_closed() - { - std::stringstream str; - { - ozlibstream ozls(str); - ozls.exceptions(std::ios::failbit | std::ios::badbit); - TS_ASSERT_THROWS_NOTHING(ozls << bigtest); - TS_ASSERT_THROWS_NOTHING(ozls.close()); - TS_ASSERT_THROWS_NOTHING(ozls << "foo"); - TS_ASSERT_THROWS_ANYTHING(ozls.close()); - TS_ASSERT(ozls.bad()); - TS_ASSERT(!str); - } - str.clear(); - str.seekp(0); - { - ozlibstream ozls(str); - //this time without exceptions - TS_ASSERT_THROWS_NOTHING(ozls << bigtest); - TS_ASSERT_THROWS_NOTHING(ozls.close()); - TS_ASSERT_THROWS_NOTHING(ozls << "foo" << std::flush); - TS_ASSERT(ozls.bad()); - TS_ASSERT_THROWS_NOTHING(ozls.close()); - TS_ASSERT(ozls.bad()); - TS_ASSERT(!str); - } - } + void test_deflate_closed() + { + std::stringstream str; + { + ozlibstream ozls(str); + ozls.exceptions(std::ios::failbit | std::ios::badbit); + TS_ASSERT_THROWS_NOTHING(ozls << bigtest); + TS_ASSERT_THROWS_NOTHING(ozls.close()); + TS_ASSERT_THROWS_NOTHING(ozls << "foo"); + TS_ASSERT_THROWS_ANYTHING(ozls.close()); + TS_ASSERT(ozls.bad()); + TS_ASSERT(!str); + } + str.clear(); + str.seekp(0); + { + ozlibstream ozls(str); + // this time without exceptions + TS_ASSERT_THROWS_NOTHING(ozls << bigtest); + TS_ASSERT_THROWS_NOTHING(ozls.close()); + TS_ASSERT_THROWS_NOTHING(ozls << "foo" << std::flush); + TS_ASSERT(ozls.bad()); + TS_ASSERT_THROWS_NOTHING(ozls.close()); + TS_ASSERT(ozls.bad()); + TS_ASSERT(!str); + } + } }; -- cgit 0.0.5-2-1-g0f52