summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/io/ozlibstream.cpp17
-rw-r--r--src/text/json_formatter.cpp29
-rw-r--r--src/value.cpp180
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;
}