summaryrefslogtreecommitdiff
path: root/libnbtplusplus/src/io
diff options
context:
space:
mode:
Diffstat (limited to 'libnbtplusplus/src/io')
-rw-r--r--libnbtplusplus/src/io/izlibstream.cpp101
-rw-r--r--libnbtplusplus/src/io/ozlibstream.cpp114
-rw-r--r--libnbtplusplus/src/io/stream_reader.cpp122
-rw-r--r--libnbtplusplus/src/io/stream_writer.cpp59
4 files changed, 396 insertions, 0 deletions
diff --git a/libnbtplusplus/src/io/izlibstream.cpp b/libnbtplusplus/src/io/izlibstream.cpp
new file mode 100644
index 0000000000..9d57545360
--- /dev/null
+++ b/libnbtplusplus/src/io/izlibstream.cpp
@@ -0,0 +1,101 @@
+// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag <ljfa-ag@web.de>
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+#include "io/izlibstream.h"
+#include "io/zlib_streambuf.h"
+
+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);
+
+ char* end = out.data() + out.size();
+ setg(end, end, end);
+ }
+
+ inflate_streambuf::~inflate_streambuf() noexcept
+ {
+ 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());
+ if (is.bad())
+ throw std::ios_base::failure("Input stream is bad");
+ size_t count = is.gcount();
+ if (count == 0 && !stream_end)
+ throw zlib_error("Unexpected end of stream", Z_DATA_ERROR);
+
+ zstr.next_in = reinterpret_cast<Bytef*>(in.data());
+ zstr.avail_in = count;
+ }
+
+ zstr.next_out = reinterpret_cast<Bytef*>(out.data());
+ zstr.avail_out = out.size();
+
+ int ret = inflate(&zstr, Z_NO_FLUSH);
+ have = out.size() - zstr.avail_out;
+ switch (ret) {
+ case Z_NEED_DICT:
+ case Z_DATA_ERROR:
+ throw zlib_error(zstr.msg, ret);
+
+ case Z_MEM_ERROR:
+ throw std::bad_alloc();
+
+ case Z_STREAM_END:
+ if (!stream_end) {
+ stream_end = true;
+ // In case we consumed too much, we have to rewind the
+ // input stream
+ is.clear();
+ is.seekg(-static_cast<std::streamoff>(zstr.avail_in),
+ std::ios_base::cur);
+ }
+ if (have == 0)
+ return traits_type::eof();
+ break;
+ }
+ } while (have == 0);
+
+ setg(out.data(), out.data(), out.data() + have);
+ return traits_type::to_int_type(*gptr());
+ }
+
+} // namespace zlib
diff --git a/libnbtplusplus/src/io/ozlibstream.cpp b/libnbtplusplus/src/io/ozlibstream.cpp
new file mode 100644
index 0000000000..c8a3e862a1
--- /dev/null
+++ b/libnbtplusplus/src/io/ozlibstream.cpp
@@ -0,0 +1,114 @@
+// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag <ljfa-ag@web.de>
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+#include "io/ozlibstream.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)
+ : 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());
+ }
+
+ 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::deflate_chunk(int flush)
+ {
+ zstr.next_in = reinterpret_cast<Bytef*>(pbase());
+ zstr.avail_in = pptr() - pbase();
+ do {
+ zstr.next_out = reinterpret_cast<Bytef*>(out.data());
+ zstr.avail_out = out.size();
+ int ret = deflate(&zstr, flush);
+ if (ret != Z_OK && ret != Z_STREAM_END) {
+ os.setstate(std::ios_base::failbit);
+ throw zlib_error(zstr.msg, ret);
+ }
+ int have = out.size() - zstr.avail_out;
+ if (!os.write(out.data(), have))
+ throw std::ios_base::failure(
+ "Could not write to the output stream");
+ } while (zstr.avail_out == 0);
+ setp(in.data(), in.data() + in.size());
+ }
+
+ deflate_streambuf::int_type deflate_streambuf::overflow(int_type ch)
+ {
+ deflate_chunk();
+ if (ch != traits_type::eof()) {
+ *pptr() = ch;
+ pbump(1);
+ }
+ return ch;
+ }
+
+ 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
+ }
+
+ // 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/libnbtplusplus/src/io/stream_reader.cpp b/libnbtplusplus/src/io/stream_reader.cpp
new file mode 100644
index 0000000000..43032b1120
--- /dev/null
+++ b/libnbtplusplus/src/io/stream_reader.cpp
@@ -0,0 +1,122 @@
+// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag <ljfa-ag@web.de>
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+#include "io/stream_reader.h"
+#include "make_unique.h"
+#include "tag_compound.h"
+#include <istream>
+
+namespace nbt
+{
+ namespace io
+ {
+
+ static constexpr int MAX_DEPTH = 1024;
+
+ std::pair<std::string, std::unique_ptr<tag_compound>>
+ read_compound(std::istream& is, endian::endian e)
+ {
+ return stream_reader(is, e).read_compound();
+ }
+
+ std::pair<std::string, std::unique_ptr<tag>> 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)
+ {
+ }
+
+ std::istream& stream_reader::get_istr() const
+ {
+ return is;
+ }
+
+ endian::endian stream_reader::get_endian() const
+ {
+ return endian;
+ }
+
+ std::pair<std::string, std::unique_ptr<tag_compound>>
+ stream_reader::read_compound()
+ {
+ if (read_type() != tag_type::Compound) {
+ is.setstate(std::ios::failbit);
+ throw input_error("Tag is not a compound");
+ }
+ std::string key = read_string();
+ auto comp = make_unique<tag_compound>();
+ comp->read_payload(*this);
+ return {std::move(key), std::move(comp)};
+ }
+
+ std::pair<std::string, std::unique_ptr<tag>> stream_reader::read_tag()
+ {
+ tag_type type = read_type();
+ std::string key = read_string();
+ std::unique_ptr<tag> t = read_payload(type);
+ return {std::move(key), std::move(t)};
+ }
+
+ std::unique_ptr<tag> stream_reader::read_payload(tag_type type)
+ {
+ if (++depth > MAX_DEPTH)
+ throw input_error("Too deeply nested");
+ std::unique_ptr<tag> t = tag::create(type);
+ t->read_payload(*this);
+ --depth;
+ return t;
+ }
+
+ tag_type stream_reader::read_type(bool allow_end)
+ {
+ int type = is.get();
+ if (!is)
+ throw input_error("Error reading tag type");
+ if (!is_valid_type(type, allow_end)) {
+ is.setstate(std::ios::failbit);
+ throw input_error("Invalid tag type: " + std::to_string(type));
+ }
+ return static_cast<tag_type>(type);
+ }
+
+ std::string stream_reader::read_string()
+ {
+ uint16_t len;
+ read_num(len);
+ if (!is)
+ throw input_error("Error reading string");
+
+ std::string 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/libnbtplusplus/src/io/stream_writer.cpp b/libnbtplusplus/src/io/stream_writer.cpp
new file mode 100644
index 0000000000..5275c5a1b4
--- /dev/null
+++ b/libnbtplusplus/src/io/stream_writer.cpp
@@ -0,0 +1,59 @@
+// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag <ljfa-ag@web.de>
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+#include "io/stream_writer.h"
+#include <sstream>
+
+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());
+ write_string(key);
+ write_payload(t);
+ }
+
+ void stream_writer::write_string(const std::string& str)
+ {
+ if (str.size() > max_string_len) {
+ os.setstate(std::ios::failbit);
+ std::ostringstream sstr;
+ sstr << "String is too long for NBT (" << str.size() << " > "
+ << max_string_len << ")";
+ throw std::length_error(sstr.str());
+ }
+ write_num(static_cast<uint16_t>(str.size()));
+ os.write(str.data(), str.size());
+ }
+
+ } // namespace io
+} // namespace nbt