diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/io/ozlibstream.cpp | 17 | ||||
| -rw-r--r-- | src/text/json_formatter.cpp | 29 | ||||
| -rw-r--r-- | src/value.cpp | 180 |
3 files changed, 111 insertions, 115 deletions
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<unsigned char>(c) < 32 || c == 127) { + // Control characters, escape as \u00XX + os << "\\u00" << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(static_cast<unsigned char>(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 <typeinfo> +#include <algorithm> +#include <initializer_list> +#include <cstdint> +#include <memory> 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<typename T> + void assign_numeric_impl(std::unique_ptr<tag>& tag_ptr, T val, + tag_type default_type) { - case tag_type::Byte: - static_cast<tag_byte&>(*tag_).set(val); - break; - case tag_type::Short: - static_cast<tag_short&>(*tag_).set(val); - break; - case tag_type::Int: - static_cast<tag_int&>(*tag_).set(val); - break; - case tag_type::Long: - static_cast<tag_long&>(*tag_).set(val); - break; - case tag_type::Float: - static_cast<tag_float&>(*tag_).set(val); - break; - case tag_type::Double: - static_cast<tag_double&>(*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<int8_t>(val))); break; + case tag_type::Short: tag_ptr.reset(new tag_short(static_cast<int16_t>(val))); break; + case tag_type::Int: tag_ptr.reset(new tag_int(static_cast<int32_t>(val))); break; + case tag_type::Long: tag_ptr.reset(new tag_long(static_cast<int64_t>(val))); break; + case tag_type::Float: tag_ptr.reset(new tag_float(static_cast<float>(val))); break; + case tag_type::Double: tag_ptr.reset(new tag_double(static_cast<double>(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<T>::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<int>(existing_type) < static_cast<int>(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<int8_t>(val))); break; + case tag_type::Short: tag_ptr.reset(new tag_short(static_cast<int16_t>(val))); break; + case tag_type::Int: tag_ptr.reset(new tag_int(static_cast<int32_t>(val))); break; + case tag_type::Long: tag_ptr.reset(new tag_long(static_cast<int64_t>(val))); break; + case tag_type::Float: tag_ptr.reset(new tag_float(static_cast<float>(val))); break; + case tag_type::Double: tag_ptr.reset(new tag_double(static_cast<double>(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_byte&>(*tag_ptr).set(static_cast<int8_t>(val)); break; + case tag_type::Short: static_cast<tag_short&>(*tag_ptr).set(static_cast<int16_t>(val)); break; + case tag_type::Int: static_cast<tag_int&>(*tag_ptr).set(static_cast<int32_t>(val)); break; + case tag_type::Long: static_cast<tag_long&>(*tag_ptr).set(static_cast<int64_t>(val)); break; + case tag_type::Float: static_cast<tag_float&>(*tag_ptr).set(static_cast<float>(val)); break; + case tag_type::Double: static_cast<tag_double&>(*tag_ptr).set(static_cast<double>(val)); break; + default: throw std::bad_cast(); + } } +} + +value& value::operator=(int8_t val) +{ + assign_numeric_impl(tag_, val, tag_type::Byte); return *this; } value& value::operator=(int16_t val) { - if(!tag_) - set(tag_short(val)); - else switch(tag_->get_type()) - { - case tag_type::Short: - static_cast<tag_short&>(*tag_).set(val); - break; - case tag_type::Int: - static_cast<tag_int&>(*tag_).set(val); - break; - case tag_type::Long: - static_cast<tag_long&>(*tag_).set(val); - break; - case tag_type::Float: - static_cast<tag_float&>(*tag_).set(val); - break; - case tag_type::Double: - static_cast<tag_double&>(*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_int&>(*tag_).set(val); - break; - case tag_type::Long: - static_cast<tag_long&>(*tag_).set(val); - break; - case tag_type::Float: - static_cast<tag_float&>(*tag_).set(val); - break; - case tag_type::Double: - static_cast<tag_double&>(*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_long&>(*tag_).set(val); - break; - case tag_type::Float: - static_cast<tag_float&>(*tag_).set(val); - break; - case tag_type::Double: - static_cast<tag_double&>(*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_float&>(*tag_).set(val); - break; - case tag_type::Double: - static_cast<tag_double&>(*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_double&>(*tag_).set(val); - break; - - default: - throw std::bad_cast(); - } + assign_numeric_impl(tag_, val, tag_type::Double); return *this; } |
