// Copyright 2007 Google Inc. // Author: Lincoln Smith // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Classes to implement an Encoder for the format described in // RFC 3284 - The VCDIFF Generic Differencing and Compression Data Format. // The RFC text can be found at http://www.faqs.org/rfcs/rfc3284.html // // The RFC describes the possibility of using a secondary compressor // to further reduce the size of each section of the VCDIFF output. // That feature is not supported in this implementation of the encoder // and decoder. // No secondary compressor types have been publicly registered with // the IANA at http://www.iana.org/assignments/vcdiff-comp-ids // in the more than five years since the registry was created, so there // is no standard set of compressor IDs which would be generated by other // encoders or accepted by other decoders. #include #include "google/vcencoder.h" #include #include "checksum.h" #include "encodetable.h" #include "logging.h" #include "google/output_string.h" #include "vcdiffengine.h" namespace open_vcdiff { HashedDictionary::HashedDictionary(const char* dictionary_contents, size_t dictionary_size) : engine_(new VCDiffEngine(dictionary_contents, dictionary_size)) { } HashedDictionary::~HashedDictionary() { delete engine_; } bool HashedDictionary::Init() { return const_cast(engine_)->Init(); } class VCDiffStreamingEncoderImpl { public: VCDiffStreamingEncoderImpl(const HashedDictionary* dictionary, VCDiffFormatExtensionFlags format_extensions, bool look_for_target_matches); // These functions are identical to their counterparts // in VCDiffStreamingEncoder. bool StartEncoding(OutputStringInterface* out); bool EncodeChunk(const char* data, size_t len, OutputStringInterface* out); bool FinishEncoding(OutputStringInterface* out); const std::vector& match_counts() const { return coder_.match_counts(); } private: // Write the header (as defined in section 4.1 of the RFC) to *output. // This includes information that can be gathered // before the first chunk of input is available. void WriteHeader(OutputStringInterface* output) const; const VCDiffEngine* engine_; // This implementation of the encoder uses the default // code table. A VCDiffCodeTableWriter could also be constructed // using a custom code table. VCDiffCodeTableWriter coder_; const VCDiffFormatExtensionFlags format_extensions_; // Determines whether to look for matches within the previously encoded // target data, or just within the source (dictionary) data. Please see // vcencoder.h for a full explanation of this parameter. const bool look_for_target_matches_; // This state variable is used to ensure that StartEncoding(), EncodeChunk(), // and FinishEncoding() are called in the correct order. It will be true // if StartEncoding() has been called, followed by zero or more calls to // EncodeChunk(), but FinishEncoding() has not yet been called. It will // be false initially, and also after FinishEncoding() has been called. bool encode_chunk_allowed_; // Making these private avoids implicit copy constructor & assignment operator VCDiffStreamingEncoderImpl(const VCDiffStreamingEncoderImpl&); // NOLINT void operator=(const VCDiffStreamingEncoderImpl&); }; inline VCDiffStreamingEncoderImpl::VCDiffStreamingEncoderImpl( const HashedDictionary* dictionary, VCDiffFormatExtensionFlags format_extensions, bool look_for_target_matches) : engine_(dictionary->engine()), coder_((format_extensions & VCD_FORMAT_INTERLEAVED) != 0), format_extensions_(format_extensions), look_for_target_matches_(look_for_target_matches), encode_chunk_allowed_(false) { } inline void VCDiffStreamingEncoderImpl::WriteHeader( OutputStringInterface* output) const { DeltaFileHeader header_data = { 0xD6, // Header1: "V" | 0x80 0xC3, // Header2: "C" | 0x80 0xC4, // Header3: "D" | 0x80 0x00, // Header4: Draft standard format 0x00 }; // Hdr_Indicator: // No compression, no custom code table if (format_extensions_ != VCD_STANDARD_FORMAT) { header_data.header4 = 'S'; // Header4: VCDIFF/SDCH, extensions used } output->append(reinterpret_cast(&header_data), sizeof(header_data)); // If custom cache table sizes or a custom code table were used // for encoding, here is where they would be appended to *output. // This implementation of the encoder does not use those features, // although the decoder can understand and interpret them. } inline bool VCDiffStreamingEncoderImpl::StartEncoding( OutputStringInterface* out) { if (!coder_.Init(engine_->dictionary_size())) { LOG(DFATAL) << "Internal error: " "Initialization of code table writer failed" << LOG_ENDL; return false; } WriteHeader(out); encode_chunk_allowed_ = true; return true; } inline bool VCDiffStreamingEncoderImpl::EncodeChunk( const char* data, size_t len, OutputStringInterface* out) { if (!encode_chunk_allowed_) { LOG(ERROR) << "EncodeChunk called before StartEncoding" << LOG_ENDL; return false; } if ((format_extensions_ & VCD_FORMAT_CHECKSUM) != 0) { coder_.AddChecksum(ComputeAdler32(data, len)); } engine_->Encode(data, len, look_for_target_matches_, out, &coder_); return true; } inline bool VCDiffStreamingEncoderImpl::FinishEncoding( OutputStringInterface* /*out*/) { if (!encode_chunk_allowed_) { LOG(ERROR) << "FinishEncoding called before StartEncoding" << LOG_ENDL; return false; } encode_chunk_allowed_ = false; // There should not be any need to output more data // since EncodeChunk() encodes a complete target window // and there is no end-of-delta-file marker. return true; } VCDiffStreamingEncoder::VCDiffStreamingEncoder( const HashedDictionary* dictionary, VCDiffFormatExtensionFlags format_extensions, bool look_for_target_matches) : impl_(new VCDiffStreamingEncoderImpl(dictionary, format_extensions, look_for_target_matches)) { } VCDiffStreamingEncoder::~VCDiffStreamingEncoder() { delete impl_; } bool VCDiffStreamingEncoder::StartEncodingToInterface( OutputStringInterface* out) { return impl_->StartEncoding(out); } bool VCDiffStreamingEncoder::EncodeChunkToInterface( const char* data, size_t len, OutputStringInterface* out) { return impl_->EncodeChunk(data, len, out); } bool VCDiffStreamingEncoder::FinishEncodingToInterface( OutputStringInterface* out) { return impl_->FinishEncoding(out); } void VCDiffStreamingEncoder::GetMatchCounts( std::vector* match_counts) const { if (!match_counts) { LOG(DFATAL) << "GetMatchCounts() called with NULL argument" << LOG_ENDL; return; } *match_counts = impl_->match_counts(); } bool VCDiffEncoder::EncodeToInterface(const char* target_data, size_t target_len, OutputStringInterface* out) { out->clear(); if (!encoder_) { if (!dictionary_.Init()) { LOG(ERROR) << "Error initializing HashedDictionary" << LOG_ENDL; return false; } encoder_ = new VCDiffStreamingEncoder(&dictionary_, flags_, look_for_target_matches_); } if (!encoder_->StartEncodingToInterface(out)) { return false; } if (!encoder_->EncodeChunkToInterface(target_data, target_len, out)) { return false; } return encoder_->FinishEncodingToInterface(out); } } // namespace open_vcdiff