#include "Lz77Encoder.h" #include "StringUtils.h" #include "BitStream.h" #include "ByteUtils.h" #include "HuffmanEncoder.h" #include Lz77Encoder::Lz77Encoder(BitStream* inputStream, BitStream* outputStream) : AbstractEncoder(inputStream, outputStream), mSearchBuffer(mSearchBufferSize), mLookaheadBuffer(mLookAheadBufferSize) { } void Lz77Encoder::setPrefixCodeGenerator(std::unique_ptr generator) { mCodeGenerator = std::move(generator); } bool Lz77Encoder::hitBufferFull() const { return mHitBuffer.size() == mMaxHitBufferSize; } void Lz77Encoder::populateSearchBuffer(const Hit& hit) { const auto& [length, distance, next_char] = hit; if (length == 0) { mSearchBuffer.addItem(next_char); } else { std::vector new_items(distance, 0); for(unsigned idx=0 ;idx 0) { for(unsigned idx=0; idx(search_offset)); } unsigned char lookahead_char = mLookaheadBuffer.getItem(idx); if ((lookahead_char != search_char) || (idx == mMaxLookAheadBufferIndex)) { if (idx + 1>= mMinLengthMatchSize) { length = idx + 1; } break; } } return length; } void Lz77Encoder::lookForMatches(unsigned char searchChar, unsigned& hitLength, unsigned& hitOffset) { for (unsigned idx = 0; idx< mSearchBuffer.getNumItems(); idx++) { if (mSearchBuffer.getItem(mSearchBuffer.getNumItems() - 1 - idx) == searchChar) { auto num_hits = lookAheadForMatchingChars(idx + 1); if (num_hits > 0 && num_hits >= hitLength) { hitLength = num_hits; hitOffset = idx + 1; } } } } bool Lz77Encoder::lookAheadSourceEmpty() const { if (mLookaheadBuffer.getNumItems() < mLookAheadBufferSize) { return true; } if (mMaxLookAheadBufferIndex < int(mLookAheadBufferSize) - 1) { return true; } return false; } void Lz77Encoder::populateLookaheadBuffer(unsigned size, bool firstPass) { if (!firstPass && lookAheadSourceEmpty()) { for(unsigned idx=0; idxreadNextByte(); if (!byte) { stream_finished = true; stream_end_id = idx -1; mLookaheadBuffer.addItem(0); mMaxLookAheadBufferIndex--; continue; } else { mLookaheadBuffer.addItem(*byte); } } else { mLookaheadBuffer.addItem(0); mMaxLookAheadBufferIndex--; } } if (stream_finished && firstPass) { mMaxLookAheadBufferIndex = stream_end_id; } } bool Lz77Encoder::encode() { if (!mCodeGenerator) { mCodeGenerator = std::make_unique(); } // Fill the lookahead buffer mMaxLookAheadBufferIndex = mLookAheadBufferSize - 1; populateLookaheadBuffer(mLookAheadBufferSize, true); if(mMaxLookAheadBufferIndex < 0) { return true; } bool input_stream_ended{false}; while(!hitBufferFull()) { if (mMaxLookAheadBufferIndex < 0) { input_stream_ended = true; break; } const auto working_byte = mLookaheadBuffer.getItem(0); unsigned hit_length{0}; unsigned hit_distance{0}; lookForMatches(working_byte, hit_length, hit_distance); const Hit hit{hit_length, hit_distance, working_byte}; mHitBuffer.push_back(hit); populateSearchBuffer(hit); if (hit_length == 0) { populateLookaheadBuffer(1); } else { populateLookaheadBuffer(hit_length); } } return input_stream_ended; } const std::vector& Lz77Encoder::getHitBuffer() const { return mHitBuffer; } /* void Lz77Encoder::flushHitBuffer() { // If dynamic huffman build trees if (!mCodeGenerator) { mCodeGenerator = std::make_unique(); } // Convert hit buffer to prefix codes and write to output stream for (const auto& hit : mHitBuffer) { const auto& [length, distance, next_char] = hit; PrefixCode code; if (length == 0) { code = *mCodeGenerator->getLiteralValue(next_char); std::cout << "Writing symbol " << static_cast(next_char) << " with code " << ByteUtils::toString(code.getData(), code.getLength()) << "\n"; mOutputStream->writeNBits(code.getData(), code.getLength()); } else { code = *mCodeGenerator->getLengthValue(length); const auto distance_code = mCodeGenerator->getDistanceValue(distance); std::cout << "Writing length " << length << " with code " << ByteUtils::toString(code.getData(), code.getLength()) << "\n"; mOutputStream->writeNBits(code.getData(), code.getLength()); std::cout << "Writing distance " << distance << " with code " << ByteUtils::toString(distance_code.getData(), distance_code.getLength()) << "\n"; mOutputStream->writeNBits(distance_code.getData(), distance_code.getLength()); } } auto eos_code = mCodeGenerator->getEndOfStreamValue(); std::cout << "Writing EOS value with code " << ByteUtils::toString(eos_code->getData(), eos_code->getLength()) << "\n"; mOutputStream->writeNBits(eos_code->getData(), eos_code->getLength()); } */ bool Lz77Encoder::decode() { /* std::string ret; unsigned loc{0}; while(loc < stream.size()) { auto working_char = stream[loc]; if (working_char == '@') { unsigned loc_working = loc; auto remainder = stream.size() - loc; std::string offset; unsigned length_loc{0}; for(unsigned jdx=0; jdx< remainder; jdx++) { loc++; auto offset_char = stream[loc]; if (offset_char == 'L') { loc++; break; } else { offset += offset_char; } } unsigned offset_amount = std::stoul(offset); std::string length; remainder = stream.size() - loc; for(unsigned jdx=0; jdx< remainder; jdx++) { auto length_char = stream[loc]; if (StringUtils::IsAlphabetical(length_char) || length_char == '@') { break; } else { loc++; length += length_char; } } unsigned length_amount = std::stoul(length); auto buffer_index = ret.size() - offset_amount; for(unsigned jdx=buffer_index;jdx