#include <config.h>
#include "checksum.h"
#include "google/encodetable.h"
#include "google/output_string.h"
#include "google/vcencoder.h"
#include "google/jsonwriter.h"
#include "logging.h"
#include "unique_ptr.h"
#include "vcdiffengine.h"
namespace open_vcdiff {
namespace {
CodeTableWriterInterface* create_writer(
VCDiffFormatExtensionFlags format_extensions) {
if (format_extensions & VCD_FORMAT_JSON) {
return new JSONCodeTableWriter();
} else {
return new VCDiffCodeTableWriter(
(format_extensions & VCD_FORMAT_INTERLEAVED) != 0);
}
}
}
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<VCDiffEngine*>(engine_)->Init();
}
class VCDiffStreamingEncoderImpl {
public:
VCDiffStreamingEncoderImpl(const HashedDictionary* dictionary,
VCDiffFormatExtensionFlags format_extensions,
bool look_for_target_matches,
CodeTableWriterInterface* writer);
bool StartEncoding(OutputStringInterface* out);
bool EncodeChunk(const char* data, size_t len, OutputStringInterface* out);
bool FinishEncoding(OutputStringInterface* out);
private:
const VCDiffEngine* engine_;
UNIQUE_PTR<CodeTableWriterInterface> coder_;
const VCDiffFormatExtensionFlags format_extensions_;
const bool look_for_target_matches_;
bool encode_chunk_allowed_;
VCDiffStreamingEncoderImpl(const VCDiffStreamingEncoderImpl&); void operator=(const VCDiffStreamingEncoderImpl&);
};
inline VCDiffStreamingEncoderImpl::VCDiffStreamingEncoderImpl(
const HashedDictionary* dictionary,
VCDiffFormatExtensionFlags format_extensions,
bool look_for_target_matches,
CodeTableWriterInterface* writer)
: engine_(dictionary->engine()),
coder_(writer),
format_extensions_(format_extensions),
look_for_target_matches_(look_for_target_matches),
encode_chunk_allowed_(false) { }
inline bool VCDiffStreamingEncoderImpl::StartEncoding(
OutputStringInterface* out) {
if (!coder_->Init(engine_->dictionary_size())) {
VCD_DFATAL << "Internal error: "
"Initialization of code table writer failed" << VCD_ENDL;
return false;
}
if (!coder_->VerifyDictionary(engine_->dictionary(),
engine_->dictionary_size())) {
VCD_ERROR << "Dictionary not valid for writer" << VCD_ENDL;
return false;
}
coder_->WriteHeader(out, format_extensions_);
encode_chunk_allowed_ = true;
return true;
}
inline bool VCDiffStreamingEncoderImpl::EncodeChunk(
const char* data,
size_t len,
OutputStringInterface* out) {
if (!encode_chunk_allowed_) {
VCD_ERROR << "EncodeChunk called before StartEncoding" << VCD_ENDL;
return false;
}
if (!coder_->VerifyChunk(data, len)) {
VCD_ERROR << "Target chunk not valid for writer" << VCD_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_.get());
return true;
}
inline bool VCDiffStreamingEncoderImpl::FinishEncoding(
OutputStringInterface* out) {
if (!encode_chunk_allowed_) {
VCD_ERROR << "FinishEncoding called before StartEncoding" << VCD_ENDL;
return false;
}
encode_chunk_allowed_ = false;
coder_->FinishEncoding(out);
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,
create_writer(format_extensions))) { }
VCDiffStreamingEncoder::VCDiffStreamingEncoder(
const HashedDictionary* dictionary,
VCDiffFormatExtensionFlags format_extensions,
bool look_for_target_matches,
CodeTableWriterInterface* writer)
: impl_(new VCDiffStreamingEncoderImpl(dictionary,
format_extensions,
look_for_target_matches,
writer)) { }
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);
}
bool VCDiffEncoder::EncodeToInterface(const char* target_data,
size_t target_len,
OutputStringInterface* out) {
out->clear();
if (!encoder_) {
if (!dictionary_.Init()) {
VCD_ERROR << "Error initializing HashedDictionary" << VCD_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);
}
}