osb/source/core/StarCompression.cpp

237 lines
6.4 KiB
C++
Raw Normal View History

2023-06-20 04:33:09 +00:00
#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;
auto tempBuffer = std::make_unique<unsigned char[]>(BUFSIZE);
2023-06-20 04:33:09 +00:00
z_stream strm{};
2023-06-20 04:33:09 +00:00
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
int deflate_res = deflateInit(&strm, compression);
if (deflate_res != Z_OK)
2023-06-27 10:23:44 +00:00
throw IOException(strf("Failed to initialise deflate ({})", deflate_res));
2023-06-20 04:33:09 +00:00
strm.next_in = (unsigned char*)in.ptr();
strm.avail_in = in.size();
strm.next_out = tempBuffer.get();
2023-06-20 04:33:09 +00:00
strm.avail_out = BUFSIZE;
while (deflate_res == Z_OK) {
deflate_res = deflate(&strm, Z_FINISH);
if (strm.avail_out == 0) {
out.append((char const*)tempBuffer.get(), BUFSIZE);
strm.next_out = tempBuffer.get();
2023-06-20 04:33:09 +00:00
strm.avail_out = BUFSIZE;
}
}
deflateEnd(&strm);
if (deflate_res != Z_STREAM_END)
2023-06-27 10:23:44 +00:00
throw IOException(strf("Internal error in uncompressData, deflate_res is {}", deflate_res));
2023-06-20 04:33:09 +00:00
out.append((char const*)tempBuffer.get(), BUFSIZE - strm.avail_out);
2023-06-20 04:33:09 +00:00
}
ByteArray compressData(ByteArray const& in, CompressionLevel compression) {
ByteArray out = ByteArray::withReserve(in.size());
compressData(in, out, compression);
return out;
}
void uncompressData(const char* in, size_t inLen, ByteArray& out, size_t limit) {
2023-06-20 04:33:09 +00:00
out.clear();
if (!inLen)
2023-06-20 04:33:09 +00:00
return;
const size_t BUFSIZE = 32 * 1024;
auto tempBuffer = std::make_unique<unsigned char[]>(BUFSIZE);
2023-06-20 04:33:09 +00:00
z_stream strm{};
2023-06-20 04:33:09 +00:00
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
int inflate_res = inflateInit(&strm);
if (inflate_res != Z_OK)
2023-06-27 10:23:44 +00:00
throw IOException(strf("Failed to initialise inflate ({})", inflate_res));
2023-06-20 04:33:09 +00:00
strm.next_in = (unsigned char*)in;
strm.avail_in = inLen;
strm.next_out = tempBuffer.get();
2023-06-20 04:33:09 +00:00
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*)tempBuffer.get(), BUFSIZE);
strm.next_out = tempBuffer.get();
2023-06-20 04:33:09 +00:00
strm.avail_out = BUFSIZE;
if (limit && out.size() >= limit) {
inflateEnd(&strm);
throw IOException(strf("hit uncompressData limit of {} bytes", limit));
break;
}
2023-06-20 04:33:09 +00:00
} else if (inflate_res == Z_BUF_ERROR) {
break;
}
}
inflateEnd(&strm);
if (inflate_res != Z_STREAM_END)
2023-06-27 10:23:44 +00:00
throw IOException(strf("Internal error in uncompressData, inflate_res is {}", inflate_res));
2023-06-20 04:33:09 +00:00
out.append((char const*)tempBuffer.get(), BUFSIZE - strm.avail_out);
2023-06-20 04:33:09 +00:00
}
ByteArray uncompressData(const char* in, size_t inLen, size_t limit) {
ByteArray out = ByteArray::withReserve(inLen);
uncompressData(in, inLen, out, limit);
2023-06-20 04:33:09 +00:00
return out;
}
void uncompressData(ByteArray const& in, ByteArray& out, size_t limit) {
uncompressData(in.ptr(), in.size(), out, limit);
}
ByteArray uncompressData(ByteArray const& in, size_t limit) {
return uncompressData(in.ptr(), in.size(), limit);
}
2023-06-20 04:33:09 +00:00
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(std::move(filename));
2023-06-20 04:33:09 +00:00
}
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) {
2023-06-27 10:23:44 +00:00
throw IOException::format("Seek error: {}", gzerror((gzFile)m_file, 0));
2023-06-20 04:33:09 +00:00
} 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)
2023-06-27 10:23:44 +00:00
throw IOException::format("Read error: {}", gzerror((gzFile)m_file, 0));
2023-06-20 04:33:09 +00:00
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)
2023-06-27 10:23:44 +00:00
throw IOException::format("Write error: {}", gzerror((gzFile)m_file, 0));
2023-06-20 04:33:09 +00:00
else
return (size_t)ret;
}
void CompressedFile::setFilename(String filename) {
if (isOpen())
throw IOException("Cannot call setFilename while CompressedFile is open");
m_filename = std::move(filename);
2023-06-20 04:33:09 +00:00
}
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)
2023-06-27 10:23:44 +00:00
throw IOException::format("Cannot open filename '{}'", m_filename);
2023-06-20 04:33:09 +00:00
}
void CompressedFile::close() {
if (m_file)
gzclose((gzFile)m_file);
m_file = 0;
setMode(IOMode::Closed);
}
}