diff --git a/src/compression/AbstractEncoder.h b/src/compression/AbstractEncoder.h new file mode 100644 index 0000000..f20f7ee --- /dev/null +++ b/src/compression/AbstractEncoder.h @@ -0,0 +1,23 @@ +#pragma once + +class BitStream; + +class AbstractEncoder +{ +public: + AbstractEncoder(BitStream* inputStream, BitStream* outputStream) + : mInputStream(inputStream), + mOutputStream(outputStream) + { + + } + + virtual ~AbstractEncoder() = default; + + virtual bool encode() = 0; + virtual bool decode() = 0; + +protected: + BitStream* mInputStream{nullptr}; + BitStream* mOutputStream{nullptr}; +}; diff --git a/src/compression/CMakeLists.txt b/src/compression/CMakeLists.txt index bb4f7a0..27153ac 100644 --- a/src/compression/CMakeLists.txt +++ b/src/compression/CMakeLists.txt @@ -4,6 +4,10 @@ list(APPEND compression_LIB_INCLUDES HuffmanEncoder.cpp RunLengthEncoder.cpp ZlibData.cpp + ZlibEncoder.cpp + DeflateEncoder.cpp + DeflateBlock.cpp + Lz77Encoder.cpp ) add_library(compression SHARED ${compression_LIB_INCLUDES}) diff --git a/src/compression/DeflateBlock.cpp b/src/compression/DeflateBlock.cpp new file mode 100644 index 0000000..8ae07dd --- /dev/null +++ b/src/compression/DeflateBlock.cpp @@ -0,0 +1,262 @@ +#include "DeflateBlock.h" + +#include "ByteUtils.h" + +#include +#include + +DeflateBlock::DeflateBlock(BitStream* inputStream, BitStream* outputStream) + : mInputStream(inputStream), + mOutputStream(outputStream) +{ + +} + +bool DeflateBlock::readNextCodeLengthSymbol(unsigned char& final_symbol) +{ + unsigned working_index{0}; + auto count = mCodeLengthMapping[working_index].first; + auto delta = count; + + bool found{false}; + unsigned char buffer{0}; + unsigned char working_bits{0}; + unsigned working_symbol{0}; + + while(!found) + { + auto valid = mInputStream->readNextNBits(delta, buffer); + working_bits = (working_bits << delta) | buffer; + + for(const auto& entry : mCodeLengthMapping[working_index].second) + { + if (entry.first == working_bits) + { + found = true; + working_symbol = entry.second; + break; + } + } + + if (!found) + { + working_index++; + if (working_index >= mCodeLengthMapping.size()) + { + break; + } + + auto new_count = mCodeLengthMapping[working_index].first; + delta = new_count - count; + count = new_count; + } + } + + if (found) + { + final_symbol = working_symbol; + std::cout << "Found symbol " << working_symbol << " with bits " << ByteUtils::toString(working_bits) << std::endl; + std::cout << "At Byte offset " << mInputStream->getCurrentByteOffset() << " and bit offset " << mInputStream->getCurrentBitOffset() << std::endl; + return true; + } + else + { + std::cout << "SYMBOL NOT FOUND " << " with bits " << ByteUtils::toString(working_bits) << std::endl; + return false; + } +} + +void DeflateBlock::setCodeLengthAlphabetLengths(const std::vector& lengths) +{ + mCodeLengthAlphabetLengths = lengths; +} + +void DeflateBlock::setCodeLengthLength(unsigned length) +{ + mHclen = length; +} + +void DeflateBlock::setLiteralsTableLength(unsigned length) +{ + mHlit = length; +} + +void DeflateBlock::setDistanceTableLength(unsigned length) +{ + mHdist = length; +} + +void DeflateBlock::setIsFinalBlock(bool isFinal) +{ + +} + +void DeflateBlock::flushToStream() +{ + +} + +void DeflateBlock::readLiteralCodeLengths() +{ + std::vector lengths; + unsigned char symbol{0}; + + while(lengths.size() < mHlit) + { + bool valid = readNextCodeLengthSymbol(symbol); + + if (!valid) + { + std::cout << "Hit unknown symbol - bailing out" << std::endl; + break; + } + + if (symbol < 16) + { + lengths.push_back(symbol); + } + else if(symbol == 16) + { + unsigned char num_reps{0}; + mInputStream->readNextNBits(2, num_reps); + + auto last_val = lengths[lengths.size()-1]; + std::cout << "Got val 16 doing " << 3 + num_reps << std::endl; + for(unsigned idx=0; idx< 3 + num_reps; idx++) + { + lengths.push_back(last_val); + } + } + else if(symbol == 17) + { + unsigned char num_reps{0}; + mInputStream->readNextNBits(3, num_reps); + + std::cout << "Got val 17 doing " << 3 + num_reps << std::endl; + for(unsigned idx=0; idx< 3 + num_reps; idx++) + { + lengths.push_back(0); + } + } + else if(symbol == 18) + { + unsigned char num_reps{0}; + mInputStream->readNextNBits(7, num_reps); + + std::cout << "Got val 18 doing " << 11 + num_reps << std::endl; + for(unsigned idx=0; idx< 11 + num_reps; idx++) + { + lengths.push_back(0); + } + } + } +} + +void DeflateBlock::buildCodeLengthMapping() +{ + for(unsigned idx=1; idx<8; idx++) + { + std::vector entries; + for(unsigned jdx=0; jdxreadNextNBits(5, h_lit); + mHlit = h_lit + 257; + std::cout << "Got HLIT " << mHlit << std::endl; + + unsigned char h_dist{0}; + mInputStream->readNextNBits(5, h_dist); + mHdist = h_dist + 1; + std::cout << "Got HDIST " << mHdist << std::endl; + + unsigned char h_clen{0}; + mInputStream->readNextNBits(4, h_clen); + mHclen = h_clen + 4; + std::cout << "Got HCLEN " << mHclen << std::endl; + + mCodeLengthAlphabetLengths = std::vector(19, 0); + unsigned char buffer{0}; + for(unsigned idx = 0; idx< mHclen; idx++) + { + mInputStream->readNextNBits(3, buffer); + mCodeLengthAlphabetLengths[CODE_LENGTH_ALPHABET_PERMUTATION[idx]] = buffer; + std::cout << "Got code length for " << CODE_LENGTH_ALPHABET_PERMUTATION[idx] << " of " << static_cast(buffer) << std::endl; + } + + buildCodeLengthMapping(); + + readLiteralCodeLengths(); +} + +void DeflateBlock::readHeader() +{ + auto working_byte = mInputStream->getCurrentByte(); + std::cout << "Into process data "<< std::endl; + std::cout << mInputStream->logNextNBytes(9); + + unsigned char final_block{0}; + mInputStream->readNextNBits(1, final_block); + mInFinalBlock = bool(final_block); + + if (mInFinalBlock) + { + std::cout << "Got final block" << std::endl; + } + + mInputStream->readNextNBits(2, mCompressionType); + std::cout << "Compress type byte is: " << static_cast(mCompressionType) << std::endl; + if (mCompressionType == NO_COMPRESSION) + { + std::cout << "Got NO_COMPRESSION" << std::endl; + } + else if (mCompressionType == FIXED_HUFFMAN) + { + std::cout << "Got FIXED_HUFFMAN" << std::endl; + } + else if (mCompressionType == DYNAMIC_HUFFMAN) + { + std::cout << "Got DYNAMIC_HUFFMAN" << std::endl; + readDynamicHuffmanTable(); + } + else if (mCompressionType == ERROR) + { + std::cout << "Got ERROR" << std::endl; + } + +} diff --git a/src/compression/DeflateBlock.h b/src/compression/DeflateBlock.h new file mode 100644 index 0000000..bffd269 --- /dev/null +++ b/src/compression/DeflateBlock.h @@ -0,0 +1,60 @@ +#pragma once + +#include "BitStream.h" + +class DeflateBlock +{ +public: + DeflateBlock(BitStream* inputStream, BitStream* outputStream); + + void readHeader(); + + void readDynamicHuffmanTable(); + + void buildCodeLengthMapping(); + + void readLiteralCodeLengths(); + + bool readNextCodeLengthSymbol(unsigned char& buffer); + + bool isFinalBlock() const + { + return mInFinalBlock; + } + + void setCodeLengthAlphabetLengths(const std::vector& lengths); + + void setCodeLengthLength(unsigned length); + + void setLiteralsTableLength(unsigned length); + + void setDistanceTableLength(unsigned length); + + void setIsFinalBlock(bool isFinal); + + void flushToStream(); + +private: + BitStream* mInputStream; + BitStream* mOutputStream; + + unsigned mHlit{0}; + unsigned mHdist{0}; + unsigned mHclen{0}; + + using CodeLengthEntry = std::pair; + using CodeLengthCountEntry = std::pair >; + std::vector mCodeLengthMapping; + + std::vector mCodeLengthAlphabetLengths; + static constexpr unsigned CODE_LENGTH_ALPHABET_PERMUTATION[19]{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + unsigned char mCompressionType{0}; + + bool mInFinalBlock{false}; + + static constexpr unsigned char NO_COMPRESSION = 0x00; + static constexpr unsigned char FIXED_HUFFMAN = 0x01; + static constexpr unsigned char DYNAMIC_HUFFMAN = 0x02; + static constexpr unsigned char ERROR = 0x03; +}; diff --git a/src/compression/DeflateEncoder.cpp b/src/compression/DeflateEncoder.cpp new file mode 100644 index 0000000..6df5680 --- /dev/null +++ b/src/compression/DeflateEncoder.cpp @@ -0,0 +1,38 @@ +#include "DeflateEncoder.h" + +#include "BitStream.h" +#include "DeflateBlock.h" + +#include + +DeflateEncoder::DeflateEncoder(BitStream* inputStream, BitStream* outputStream) + : AbstractEncoder(inputStream, outputStream) +{ + +} + +DeflateEncoder::~DeflateEncoder() +{ + +} + +bool DeflateEncoder::encode() +{ + return false; +} + +bool DeflateEncoder::decode() +{ + auto working_block = std::make_unique(mInputStream, mOutputStream); + working_block->readHeader(); + + DeflateBlock* raw_block = working_block.get(); + + while(!raw_block->isFinalBlock()) + { + break; + } + + return false; +} + diff --git a/src/compression/DeflateEncoder.h b/src/compression/DeflateEncoder.h new file mode 100644 index 0000000..fe0c890 --- /dev/null +++ b/src/compression/DeflateEncoder.h @@ -0,0 +1,25 @@ +#pragma once + +#include "AbstractEncoder.h" + +#include +#include + +class DeflateBlock; + +class DeflateEncoder : public AbstractEncoder +{ +public: + DeflateEncoder(BitStream* inputStream, BitStream* outputStream); + + ~DeflateEncoder(); + + bool encode() override; + + bool decode() override; + +private: + std::vector > mBlocks; +}; + + diff --git a/src/compression/Lz77Encoder.cpp b/src/compression/Lz77Encoder.cpp index e69de29..5fcfe2c 100644 --- a/src/compression/Lz77Encoder.cpp +++ b/src/compression/Lz77Encoder.cpp @@ -0,0 +1,170 @@ +#include "Lz77Encoder.h" + +#include "StringUtils.h" +#include "BitStream.h" + +Lz77Encoder::Lz77Encoder(BitStream* inputStream, BitStream* outputStream) + : AbstractEncoder(inputStream, outputStream) +{ + +} + + +unsigned Lz77Encoder::lookAheadForMatchingChars(std::vector& matchBuffer, unsigned searchIndex, unsigned hitOffset, const std::string& stream, unsigned streamLoc) +{ + auto remaining_size = stream.size() - streamLoc; + + unsigned num_hits{1}; + for (unsigned jdx=1; jdx< remaining_size; jdx++) + { + char buffer_char{0}; + if (searchIndex + jdx < mSearchBuffer.size()) + { + buffer_char = mSearchBuffer[searchIndex + jdx]; + } + else + { + buffer_char = stream[jdx - hitOffset]; + } + + auto lookahead_char = stream[streamLoc + jdx]; + if (lookahead_char == buffer_char) + { + matchBuffer.push_back(buffer_char); + num_hits++; + } + else + { + break; + } + } + return num_hits; +} + +void Lz77Encoder::lookThroughSearchBuffer(char searchChar, unsigned& hitLength, unsigned& hitOffset, const std::string& stream, unsigned streamLoc) +{ + for(unsigned idx=0; idx match_buffer{buffer_char}; + auto num_hits = lookAheadForMatchingChars(match_buffer, search_index, idx, stream, streamLoc); + + if (num_hits >= hitLength) + { + hitLength = num_hits; + hitOffset = idx + 1; + } + } + } +} + +bool Lz77Encoder::encode() +{ + /* + unsigned loc{0}; + std::string ret; + + while(!mInputStream->isFinished()) + { + auto search_char = stream[loc]; + + unsigned hit_length{0}; + unsigned hit_offset{0}; + lookThroughSearchBuffer(search_char, hit_length, hit_offset, stream, loc); + + if (hit_length > 0) + { + ret += "@" + std::to_string(hit_offset) + "L" + std::to_string(hit_length); + loc+=hit_length; + + auto hit_loc = mSearchBuffer.size() - hit_offset; + for(unsigned idx=hit_loc; idx #include -class Lz77Encoder +class Lz77Encoder : public AbstractEncoder { public: - using DataStream = std::vector; + using Buffer = std::vector; - unsigned lookAheadForMatchingChars(std::vector& matchBuffer, unsigned searchIndex, unsigned hitOffset, const std::string& stream, unsigned streamLoc) - { - auto remaining_size = stream.size() - streamLoc; + Lz77Encoder(BitStream* inputStream, BitStream* outputStream); - unsigned num_hits{1}; - for (unsigned jdx=1; jdx< remaining_size; jdx++) - { - char buffer_char{0}; - if (searchIndex + jdx < mSearchBuffer.size()) - { - buffer_char = mSearchBuffer[searchIndex + jdx]; - } - else - { - buffer_char = stream[jdx - hitOffset]; - } + unsigned lookAheadForMatchingChars(std::vector& matchBuffer, unsigned searchIndex, unsigned hitOffset, const std::string& stream, unsigned streamLoc); - auto lookahead_char = stream[streamLoc + jdx]; - if (lookahead_char == buffer_char) - { - matchBuffer.push_back(buffer_char); - num_hits++; - } - else - { - break; - } - } - return num_hits; - } + void lookThroughSearchBuffer(char searchChar, unsigned& hitLength, unsigned& hitOffset, const std::string& stream, unsigned streamLoc); - void lookThroughSearchBuffer(char searchChar, unsigned& hitLength, unsigned& hitOffset, const std::string& stream, unsigned streamLoc) - { - for(unsigned idx=0; idx match_buffer{buffer_char}; - auto num_hits = lookAheadForMatchingChars(match_buffer, search_index, idx, stream, streamLoc); + bool decode() override; - if (num_hits >= hitLength) - { - hitLength = num_hits; - hitOffset = idx + 1; - } - } - } - } + void setSearchBufferSize(unsigned size); - std::string encode(const std::string& stream) - { - unsigned loc{0}; - std::string ret; + void setLookAheadBufferSize(unsigned size); - while(loc < stream.size()) - { - auto search_char = stream[loc]; +private: + unsigned mSearchBufferSize{32000}; + Buffer mSearchBuffer; - unsigned hit_length{0}; - unsigned hit_offset{0}; - lookThroughSearchBuffer(search_char, hit_length, hit_offset, stream, loc); - - if (hit_length > 0) - { - ret += "@" + std::to_string(hit_offset) + "L" + std::to_string(hit_length); - loc+=hit_length; - - auto hit_loc = mSearchBuffer.size() - hit_offset; - for(unsigned idx=hit_loc; idx + +ZlibEncoder::ZlibEncoder(BitStream* inputStream, BitStream* outputStream) +: AbstractEncoder(inputStream, outputStream) +{ + +} + +ZlibEncoder::~ZlibEncoder() +{ + +} + +void ZlibEncoder::setCompressionMethod(unsigned char method) +{ + std::cout << "Got compression input " << static_cast(method) << std::endl; + mCmf = method; + mCompressionMethod = ByteUtils::getLowerNBits(method, 4); + mCompressionInfo = ByteUtils::getHigherNBits(method, 4); + + std::cout << "Got compression method " << static_cast(mCompressionMethod) << " and info " << static_cast(mCompressionInfo) << std::endl; +} + +void ZlibEncoder::setExtraFlags(unsigned char extraFlags) +{ + std::cout << "Got flags " << static_cast(extraFlags) << std::endl; + + mFlg = extraFlags; + mFlagCheck = ByteUtils::getLowerNBits(extraFlags, 5); + mFlagDict = ByteUtils::getBitN(extraFlags, 5); + mFlagLevel = ByteUtils::getHigherNBits(extraFlags, 2); + + std::cout << "Got flag check " << static_cast(mFlagCheck) << " and dict " << static_cast(mFlagDict) << " and level " << static_cast(mFlagLevel) << std::endl; +} + +bool ZlibEncoder::encode() +{ + if (!mWorkingEncoder) + { + if (mCompressionMethod == 8) + { + mWorkingEncoder = std::make_unique(mInputStream, mOutputStream); + } + else + { + MLOG_ERROR("Zib requested decoder not recognized: " << mCompressionMethod << " aborting encode"); + return false; + } + } + return mWorkingEncoder->encode(); +} + +bool ZlibEncoder::decode() +{ + if (!mWorkingEncoder) + { + if (mCompressionMethod == 8) + { + mWorkingEncoder = std::make_unique(mInputStream, mOutputStream); + } + else + { + MLOG_ERROR("Zib requested decoder not recognized: " << mCompressionMethod << " aborting decode"); + return false; + } + } + return mWorkingEncoder->decode(); +} diff --git a/src/compression/ZlibEncoder.h b/src/compression/ZlibEncoder.h new file mode 100644 index 0000000..53077e3 --- /dev/null +++ b/src/compression/ZlibEncoder.h @@ -0,0 +1,34 @@ +#pragma once + +#include "AbstractEncoder.h" + +#include +#include + +class ZlibEncoder : public AbstractEncoder +{ + +public: + ZlibEncoder(BitStream* inputStream, BitStream* outputStream); + ~ZlibEncoder(); + + void setCompressionMethod(unsigned char method); + + void setExtraFlags(unsigned char extraFlags); + + bool encode() override; + + bool decode() override; + +private: + unsigned char mCmf{0}; + unsigned char mFlg{0}; + unsigned char mCompressionMethod{8}; + unsigned char mCompressionInfo{0}; + unsigned char mFlagCheck{0}; + unsigned char mFlagDict{0}; + unsigned char mFlagLevel{0}; + unsigned char mCheckValue{0}; + + std::unique_ptr mWorkingEncoder; +}; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 91bfdc4..2227000 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -30,6 +30,9 @@ list(APPEND core_LIB_INCLUDES StringUtils.cpp streams/BinaryStream.cpp streams/BitStream.cpp + streams/InputBitStream.cpp + streams/OutputBitStream.cpp + streams/BufferBitStream.cpp http/HttpResponse.cpp http/HttpHeader.cpp http/HttpRequest.cpp diff --git a/src/core/StringUtils.cpp b/src/core/StringUtils.cpp index d3cc5a2..467b1e2 100644 --- a/src/core/StringUtils.cpp +++ b/src/core/StringUtils.cpp @@ -25,6 +25,16 @@ bool StringUtils::IsAlphabetical(char c) return std::isalpha(c); } +std::vector StringUtils::toBytes(const std::string& input) +{ + return {input.begin(), input.end()}; +} + +std::string StringUtils::toString(const std::vector& bytes) +{ + return {bytes.begin(), bytes.end()}; +} + std::vector StringUtils::toLines(const std::string& input) { auto result = std::vector{}; diff --git a/src/core/StringUtils.h b/src/core/StringUtils.h index 14c6784..41dbf42 100644 --- a/src/core/StringUtils.h +++ b/src/core/StringUtils.h @@ -30,4 +30,8 @@ public: static std::vector toLines(const std::string& input); static std::string stripQuotes(const std::string& input); + + static std::vector toBytes(const std::string& input); + static std::string toString(const std::vector& bytes); + }; diff --git a/src/core/streams/BitStream.cpp b/src/core/streams/BitStream.cpp index ed3bdf6..dc1b044 100644 --- a/src/core/streams/BitStream.cpp +++ b/src/core/streams/BitStream.cpp @@ -2,37 +2,70 @@ #include "ByteUtils.h" -bool BitStream::loadNextByte() +#include +#include + +BitStream::~BitStream() { - if (mByteOffset + 1 == mBuffer.size()) - { - return false; - } - else - { - mByteOffset++; - mCurrentByte = mBuffer[mByteOffset]; - return true; - } + } -bool BitStream::getNextNBits(unsigned n, unsigned char& buffer) +unsigned char BitStream::getCurrentByte() { - int overshoot = n + mBitOffset - 7; + if (mByteOffset < 0) + { + readNextByte(); + } + return mCurrentByte; +} + +int BitStream::getCurrentByteOffset() const +{ + return mByteOffset; +} + +unsigned BitStream::getCurrentBitOffset() const +{ + return mBitOffset; +} + +std::string BitStream::logNextNBytes(unsigned n) const +{ + std::stringstream sstr; + unsigned count{0}; + for(auto byte : peekNextNBytes(n)) + { + sstr << count << " | " << ByteUtils::toString(byte) + '\n'; + count++; + } + return sstr.str(); +} + +bool BitStream::readNextNBits(unsigned n, unsigned char& buffer) +{ + if (mByteOffset < 0) + { + if (!readNextByte()) + { + return false; + } + } + + int overshoot = n + mBitOffset - 8; if (overshoot > 0) { unsigned char last_byte = mCurrentByte; - if (!loadNextByte()) + if (!readNextByte()) { return false; } - auto num_lower = 7 - mBitOffset; + auto num_lower = 8 - mBitOffset; char lower_bits = ByteUtils::getHigherNBits(last_byte, num_lower); char higher_bits = ByteUtils::getLowerNBits(mCurrentByte, overshoot); - buffer = (higher_bits << (8 - num_lower)) | (lower_bits >> mBitOffset); + buffer = (higher_bits << num_lower) | lower_bits; mBitOffset = overshoot; return true; @@ -44,13 +77,3 @@ bool BitStream::getNextNBits(unsigned n, unsigned char& buffer) return true; } } - -void BitStream::setByte(unsigned idx, unsigned char data) -{ - mBuffer[idx] = data; -} - -void BitStream::setBufferSize(std::size_t size) -{ - mBuffer = std::vector(size); -} diff --git a/src/core/streams/BitStream.h b/src/core/streams/BitStream.h index c8193e5..9dd3fc6 100644 --- a/src/core/streams/BitStream.h +++ b/src/core/streams/BitStream.h @@ -1,27 +1,35 @@ #pragma once #include +#include +#include class BitStream { public: - bool getNextNBits(unsigned n, unsigned char& buffer); + virtual ~BitStream(); - bool loadNextByte(); + unsigned char getCurrentByte(); - void setByte(unsigned idx, unsigned char data); + int getCurrentByteOffset() const; - void setBufferSize(std::size_t size); + unsigned getCurrentBitOffset() const; - unsigned char getCurrentByte() const - { - return mCurrentByte; - } + virtual bool isFinished() const = 0; -private: - unsigned mByteOffset{0}; + std::string logNextNBytes(unsigned n) const; + + virtual std::vector peekNextNBytes(unsigned n) const = 0; + + virtual bool readNextNBits(unsigned n, unsigned char& buffer); + + virtual std::optional readNextByte() = 0; + + virtual void writeByte(unsigned char data) = 0; + +protected: + int mByteOffset{0}; unsigned mBitOffset{0}; - char mCurrentByte{0}; - std::vector mBuffer; + unsigned char mCurrentByte{0}; }; diff --git a/src/core/streams/BufferBitStream.cpp b/src/core/streams/BufferBitStream.cpp new file mode 100644 index 0000000..4f794c9 --- /dev/null +++ b/src/core/streams/BufferBitStream.cpp @@ -0,0 +1,49 @@ +#include "BufferBitStream.h" + +bool BufferBitStream::isFinished() const +{ + return mByteOffset == mBuffer.size(); +} + +std::vector BufferBitStream::peekNextNBytes(unsigned n) const +{ + std::vector ret (n, 0); + unsigned count = 0; + for(unsigned idx=mByteOffset; idx BufferBitStream::readNextByte() +{ + if (mByteOffset + 1 == mBuffer.size()) + { + return std::nullopt; + } + else + { + mByteOffset++; + mCurrentByte = mBuffer[mByteOffset]; + return mCurrentByte; + } +} + +void BufferBitStream::setBuffer(const std::vector& data) +{ + mBuffer = data; +} + +void BufferBitStream::writeByte(unsigned char data) +{ + mBuffer.push_back(data); +} + + diff --git a/src/core/streams/BufferBitStream.h b/src/core/streams/BufferBitStream.h new file mode 100644 index 0000000..bc898c4 --- /dev/null +++ b/src/core/streams/BufferBitStream.h @@ -0,0 +1,27 @@ +#pragma once + +#include "BitStream.h" + +#include + +class BufferBitStream : public BitStream +{ +public: + bool isFinished() const override; + + std::vector peekNextNBytes(unsigned n) const override; + + std::optional readNextByte() override; + + void setBuffer(const std::vector& data); + + void writeByte(unsigned char data) override; + + const std::vector& getBuffer() const + { + return mBuffer; + } + +private: + std::vector mBuffer; +}; diff --git a/src/core/streams/InputBitStream.cpp b/src/core/streams/InputBitStream.cpp new file mode 100644 index 0000000..955311a --- /dev/null +++ b/src/core/streams/InputBitStream.cpp @@ -0,0 +1,35 @@ +#include "InputBitStream.h" + +InputBitStream::InputBitStream(std::basic_istream* stream) + : BitStream(), + mStream(stream) +{ + +} + +bool InputBitStream::isFinished() const +{ + return mStream->good(); +} + +std::vector InputBitStream::peekNextNBytes(unsigned n) const +{ + return {}; +} + +std::optional InputBitStream::readNextByte() +{ + if (mStream->good()) + { + return mStream->get(); + } + else + { + return std::nullopt; + } +} + +void InputBitStream::writeByte(unsigned char data) +{ + +} diff --git a/src/core/streams/InputBitStream.h b/src/core/streams/InputBitStream.h new file mode 100644 index 0000000..5831fe6 --- /dev/null +++ b/src/core/streams/InputBitStream.h @@ -0,0 +1,21 @@ +#pragma once + +#include "BitStream.h" + +#include + +class InputBitStream : public BitStream +{ + InputBitStream(std::basic_istream* stream); + + bool isFinished() const override; + + std::vector peekNextNBytes(unsigned n) const override; + + std::optional readNextByte() override; + + void writeByte(unsigned char data) override; + +private: + std::basic_istream* mStream{nullptr}; +}; diff --git a/src/core/streams/OutputBitStream.cpp b/src/core/streams/OutputBitStream.cpp new file mode 100644 index 0000000..5e05194 --- /dev/null +++ b/src/core/streams/OutputBitStream.cpp @@ -0,0 +1,28 @@ +#include "OutputBitStream.h" + +OutputBitStream::OutputBitStream(std::basic_ostream* stream) + : BitStream(), + mStream(stream) +{ + +} + +bool OutputBitStream::isFinished() const +{ + return true; +} + +std::vector OutputBitStream::peekNextNBytes(unsigned n) const +{ + return {}; +} + +std::optional OutputBitStream::readNextByte() +{ + return std::nullopt; +} + +void OutputBitStream::writeByte(unsigned char data) +{ + (*mStream) << data; +} diff --git a/src/core/streams/OutputBitStream.h b/src/core/streams/OutputBitStream.h new file mode 100644 index 0000000..5dc59a1 --- /dev/null +++ b/src/core/streams/OutputBitStream.h @@ -0,0 +1,22 @@ +#pragma once + +#include "BitStream.h" + +#include + +class OutputBitStream : public BitStream +{ +public: + OutputBitStream(std::basic_ostream* stream); + + bool isFinished() const override; + + std::vector peekNextNBytes(unsigned n) const override; + + std::optional readNextByte() override; + + void writeByte(unsigned char data) override; + +private: + std::basic_ostream* mStream{nullptr}; +}; diff --git a/src/image/CMakeLists.txt b/src/image/CMakeLists.txt index b93813d..2880f4f 100644 --- a/src/image/CMakeLists.txt +++ b/src/image/CMakeLists.txt @@ -7,6 +7,7 @@ list(APPEND image_LIB_INCLUDES Image.cpp PngWriter.cpp PngReader.cpp + ImageBitStream.cpp ) list(APPEND image_LIBS core compression) diff --git a/src/image/ImageBitStream.cpp b/src/image/ImageBitStream.cpp new file mode 100644 index 0000000..e65324c --- /dev/null +++ b/src/image/ImageBitStream.cpp @@ -0,0 +1,29 @@ +#include "ImageBitStream.h" + +ImageBitStream::ImageBitStream(Image* image) + : BitStream(), + mImage(image) +{ + +} + +bool ImageBitStream::isFinished() const +{ + return true; +} + +std::vector ImageBitStream::peekNextNBytes(unsigned n) const +{ + return {}; +} + +std::optional ImageBitStream::readNextByte() +{ + return std::nullopt; +} + +void ImageBitStream::writeByte(unsigned char data) +{ + +} + diff --git a/src/image/ImageBitStream.h b/src/image/ImageBitStream.h new file mode 100644 index 0000000..0663bd0 --- /dev/null +++ b/src/image/ImageBitStream.h @@ -0,0 +1,22 @@ +#pragma once + +#include "BitStream.h" + +#include "Image.h" + +class ImageBitStream : public BitStream +{ +public: + ImageBitStream(Image* image); + + bool isFinished() const override; + + std::vector peekNextNBytes(unsigned n) const override; + + std::optional readNextByte() override; + + void writeByte(unsigned char data) override; + +private: + Image* mImage{nullptr}; +}; diff --git a/src/image/PngElements.h b/src/image/PngElements.h index 05f48ce..5125c80 100644 --- a/src/image/PngElements.h +++ b/src/image/PngElements.h @@ -3,9 +3,18 @@ #include #include - namespace Png { + inline unsigned char getHighBitCheck() + { + return 0x89; + } + + inline std::vector getSignature() + { + return {13, 10, 26, 10}; + } + struct IHDRChunk { unsigned width{0}; diff --git a/src/image/PngReader.cpp b/src/image/PngReader.cpp index 47ea371..77fefe0 100644 --- a/src/image/PngReader.cpp +++ b/src/image/PngReader.cpp @@ -1,6 +1,10 @@ #include "PngReader.h" #include "BinaryStream.h" +#include "BitStream.h" +#include "BufferBitStream.h" + +#include "ZlibEncoder.h" #include #include @@ -84,12 +88,11 @@ void PngReader::readIDATChunk(unsigned length) { if (mAwaitingDataBlock) { - mImageData.setCompressionMethod(mFile->readNextByte()); - mImageData.setExtraFlags(mFile->readNextByte()); - mImageData.setDataSize(length-2); + mEncoder->setCompressionMethod(mFile->readNextByte()); + mEncoder->setExtraFlags(mFile->readNextByte()); for(unsigned idx=0; idxreadNextByte()); + mInputStream->writeByte(mFile->readNextByte()); } mAwaitingDataBlock = false; } @@ -97,7 +100,7 @@ void PngReader::readIDATChunk(unsigned length) { for(unsigned idx=0; idxreadNextByte()); + mInputStream->writeByte(mFile->readNextByte()); } } } @@ -135,10 +138,14 @@ std::unique_ptr > PngReader::read() return image; } + mInputStream = std::make_unique(); + mOutputStream = std::make_unique(); + mEncoder = std::make_unique(mInputStream.get(), mOutputStream.get()); + while(readChunk()) { } - mImageData.processData(); + mEncoder->decode(); return std::move(image); } diff --git a/src/image/PngReader.h b/src/image/PngReader.h index b2062e0..a921c00 100644 --- a/src/image/PngReader.h +++ b/src/image/PngReader.h @@ -3,12 +3,15 @@ #include "File.h" #include "Image.h" #include "PngElements.h" -#include "ZlibData.h" +#include "ZlibEncoder.h" #include #include #include +class BitStream; +class ZlibEncoder; + using Path = std::filesystem::path; class PngReader @@ -37,6 +40,8 @@ private: std::unique_ptr mFile; Path mPath; - ZlibData mImageData; + std::unique_ptr mEncoder; + std::unique_ptr mInputStream; + std::unique_ptr mOutputStream; bool mAwaitingDataBlock{true}; }; diff --git a/src/image/PngWriter.cpp b/src/image/PngWriter.cpp index e3b53c2..3064021 100644 --- a/src/image/PngWriter.cpp +++ b/src/image/PngWriter.cpp @@ -1,6 +1,13 @@ #include "PngWriter.h" +#include "PngElements.h" #include "Image.h" +#include "File.h" +#include "BufferBitStream.h" +#include "OutputBitStream.h" +#include "ImageBitStream.h" + +#include "Lz77Encoder.h" #include @@ -9,6 +16,11 @@ PngWriter::PngWriter() } +PngWriter::~PngWriter() +{ + +} + std::unique_ptr PngWriter::Create() { return std::make_unique(); @@ -19,8 +31,40 @@ void PngWriter::setPath(const Path& path) mPath = path; } -void PngWriter::write(const std::unique_ptr >& image) const +void PngWriter::writeSignature() { + mOutStream->writeByte(Png::getHighBitCheck()); + for (auto byte : Png::getSignature()) + { + mOutStream->writeByte(byte); + } +} + +void PngWriter::writeHeader() +{ + writeSignature(); +} + +void PngWriter::write(const std::unique_ptr >& image) +{ + if (!mPath.empty()) + { + mWorkingFile = std::make_unique(mPath); + mWorkingFile->Open(true); + mOutStream = std::make_unique(mWorkingFile->GetOutHandle()); + } + else + { + mOutStream = std::make_unique(); + } + + mWorkingImage = image.get(); + mInStream = std::make_unique(image.get()); + + writeHeader(); + + + //mImpl->write(image); //auto fp = fopen(mPath.c_str(), "wb"); @@ -76,4 +120,9 @@ void PngWriter::write(const std::unique_ptr >& image) const //fclose(fp); //return; + + if (mWorkingFile) + { + mWorkingFile->Close(); + } } diff --git a/src/image/PngWriter.h b/src/image/PngWriter.h index 3712d56..b8e8656 100644 --- a/src/image/PngWriter.h +++ b/src/image/PngWriter.h @@ -8,19 +8,31 @@ using Path = std::filesystem::path; +class BitStream; +class File; + class PngWriter { public: PngWriter(); + ~PngWriter(); static std::unique_ptr Create(); void setPath(const Path& path); - void write(const std::unique_ptr >& image) const; + void write(const std::unique_ptr >& image); private: + void writeSignature(); + void writeHeader(); + + Path mPath; + Image* mWorkingImage{nullptr}; + std::unique_ptr mInStream; + std::unique_ptr mOutStream; + std::unique_ptr mWorkingFile; }; using PngWriterPtr = std::unique_ptr; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 919d76f..9b44e1f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,6 +12,7 @@ list(APPEND TestFiles audio/TestMidiReader.cpp core/TestByteUtils.cpp core/TestBinaryStream.cpp + core/TestBitStream.cpp core/TestTomlReader.cpp compiler/TestLexer.cpp compiler/TestTemplatingEngine.cpp diff --git a/test/compression/TestStreamCompressor.cpp b/test/compression/TestStreamCompressor.cpp index 323a80e..4f4af7c 100644 --- a/test/compression/TestStreamCompressor.cpp +++ b/test/compression/TestStreamCompressor.cpp @@ -1,5 +1,6 @@ #include +#include "BufferBitStream.h" #include "HuffmanEncoder.h" #include "RunLengthEncoder.h" #include "Lz77Encoder.h" @@ -42,10 +43,14 @@ void test_lz77_encoder() std::string test_data = "sir sid eastman easily teases sea sick seals"; //std::string test_data = "sir sid eastman"; - Lz77Encoder encoder; - auto encoded = encoder.encode(test_data); + BufferBitStream input_stream; + input_stream.setBuffer(StringUtils::toBytes(test_data)); - std::cout << "Encoded: " << encoded << std::endl; + BufferBitStream output_stream; + Lz77Encoder encoder(&input_stream, &output_stream); + encoder.encode(); + + std::cout << "Encoded: " << StringUtils::toString(output_stream.getBuffer()) << std::endl; //auto decoded = encoder.decode(encoded); diff --git a/test/core/TestBitStream.cpp b/test/core/TestBitStream.cpp new file mode 100644 index 0000000..e8aa34e --- /dev/null +++ b/test/core/TestBitStream.cpp @@ -0,0 +1,30 @@ +#include "ByteUtils.h" +#include "BufferBitStream.h" + +#include + +int main() +{ + std::vector bytes{"11100101", "00110101", "00010001"}; + + BufferBitStream stream; + for(const auto& byte : bytes) + { + stream.writeByte(ByteUtils::getFromString(byte)); + } + + unsigned char buffer{0} ; + auto valid = stream.readNextNBits(3, buffer); + std::cout << "Slice0 is " << ByteUtils::toString(buffer) << std::endl; + + valid = stream.readNextNBits(3, buffer); + std::cout << "Slice1 is " << ByteUtils::toString(buffer) << std::endl; + + valid = stream.readNextNBits(5, buffer); + std::cout << "Slice2 is " << ByteUtils::toString(buffer) << std::endl; + + valid = stream.readNextNBits(7, buffer); + std::cout << "Slice3 is " << ByteUtils::toString(buffer) << std::endl; + + return 0; +} diff --git a/test/image/TestPngReader.cpp b/test/image/TestPngReader.cpp index 7062b02..d1f2cdb 100644 --- a/test/image/TestPngReader.cpp +++ b/test/image/TestPngReader.cpp @@ -1,11 +1,15 @@ #include "PngReader.h" +#include "BitStream.h" + #include "Image.h" #include int main() { - const auto path = "/home/jmsgrogan/Downloads/test.png"; + //const auto path = "/home/jmsgrogan/Downloads/test.png"; + + const auto path = "/home/jmsgrogan/Downloads/index.png"; PngReader reader; reader.setPath(path); diff --git a/test/image/TestPngWriter.cpp b/test/image/TestPngWriter.cpp index 2124afd..26c6549 100644 --- a/test/image/TestPngWriter.cpp +++ b/test/image/TestPngWriter.cpp @@ -1,6 +1,8 @@ #include "Image.h" #include "PngWriter.h" + +#include "BitStream.h" #include "ImagePrimitives.h" #include