From 53173dd7c7b74b278a78c57bcf141c09b125506c Mon Sep 17 00:00:00 2001 From: BodgeMaster <> Date: Sun, 9 Jun 2024 02:51:32 +0200 Subject: [PATCH] lib/zlibutil, tools/zlibutil: moving things around This includes the following changes: - Move the data type wrangling into the lib to make it easier to use - Put the functions into their own `zlib` namespace - Use ErrorOr instead of exceptions - Error codes for compression/decompression --- src/lib/error.hpp | 8 +++ src/lib/zlibutil.cpp | 145 ++++++++++++++++++++++------------------- src/lib/zlibutil.hpp | 14 ++-- src/tools/zlibutil.cpp | 34 ++++++---- 4 files changed, 115 insertions(+), 86 deletions(-) diff --git a/src/lib/error.hpp b/src/lib/error.hpp index 207b0ee..697c9b4 100644 --- a/src/lib/error.hpp +++ b/src/lib/error.hpp @@ -17,6 +17,11 @@ #include + +//TODO: needed macros: +// TRY: takes a variable, a function call, and the ErrorOr return type of the calling function - intended to automatically unwrap the ErrorOr data type or propagate the error upwards +// RAISE: takes an error code and optionally a message to produce something like `return ErrorOr(true, errorCode, file, lineNumber, message)` + template struct ErrorOr { bool isError; @@ -100,6 +105,9 @@ namespace ErrorCodes { // when too much data is available const uint8_t OVERFLOW = 13; + const uint8_t COMPRESSION = 14; + const uint8_t DECOMPRESSION = 15; + const uint8_t UNIMPLEMENTED = 254; const uint8_t UNKNOWN = 255; diff --git a/src/lib/zlibutil.cpp b/src/lib/zlibutil.cpp index d9f1a85..e872cad 100644 --- a/src/lib/zlibutil.cpp +++ b/src/lib/zlibutil.cpp @@ -16,92 +16,101 @@ // version 3 along with this program. // If not, see https://www.gnu.org/licenses/agpl-3.0.en.html -#include "../lib/zlibutil.hpp" +// includes vector and error.hpp +#include "zlibutil.hpp" -#include -#include -#include -#include -#include #include #include -#include "../lib/cli.hpp" -#include "../lib/file.hpp" - #define EXIT_SUCCESS 0 #define EXIT_RUNTIME 1 #define EXIT_USAGE 2 -#include -#include -#include -#include - #define CHUNK_SIZE 16384 // Chunk size +namespace zlib { -std::vector compressData(const char* data, int size) { - z_stream zs; - memset(&zs, 0, sizeof(zs)); + ErrorOr> compressData(std::vector data) { + // I, too, love the fact that raw bytes are signed and therefore can have negative values. -_- + std::vector signedData = std::vector(data.begin(), data.end()); - if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) != Z_OK) - throw(std::runtime_error("deflateInit failed while compressing.")); + z_stream zs; + memset(&zs, 0, sizeof(zs)); - zs.next_in = reinterpret_cast(const_cast(data)); - zs.avail_in = size; + if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) != Z_OK) { + return ErrorOr>(true, ErrorCodes::COMPRESSION); + //TODO: include error message once implemented + //throw(std::runtime_error("deflateInit failed while compressing.")); + } - int ret; - char outbuffer[CHUNK_SIZE]; - std::vector compressedData; + zs.next_in = reinterpret_cast(reinterpret_cast(signedData.data())); + zs.avail_in = signedData.size(); - do { - zs.next_out = reinterpret_cast(outbuffer); - zs.avail_out = CHUNK_SIZE; + int ret; + char outbuffer[CHUNK_SIZE]; + std::vector compressedData; - ret = deflate(&zs, Z_FINISH); + do { + zs.next_out = reinterpret_cast(outbuffer); + zs.avail_out = CHUNK_SIZE; - if (compressedData.size() < zs.total_out) - compressedData.insert(compressedData.end(), outbuffer, outbuffer + CHUNK_SIZE - zs.avail_out); - } while (ret == Z_OK); + ret = deflate(&zs, Z_FINISH); - deflateEnd(&zs); + if (compressedData.size() < zs.total_out) + compressedData.insert(compressedData.end(), outbuffer, outbuffer + CHUNK_SIZE - zs.avail_out); + } while (ret == Z_OK); - if (ret != Z_STREAM_END) - throw(std::runtime_error("Error while compressing: " + std::to_string(ret))); + deflateEnd(&zs); + + if (ret != Z_STREAM_END) { + return ErrorOr>(true, ErrorCodes::COMPRESSION); + //TODO: include error message once implemented + //throw(std::runtime_error("Error while compressing: " + std::to_string(ret))); + } + + return ErrorOr>(std::vector(compressedData.begin(), compressedData.end())); + } + + + ErrorOr> decompressData(std::vector data) { + // I, too, love the fact that raw bytes are signed and therefore can have negative values. -_- + std::vector signedData = std::vector(data.begin(), data.end()); + + z_stream zs; + memset(&zs, 0, sizeof(zs)); + + if (inflateInit(&zs) != Z_OK) { + return ErrorOr>(true, ErrorCodes::DECOMPRESSION); + //TODO: include error message once implemented + //throw(std::runtime_error("inflateInit failed while decompressing.")); + } + + zs.next_in = reinterpret_cast(reinterpret_cast(signedData.data())); + zs.avail_in = signedData.size(); + + int ret; + char outbuffer[CHUNK_SIZE]; + std::vector decompressedData; + + do { + zs.next_out = reinterpret_cast(outbuffer); + zs.avail_out = CHUNK_SIZE; + + ret = inflate(&zs, 0); + + if (decompressedData.size() < zs.total_out) + decompressedData.insert(decompressedData.end(), outbuffer, outbuffer + CHUNK_SIZE - zs.avail_out); + } while (ret == Z_OK); + + inflateEnd(&zs); + + if (ret != Z_STREAM_END) { + return ErrorOr>(true, ErrorCodes::DECOMPRESSION); + //TODO: include error message once implemented + //throw(std::runtime_error("Error while decompressing: " + std::to_string(ret))); + } + + return ErrorOr>(std::vector(decompressedData.begin(), decompressedData.end())); + } - return compressedData; -} - - -std::vector decompressData(const char* data, int size) { - z_stream zs; - memset(&zs, 0, sizeof(zs)); - - if (inflateInit(&zs) != Z_OK) - throw(std::runtime_error("inflateInit failed while decompressing.")); - - zs.next_in = reinterpret_cast(const_cast(data)); - zs.avail_in = size; - - int ret; - char outbuffer[CHUNK_SIZE]; - std::vector decompressedData; - - do { - zs.next_out = reinterpret_cast(outbuffer); - zs.avail_out = CHUNK_SIZE; - - ret = inflate(&zs, 0); - - if (decompressedData.size() < zs.total_out) - decompressedData.insert(decompressedData.end(), outbuffer, outbuffer + CHUNK_SIZE - zs.avail_out); - } while (ret == Z_OK); - - inflateEnd(&zs); - - if (ret != Z_STREAM_END) - throw(std::runtime_error("Error while decompressing: " + std::to_string(ret))); - - return decompressedData; } diff --git a/src/lib/zlibutil.hpp b/src/lib/zlibutil.hpp index 3be3de2..861aeb8 100644 --- a/src/lib/zlibutil.hpp +++ b/src/lib/zlibutil.hpp @@ -16,12 +16,12 @@ // version 3 along with this program. // If not, see https://www.gnu.org/licenses/agpl-3.0.en.html +#pragma once + #include +#include "error.hpp" -#ifndef ZLIBUTIL_H -#define ZLIBUTIL_H - -std::vector compressData(const char* data, int size); -std::vector decompressData(const char* data, int size); - -#endif // ZLIBUTIL_H +namespace zlib { + ErrorOr> compressData(std::vector data); + ErrorOr> decompressData(std::vector data); +} diff --git a/src/tools/zlibutil.cpp b/src/tools/zlibutil.cpp index f9e3bdb..184f05b 100644 --- a/src/tools/zlibutil.cpp +++ b/src/tools/zlibutil.cpp @@ -28,6 +28,7 @@ #include "../lib/cli.hpp" #include "../lib/file.hpp" #include "../lib/zlibutil.hpp" +#include "../lib/error.hpp" #define EXIT_SUCCESS 0 #define EXIT_RUNTIME 1 @@ -112,14 +113,18 @@ int main(int argc, char* argv[]) { return EXIT_RUNTIME; } File* file = filePointer.value; - File *writeFile; + File* writeFile; if (cliParser.getFlag("decompress").value) { - std::vector bytes = file->read(file->size.value).value; // this is what you get from lib/file IIRC - std::vector differentBytes = std::vector(bytes.begin(), bytes.end()); + std::vector bytes = file->read(file->size.value).value; - std::vector compressed = decompressData(differentBytes.data(), file->size.value); - std::vector unsigneddata = std::vector(compressed.begin(), compressed.end()); + ErrorOr> decompressed = zlib::decompressData(bytes); + if (decompressed.isError) { + std::cout << "Error: Failed to decompress: " << filename << std::endl; + delete file; + // not cleaning up writeFile here bc it hasn't been created + return EXIT_RUNTIME; + } std::string outFilename; if (filename.length() > 3 && filename.rfind(".zz") == filename.length()-3) { @@ -128,16 +133,23 @@ int main(int argc, char* argv[]) { outFilename = filename + ".uncompressed"; } writeFile = File::open(outFilename, 'w').value; - writeFile->write(unsigneddata); + writeFile->write(decompressed.value); writeFile->close(); } else { - std::vector bytes = file->read(file->size.value).value; // this is what you get from lib/file IIRC - std::vector differentBytes = std::vector(bytes.begin(), bytes.end()); + std::vector bytes = file->read(file->size.value).value; - std::vector compressed = compressData(differentBytes.data(), file->size.value); - std::vector unsigneddata = std::vector(compressed.begin(), compressed.end()); + ErrorOr> compressed = zlib::compressData(bytes); + if (compressed.isError) { + std::cout << "Error: Failed to compress: " << filename << std::endl; + delete file; + // not cleaning up writeFile here bc it hasn't been created + return EXIT_RUNTIME; + } writeFile = File::open(filename + ".zz", 'w').value; - writeFile->write(unsigneddata); + writeFile->write(compressed.value); writeFile->close(); } + + delete file; + delete writeFile; }