summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Libraries/CMakeLists.txt1
-rw-r--r--Libraries/LibCompress/CMakeLists.txt7
-rw-r--r--Libraries/LibCompress/Deflate.cpp428
-rw-r--r--Libraries/LibCompress/Deflate.h98
-rw-r--r--Libraries/LibCompress/Zlib.cpp72
-rw-r--r--Libraries/LibCompress/Zlib.h52
-rw-r--r--Meta/Lagom/CMakeLists.txt3
-rw-r--r--Userland/CMakeLists.txt1
-rw-r--r--Userland/test-compress.cpp103
9 files changed, 764 insertions, 1 deletions
diff --git a/Libraries/CMakeLists.txt b/Libraries/CMakeLists.txt
index 1d460d9f84..e0180cf3ba 100644
--- a/Libraries/CMakeLists.txt
+++ b/Libraries/CMakeLists.txt
@@ -3,6 +3,7 @@ add_subdirectory(LibC)
add_subdirectory(LibCore)
add_subdirectory(LibCrypt)
add_subdirectory(LibCrypto)
+add_subdirectory(LibCompress)
add_subdirectory(LibDebug)
add_subdirectory(LibDesktop)
add_subdirectory(LibGemini)
diff --git a/Libraries/LibCompress/CMakeLists.txt b/Libraries/LibCompress/CMakeLists.txt
new file mode 100644
index 0000000000..a61ff5a486
--- /dev/null
+++ b/Libraries/LibCompress/CMakeLists.txt
@@ -0,0 +1,7 @@
+set(SOURCES
+ Deflate.cpp
+ Zlib.cpp
+)
+
+serenity_lib(LibCompress compression)
+target_link_libraries(LibCompress LibC)
diff --git a/Libraries/LibCompress/Deflate.cpp b/Libraries/LibCompress/Deflate.cpp
new file mode 100644
index 0000000000..35880cf15e
--- /dev/null
+++ b/Libraries/LibCompress/Deflate.cpp
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/Assertions.h>
+#include <AK/LogStream.h>
+#include <AK/Span.h>
+#include <AK/Types.h>
+#include <AK/Vector.h>
+#include <LibCompress/Deflate.h>
+
+namespace Compress {
+
+Vector<u8> Deflate::decompress()
+{
+ bool is_final_block = false;
+
+ do {
+ is_final_block = m_reader.read();
+ auto block_type = m_reader.read_bits(2);
+
+ switch (block_type) {
+ case 0:
+ decompress_uncompressed_block();
+ break;
+ case 1:
+ decompress_static_block();
+ break;
+ case 2:
+ decompress_dynamic_block();
+ break;
+ case 3:
+ dbg() << "Block contains reserved block type...";
+ ASSERT_NOT_REACHED();
+ break;
+ default:
+ dbg() << "Invalid block type was read...";
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ } while (!is_final_block);
+
+ return m_output_buffer;
+}
+
+void Deflate::decompress_uncompressed_block()
+{
+ // Align to the next byte boundary.
+ while (m_reader.get_bit_byte_offset() != 0) {
+ m_reader.read();
+ }
+
+ auto length = m_reader.read_bits(16) & 0xFFFF;
+ auto negated_length = m_reader.read_bits(16) & 0xFFFF;
+
+ if ((length ^ 0xFFFF) != negated_length) {
+ dbg() << "Block length is invalid...";
+ ASSERT_NOT_REACHED();
+ }
+
+ for (size_t i = 0; i < length; i++) {
+ auto byte = m_reader.read_byte();
+ if (byte < 0) {
+ dbg() << "Ran out of bytes while reading uncompressed block...";
+ ASSERT_NOT_REACHED();
+ }
+
+ m_output_buffer.append(byte);
+ m_history_buffer.enqueue(byte);
+ }
+}
+
+void Deflate::decompress_static_block()
+{
+ decompress_huffman_block(m_literal_length_codes, &m_fixed_distance_codes);
+}
+
+void Deflate::decompress_dynamic_block()
+{
+ auto codes = decode_huffman_codes();
+ if (codes.size() == 2) {
+ decompress_huffman_block(codes[0], &codes[1]);
+ } else {
+ decompress_huffman_block(codes[0], nullptr);
+ }
+}
+
+void Deflate::decompress_huffman_block(CanonicalCode& length_codes, CanonicalCode* distance_codes)
+{
+ for (;;) {
+ u32 symbol = length_codes.next_symbol(m_reader);
+
+ // End of block.
+ if (symbol == 256) {
+ break;
+ }
+
+ // literal byte.
+ if (symbol < 256) {
+ m_history_buffer.enqueue(symbol);
+ m_output_buffer.append(symbol);
+ continue;
+ }
+
+ // Length and distance for copying.
+ ASSERT(distance_codes);
+
+ auto run = decode_run_length(symbol);
+ if (run < 3 || run > 258) {
+ dbg() << "Invalid run length";
+ ASSERT_NOT_REACHED();
+ }
+
+ auto distance_symbol = distance_codes->next_symbol(m_reader);
+ auto distance = decode_distance(distance_symbol);
+ if (distance < 1 || distance > 32768) {
+ dbg() << "Invalid distance";
+ ASSERT_NOT_REACHED();
+ }
+
+ copy_from_history(distance, run);
+ }
+}
+
+Vector<CanonicalCode> Deflate::decode_huffman_codes()
+{
+ // FIXME: This path is not tested.
+ Vector<CanonicalCode> result;
+
+ auto length_code_count = m_reader.read_bits(5) + 257;
+ auto distance_code_count = m_reader.read_bits(5) + 1;
+
+ size_t length_code_code_length = m_reader.read_bits(4) + 4;
+
+ Vector<u8> code_length_code_length;
+ code_length_code_length.resize(19);
+ code_length_code_length[16] = m_reader.read_bits(3);
+ code_length_code_length[17] = m_reader.read_bits(3);
+ code_length_code_length[18] = m_reader.read_bits(3);
+ code_length_code_length[0] = m_reader.read_bits(3);
+ for (size_t i = 0; i < length_code_code_length; i++) {
+ auto index = (i % 2 == 0) ? (8 + (i / 2)) : (7 - (i / 2));
+ code_length_code_length[index] = m_reader.read_bits(3);
+ }
+
+ auto code_length_code = CanonicalCode(code_length_code_length);
+
+ Vector<u32> code_lens;
+ code_lens.resize(length_code_count + distance_code_count);
+
+ for (size_t index = 0; index < code_lens.capacity();) {
+ auto symbol = code_length_code.next_symbol(m_reader);
+
+ if (symbol <= 15) {
+ code_lens[index] = symbol;
+ index++;
+ continue;
+ }
+
+ u32 run_length;
+ u32 run_value = 0;
+
+ if (symbol == 16) {
+ if (index == 0) {
+ dbg() << "No code length value avaliable";
+ ASSERT_NOT_REACHED();
+ }
+
+ run_length = m_reader.read_bits(2) + 3;
+ run_value = code_lens[index - 1];
+ } else if (symbol == 17) {
+ run_length = m_reader.read_bits(3) + 3;
+ } else if (symbol == 18) {
+ run_length = m_reader.read_bits(7) + 11;
+ } else {
+ dbg() << "Code symbol is out of range!";
+ ASSERT_NOT_REACHED();
+ }
+
+ u32 end = index + run_length;
+ if (end > code_lens.capacity()) {
+ dbg() << "Code run is out of range!";
+ ASSERT_NOT_REACHED();
+ }
+
+ memset(code_lens.data() + index, run_value, run_length);
+ index = end;
+ }
+
+ Vector<u8> literal_codes;
+ literal_codes.resize(length_code_count);
+ memcpy(literal_codes.data(), code_lens.data(), literal_codes.capacity());
+ result.append(CanonicalCode(literal_codes));
+
+ Vector<u8> distance_codes;
+ distance_codes.resize(distance_code_count);
+ memcpy(distance_codes.data(), code_lens.data() + length_code_count, distance_codes.capacity());
+
+ if (distance_code_count == 1 && distance_codes[0] == 0) {
+ return result;
+ }
+
+ u8 one_count = 0;
+ u8 other_count = 0;
+
+ for (size_t i = 0; i < distance_codes.capacity(); i++) {
+ u8 value = distance_codes.at(i);
+
+ if (value == 1) {
+ one_count++;
+ } else if (value > 1) {
+ other_count++;
+ }
+ }
+
+ if (one_count == 1 && other_count == 0) {
+ distance_codes.resize(32);
+ distance_codes[31] = 1;
+ }
+
+ result.append(CanonicalCode(distance_codes));
+ return result;
+}
+
+u32 Deflate::decode_run_length(u32 symbol)
+{
+ if (symbol <= 264) {
+ return symbol - 254;
+ }
+
+ if (symbol <= 284) {
+ auto extra_bits = (symbol - 261) / 4;
+ return ((((symbol - 265) % 4) + 4) << extra_bits) + 3 + m_reader.read_bits(extra_bits);
+ }
+
+ if (symbol == 285) {
+ return 258;
+ }
+
+ dbg() << "Found invalid symbol in run length " << symbol;
+ ASSERT_NOT_REACHED();
+}
+
+u32 Deflate::decode_distance(u32 symbol)
+{
+ if (symbol <= 3) {
+ return symbol + 1;
+ }
+
+ if (symbol <= 29) {
+ auto extra_bits = (symbol / 2) - 1;
+ return (((symbol % 2) + 2) << extra_bits) + 1 + m_reader.read_bits(extra_bits);
+ }
+
+ dbg() << "Found invalid symbol in distance" << symbol;
+ ASSERT_NOT_REACHED();
+}
+
+void Deflate::copy_from_history(u32 distance, u32 run)
+{
+ auto head_index = (m_history_buffer.head_index() + m_history_buffer.size()) % m_history_buffer.capacity();
+ auto read_index = (head_index - distance + m_history_buffer.capacity()) % m_history_buffer.capacity();
+
+ for (size_t i = 0; i < run; i++) {
+ auto data = m_history_buffer.at(read_index++);
+ m_output_buffer.append(data);
+ m_history_buffer.enqueue(data);
+ }
+}
+
+i8 BitStreamReader::read()
+{
+ if (m_current_byte == -1) {
+ return -1;
+ }
+
+ if (m_remaining_bits == 0) {
+ if (m_data_index + 1 > m_data.size())
+ return -1;
+
+ m_current_byte = m_data.at(m_data_index++);
+ m_remaining_bits = 8;
+ }
+
+ m_remaining_bits--;
+ return (m_current_byte >> (7 - m_remaining_bits)) & 1;
+}
+
+
+i8 BitStreamReader::read_byte()
+{
+ m_current_byte = 0;
+ m_remaining_bits = 0;
+
+ if (m_data_index + 1 > m_data.size())
+ return -1;
+
+ return m_data.at(m_data_index++);
+}
+
+
+u8 BitStreamReader::get_bit_byte_offset()
+{
+ return (8 - m_remaining_bits) % 8;
+}
+
+u32 BitStreamReader::read_bits(u8 count)
+{
+ ASSERT(count > 0 && count < 32);
+
+ u32 result = 0;
+ for (size_t i = 0; i < count; i++) {
+ result |= read() << i;
+ }
+ return result;
+}
+
+Vector<u8> Deflate::generate_literal_length_codes()
+{
+ Vector<u8> ll_codes;
+ ll_codes.resize(288);
+ memset(ll_codes.data() + 0, 8, 144 - 0);
+ memset(ll_codes.data() + 144, 9, 256 - 144);
+ memset(ll_codes.data() + 256, 7, 280 - 256);
+ memset(ll_codes.data() + 280, 8, 288 - 280);
+ return ll_codes;
+}
+
+Vector<u8> Deflate::generate_fixed_distance_codes()
+{
+ Vector<u8> fd_codes;
+ fd_codes.resize(32);
+ memset(fd_codes.data(), 5, 32);
+ return fd_codes;
+}
+
+CanonicalCode::CanonicalCode(Vector<u8> codes)
+{
+ m_symbol_codes.resize(codes.size());
+ m_symbol_values.resize(codes.size());
+
+ auto allocated_symbols_count = 0;
+ auto next_code = 0;
+
+ for (size_t code_length = 1; code_length <= 15; code_length++) {
+ next_code <<= 1;
+ auto start_bit = 1 << code_length;
+
+ for (size_t symbol = 0; symbol < codes.size(); symbol++) {
+ if (codes.at(symbol) != code_length) {
+ continue;
+ }
+
+ if (next_code > start_bit) {
+ dbg() << "Canonical code overflows the huffman tree";
+ ASSERT_NOT_REACHED();
+ }
+
+ m_symbol_codes[allocated_symbols_count] = start_bit | next_code;
+ m_symbol_values[allocated_symbols_count] = symbol;
+
+ allocated_symbols_count++;
+ next_code++;
+ }
+ }
+
+ if (next_code != (1 << 15)) {
+ dbg() << "Canonical code underflows the huffman tree " << next_code;
+ ASSERT_NOT_REACHED();
+ }
+}
+
+i32 binary_search(Vector<u32>& heystack, u32 needle)
+{
+ i32 low = 0;
+ i32 high = heystack.size();
+
+ while (low <= high) {
+ u32 mid = (low + high) >> 1;
+ u32 value = heystack.at(mid);
+
+ if (value < needle) {
+ low = mid + 1;
+ } else if (value > needle) {
+ high = mid - 1;
+ } else {
+ return mid;
+ }
+ }
+
+ return -1;
+}
+
+u32 CanonicalCode::next_symbol(BitStreamReader& reader)
+{
+ auto code_bits = 1;
+
+ for (;;) {
+ code_bits = code_bits << 1 | reader.read();
+ i32 index = binary_search(m_symbol_codes, code_bits);
+ if (index >= 0) {
+ return m_symbol_values.at(index);
+ }
+ }
+}
+}
diff --git a/Libraries/LibCompress/Deflate.h b/Libraries/LibCompress/Deflate.h
new file mode 100644
index 0000000000..54e053155b
--- /dev/null
+++ b/Libraries/LibCompress/Deflate.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/CircularQueue.h>
+#include <AK/Span.h>
+#include <AK/Types.h>
+#include <AK/Vector.h>
+#include <cstring>
+
+namespace Compress {
+
+// Reads one bit at a time starting with the rightmost bit
+class BitStreamReader {
+public:
+ BitStreamReader(ReadonlyBytes data)
+ : m_data(data)
+ {
+ }
+
+ i8 read();
+ i8 read_byte();
+ u32 read_bits(u8);
+ u8 get_bit_byte_offset();
+
+private:
+ ReadonlyBytes m_data;
+ size_t m_data_index { 0 };
+
+ i8 m_current_byte { 0 };
+ u8 m_remaining_bits { 0 };
+};
+
+class CanonicalCode {
+public:
+ CanonicalCode(Vector<u8>);
+ u32 next_symbol(BitStreamReader&);
+
+private:
+ Vector<u32> m_symbol_codes;
+ Vector<u32> m_symbol_values;
+};
+
+class Deflate {
+public:
+ Deflate(ReadonlyBytes data)
+ : m_reader(data)
+ , m_literal_length_codes(generate_literal_length_codes())
+ , m_fixed_distance_codes(generate_fixed_distance_codes())
+ {
+ }
+
+ Vector<u8> decompress();
+
+private:
+ void decompress_uncompressed_block();
+ void decompress_static_block();
+ void decompress_dynamic_block();
+ void decompress_huffman_block(CanonicalCode&, CanonicalCode*);
+ Vector<CanonicalCode> decode_huffman_codes();
+ void copy_from_history(u32, u32);
+ u32 decode_run_length(u32);
+ u32 decode_distance(u32);
+ Vector<u8> generate_literal_length_codes();
+ Vector<u8> generate_fixed_distance_codes();
+
+ BitStreamReader m_reader;
+ CircularQueue<u8, 32 * 1024> m_history_buffer;
+ Vector<u8, 256> m_output_buffer;
+
+ CanonicalCode m_literal_length_codes;
+ CanonicalCode m_fixed_distance_codes;
+};
+}
diff --git a/Libraries/LibCompress/Zlib.cpp b/Libraries/LibCompress/Zlib.cpp
new file mode 100644
index 0000000000..7aadc2d5ea
--- /dev/null
+++ b/Libraries/LibCompress/Zlib.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/Assertions.h>
+#include <AK/Span.h>
+#include <AK/Types.h>
+#include <AK/Vector.h>
+#include <LibCompress/Deflate.h>
+#include <LibCompress/Zlib.h>
+
+namespace Compress {
+
+Zlib::Zlib(ReadonlyBytes data)
+{
+ m_input_data = data;
+
+ u8 compression_info = data.at(0);
+ u8 flags = data.at(1);
+
+ m_compression_method = compression_info & 0xF;
+ m_compression_info = (compression_info >> 4) & 0xF;
+ m_check_bits = flags & 0xF;
+ m_has_dictionary = (flags >> 5) & 0x1;
+ m_compression_level = (flags >> 6) & 0x3;
+
+ ASSERT(m_compression_method == 8);
+ ASSERT(m_compression_info == 7);
+ ASSERT(!m_has_dictionary);
+ ASSERT((compression_info * 256 + flags) % 31 == 0);
+
+ m_data_bytes = data.slice(2, data.size() - 2 - 4);
+}
+
+Vector<u8> Zlib::decompress()
+{
+ return Deflate(m_data_bytes).decompress();
+}
+
+u32 Zlib::checksum()
+{
+ if (!m_checksum) {
+ auto bytes = m_input_data.slice(m_input_data.size() - 4, 4);
+ m_checksum = bytes.at(0) << 24 | bytes.at(1) << 16 | bytes.at(2) << 8 || bytes.at(3);
+ }
+
+ return m_checksum;
+}
+
+}
diff --git a/Libraries/LibCompress/Zlib.h b/Libraries/LibCompress/Zlib.h
new file mode 100644
index 0000000000..cd99bbb0f0
--- /dev/null
+++ b/Libraries/LibCompress/Zlib.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Span.h>
+#include <AK/Types.h>
+#include <AK/Vector.h>
+
+namespace Compress {
+class Zlib {
+public:
+ Zlib(ReadonlyBytes data);
+
+ Vector<u8> decompress();
+ u32 checksum();
+
+private:
+ u8 m_compression_method;
+ u8 m_compression_info;
+ u8 m_check_bits;
+ u8 m_has_dictionary;
+ u8 m_compression_level;
+
+ u32 m_checksum;
+ ReadonlyBytes m_input_data;
+ ReadonlyBytes m_data_bytes;
+};
+}
diff --git a/Meta/Lagom/CMakeLists.txt b/Meta/Lagom/CMakeLists.txt
index ba899c5681..b0bb13d242 100644
--- a/Meta/Lagom/CMakeLists.txt
+++ b/Meta/Lagom/CMakeLists.txt
@@ -49,6 +49,7 @@ file(GLOB LIBMARKDOWN_SOURCES "../../Libraries/LibMarkdown/*.cpp")
file(GLOB LIBX86_SOURCES "../../Libraries/LibX86/*.cpp")
file(GLOB LIBJS_SOURCES "../../Libraries/LibJS/*.cpp")
file(GLOB LIBJS_SUBDIR_SOURCES "../../Libraries/LibJS/*/*.cpp")
+file(GLOB LIBCOMPRESS_SOURCES "../../Libraries/LibCompress/*.cpp")
file(GLOB LIBCRYPTO_SOURCES "../../Libraries/LibCrypto/*.cpp")
file(GLOB LIBCRYPTO_SUBDIR_SOURCES "../../Libraries/LibCrypto/*/*.cpp")
file(GLOB LIBTLS_SOURCES "../../Libraries/LibTLS/*.cpp")
@@ -56,7 +57,7 @@ file(GLOB SHELL_SOURCES "../../Shell/*.cpp")
file(GLOB SHELL_TESTS "../../Shell/Tests/*.sh")
set(LAGOM_CORE_SOURCES ${AK_SOURCES} ${LIBCORE_SOURCES})
-set(LAGOM_MORE_SOURCES ${LIBIPC_SOURCES} ${LIBLINE_SOURCES} ${LIBJS_SOURCES} ${LIBJS_SUBDIR_SOURCES} ${LIBX86_SOURCES} ${LIBCRYPTO_SOURCES} ${LIBCRYPTO_SUBDIR_SOURCES} ${LIBTLS_SOURCES} ${LIBMARKDOWN_SOURCES} ${LIBGEMINI_SOURCES} ${LIBGFX_SOURCES})
+set(LAGOM_MORE_SOURCES ${LIBIPC_SOURCES} ${LIBLINE_SOURCES} ${LIBJS_SOURCES} ${LIBJS_SUBDIR_SOURCES} ${LIBX86_SOURCES} ${LIBCRYPTO_SOURCES} ${LIBCOMPRESS_SOURCES} ${LIBCRYPTO_SUBDIR_SOURCES} ${LIBTLS_SOURCES} ${LIBMARKDOWN_SOURCES} ${LIBGEMINI_SOURCES} ${LIBGFX_SOURCES})
include_directories (../../)
include_directories (../../Libraries/)
diff --git a/Userland/CMakeLists.txt b/Userland/CMakeLists.txt
index d2beb4c401..86a36ce680 100644
--- a/Userland/CMakeLists.txt
+++ b/Userland/CMakeLists.txt
@@ -34,6 +34,7 @@ target_link_libraries(paste LibGUI)
target_link_libraries(pro LibProtocol)
target_link_libraries(su LibCrypt)
target_link_libraries(test-crypto LibCrypto LibTLS LibLine)
+target_link_libraries(test-compress LibCompress)
target_link_libraries(test-js LibJS LibLine LibCore)
target_link_libraries(test-web LibWeb)
target_link_libraries(tt LibPthread)
diff --git a/Userland/test-compress.cpp b/Userland/test-compress.cpp
new file mode 100644
index 0000000000..5dfe1439fe
--- /dev/null
+++ b/Userland/test-compress.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibCompress/Deflate.h>
+#include <LibCompress/Zlib.h>
+#include <LibCore/ArgsParser.h>
+
+auto main(int argc, char** argv) -> int
+{
+ const char* mode = nullptr;
+ const char* type = nullptr;
+
+ Core::ArgsParser parser;
+ parser.add_positional_argument(type, "Type of algorithm to apply (Only Zlib and DEFLATE is present at the moment)", "type", Core::ArgsParser::Required::No);
+ parser.add_positional_argument(mode, "Mode to operate in (compress|decompress; Only decompress is valid at the moment)", "mode", Core::ArgsParser::Required::No);
+ parser.parse(argc, argv);
+
+ if (type == nullptr) {
+ type = "deflate";
+ }
+
+ if (mode == nullptr) {
+ mode = "decompress";
+ }
+
+ StringView mode_sv { mode };
+ StringView type_sv { type };
+ if (mode_sv == "decompress") {
+ if (type_sv == "deflate") {
+ // Deflated bytes for the string "This is a simple text file :)"
+ u8 data_bytes[] = {
+ 0x0B, 0xC9, 0xC8, 0x2C,
+ 0x56, 0x00, 0xA2, 0x44,
+ 0x85, 0xE2, 0xCC, 0xDC,
+ 0x82, 0x9C, 0x54, 0x85,
+ 0x92, 0xD4, 0x8A, 0x12,
+ 0x85, 0xB4, 0x4C, 0x20,
+ 0xCB, 0x4A, 0x13, 0x00
+ };
+
+ auto deflater = Compress::Deflate({ data_bytes, 4 * 7 });
+ auto deflated = deflater.decompress();
+ auto decompressed = String((const char*)deflated.data(), deflated.size());
+
+ if (decompressed.equals_ignoring_case("This is a simple text file :)")) {
+ printf("Test PASSED");
+ return 0;
+ } else {
+ printf("Test FAILED");
+ return 1;
+ }
+ }
+
+ if (type_sv == "zlib") {
+ // zlib bytes for the string "This is a simple text file :)"
+ u8 data_bytes[] = {
+ 0x78, 0x01, 0x01, 0x1D, 0x00, 0xE2, 0xFF, 0x54,
+ 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61,
+ 0x20, 0x73, 0x69, 0x6D, 0x70, 0x6C, 0x65, 0x20,
+ 0x74, 0x65, 0x78, 0x74, 0x20, 0x66, 0x69, 0x6C,
+ 0x65, 0x20, 0x3A, 0x29, 0x99, 0x5E, 0x09, 0xE8
+ };
+
+ auto deflater = Compress::Zlib({ data_bytes, 8 * 5 });
+ auto deflated = deflater.decompress();
+ auto decompressed = String((const char*)deflated.data(), deflated.size());
+
+ if (decompressed.equals_ignoring_case("This is a simple text file :)")) {
+ printf("Test PASSED");
+ return 0;
+ } else {
+ printf("Test FAILED");
+ return 1;
+ }
+ }
+ }
+
+ printf("Unknown arguments passed to test!");
+ return 1;
+}