Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/core/StarCompression.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/core/StarCompression.cpp')
-rw-r--r--source/core/StarCompression.cpp223
1 files changed, 223 insertions, 0 deletions
diff --git a/source/core/StarCompression.cpp b/source/core/StarCompression.cpp
new file mode 100644
index 0000000..e8d282c
--- /dev/null
+++ b/source/core/StarCompression.cpp
@@ -0,0 +1,223 @@
+#include "StarCompression.hpp"
+#include "StarFormat.hpp"
+#include "StarLexicalCast.hpp"
+
+#include <zlib.h>
+#include <errno.h>
+#include <string.h>
+
+namespace Star {
+
+void compressData(ByteArray const& in, ByteArray& out, CompressionLevel compression) {
+ out.clear();
+
+ if (in.empty())
+ return;
+
+ const size_t BUFSIZE = 32 * 1024;
+ unsigned char temp_buffer[BUFSIZE];
+
+ z_stream strm;
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ int deflate_res = deflateInit(&strm, compression);
+ if (deflate_res != Z_OK)
+ throw IOException(strf("Failed to initialise deflate (%d)", deflate_res));
+
+ strm.next_in = (unsigned char*)in.ptr();
+ strm.avail_in = in.size();
+ strm.next_out = temp_buffer;
+ strm.avail_out = BUFSIZE;
+ while (deflate_res == Z_OK) {
+ deflate_res = deflate(&strm, Z_FINISH);
+ if (strm.avail_out == 0) {
+ out.append((char const*)temp_buffer, BUFSIZE);
+ strm.next_out = temp_buffer;
+ strm.avail_out = BUFSIZE;
+ }
+ }
+ deflateEnd(&strm);
+
+ if (deflate_res != Z_STREAM_END)
+ throw IOException(strf("Internal error in uncompressData, deflate_res is %s", deflate_res));
+
+ out.append((char const*)temp_buffer, BUFSIZE - strm.avail_out);
+}
+
+ByteArray compressData(ByteArray const& in, CompressionLevel compression) {
+ ByteArray out = ByteArray::withReserve(in.size());
+ compressData(in, out, compression);
+ return out;
+}
+
+void uncompressData(ByteArray const& in, ByteArray& out) {
+ out.clear();
+
+ if (in.empty())
+ return;
+
+ const size_t BUFSIZE = 32 * 1024;
+ unsigned char temp_buffer[BUFSIZE];
+
+ z_stream strm;
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ int inflate_res = inflateInit(&strm);
+ if (inflate_res != Z_OK)
+ throw IOException(strf("Failed to initialise inflate (%d)", inflate_res));
+
+ strm.next_in = (unsigned char*)in.ptr();
+ strm.avail_in = in.size();
+ strm.next_out = temp_buffer;
+ strm.avail_out = BUFSIZE;
+
+ while (inflate_res == Z_OK || inflate_res == Z_BUF_ERROR) {
+ inflate_res = inflate(&strm, Z_FINISH);
+ if (strm.avail_out == 0) {
+ out.append((char const*)temp_buffer, BUFSIZE);
+ strm.next_out = temp_buffer;
+ strm.avail_out = BUFSIZE;
+ } else if (inflate_res == Z_BUF_ERROR) {
+ break;
+ }
+ }
+ inflateEnd(&strm);
+
+ if (inflate_res != Z_STREAM_END)
+ throw IOException(strf("Internal error in uncompressData, inflate_res is %s", inflate_res));
+
+ out.append((char const*)temp_buffer, BUFSIZE - strm.avail_out);
+}
+
+ByteArray uncompressData(ByteArray const& in) {
+ ByteArray out = ByteArray::withReserve(in.size());
+ uncompressData(in, out);
+ return out;
+}
+
+CompressedFilePtr CompressedFile::open(String const& filename, IOMode mode, CompressionLevel comp) {
+ CompressedFilePtr f = make_shared<CompressedFile>(filename);
+ f->open(mode, comp);
+ return f;
+}
+
+CompressedFile::CompressedFile()
+ : IODevice(IOMode::Closed), m_file(0), m_compression(MediumCompression) {}
+
+CompressedFile::CompressedFile(String filename)
+ : IODevice(IOMode::Closed), m_file(0), m_compression(MediumCompression) {
+ setFilename(move(filename));
+}
+
+CompressedFile::~CompressedFile() {
+ close();
+}
+
+StreamOffset CompressedFile::pos() {
+ return gztell((gzFile)m_file);
+}
+
+void CompressedFile::seek(StreamOffset offset, IOSeek seekMode) {
+ StreamOffset begPos = pos();
+
+ int retCode;
+ if (seekMode == IOSeek::Relative) {
+ retCode = gzseek((gzFile)m_file, (z_off_t)offset, SEEK_CUR);
+ } else if (seekMode == IOSeek::Absolute) {
+ retCode = gzseek((gzFile)m_file, (z_off_t)offset, SEEK_SET);
+ } else {
+ throw IOException("Cannot seek with SeekEnd in compressed file");
+ }
+
+ StreamOffset endPos = pos();
+
+ if (retCode < 0) {
+ throw IOException::format("Seek error: %s", gzerror((gzFile)m_file, 0));
+ } else if ((seekMode == IOSeek::Relative && begPos + offset != endPos)
+ || (seekMode == IOSeek::Absolute && offset != endPos)) {
+ throw EofException("Error, unexpected end of file found");
+ }
+}
+
+bool CompressedFile::atEnd() {
+ return gzeof((gzFile)m_file);
+}
+
+size_t CompressedFile::read(char* data, size_t len) {
+ if (len == 0)
+ return 0;
+
+ int ret = gzread((gzFile)m_file, data, len);
+ if (ret == 0)
+ throw EofException("Error, unexpected end of file found");
+ else if (ret == -1)
+ throw IOException::format("Read error: %s", gzerror((gzFile)m_file, 0));
+ else
+ return (size_t)ret;
+}
+
+size_t CompressedFile::write(const char* data, size_t len) {
+ if (len == 0)
+ return 0;
+
+ int ret = gzwrite((gzFile)m_file, data, len);
+ if (ret == 0)
+ throw IOException::format("Write error: %s", gzerror((gzFile)m_file, 0));
+ else
+ return (size_t)ret;
+}
+
+void CompressedFile::setFilename(String filename) {
+ if (isOpen())
+ throw IOException("Cannot call setFilename while CompressedFile is open");
+ m_filename = move(filename);
+}
+
+void CompressedFile::setCompression(CompressionLevel compression) {
+ if (isOpen())
+ throw IOException("Cannot call setCompression while CompressedFile is open");
+ m_compression = compression;
+}
+
+void CompressedFile::open(IOMode mode, CompressionLevel compression) {
+ close();
+ setCompression(compression);
+ open(mode);
+}
+
+void CompressedFile::sync() {
+ gzflush((gzFile)m_file, Z_FULL_FLUSH);
+}
+
+void CompressedFile::open(IOMode mode) {
+ setMode(mode);
+ String modeString;
+
+ if (mode & IOMode::Append) {
+ throw IOException("CompressedFile not compatible with Append mode");
+ } else if ((mode & IOMode::Read) && (mode & IOMode::Write)) {
+ throw IOException("CompressedFile not compatible with ReadWrite mode");
+ } else if (mode & IOMode::Write) {
+ modeString = "wb";
+ } else if (mode & IOMode::Read) {
+ modeString = "rb";
+ }
+
+ modeString += toString(m_compression);
+
+ m_file = gzopen(m_filename.utf8Ptr(), modeString.utf8Ptr());
+
+ if (!m_file)
+ throw IOException::format("Cannot open filename '%s'", m_filename);
+}
+
+void CompressedFile::close() {
+ if (m_file)
+ gzclose((gzFile)m_file);
+ m_file = 0;
+ setMode(IOMode::Closed);
+}
+
+}