#include <config.h>
#include <limits.h>
#include <string>
#include "addrcache.h"
#include "codetable.h"
#include "google/encodetable.h"
#include "instruction_map.h"
#include "logging.h"
#include "google/output_string.h"
#include "varint_bigendian.h"
#include "vcdiff_defs.h"
namespace open_vcdiff {
static const DeltaFileHeader kHeaderStandardFormat = {
0xD6, 0xC3, 0xC4, 0x00, 0x00 };
static const DeltaFileHeader kHeaderExtendedFormat = {
0xD6, 0xC3, 0xC4, 'S', 0x00 };
VCDiffCodeTableWriter::VCDiffCodeTableWriter(bool interleaved)
: max_mode_(VCDiffAddressCache::DefaultLastMode()),
dictionary_size_(0),
target_length_(0),
code_table_data_(&VCDiffCodeTableData::kDefaultCodeTableData),
instruction_map_(NULL),
last_opcode_index_(-1),
add_checksum_(false),
checksum_(0) {
InitSectionPointers(interleaved);
}
VCDiffCodeTableWriter::VCDiffCodeTableWriter(
bool interleaved,
unsigned char near_cache_size,
unsigned char same_cache_size,
const VCDiffCodeTableData& code_table_data,
unsigned char max_mode)
: max_mode_(max_mode),
address_cache_(near_cache_size, same_cache_size),
dictionary_size_(0),
target_length_(0),
code_table_data_(&code_table_data),
instruction_map_(NULL),
last_opcode_index_(-1),
add_checksum_(false),
checksum_(0) {
InitSectionPointers(interleaved);
}
VCDiffCodeTableWriter::~VCDiffCodeTableWriter() {
if (code_table_data_ != &VCDiffCodeTableData::kDefaultCodeTableData) {
delete instruction_map_;
}
}
void VCDiffCodeTableWriter::InitSectionPointers(bool interleaved) {
if (interleaved) {
data_for_add_and_run_ = &instructions_and_sizes_;
addresses_for_copy_ = &instructions_and_sizes_;
} else {
data_for_add_and_run_ = &separate_data_for_add_and_run_;
addresses_for_copy_ = &separate_addresses_for_copy_;
}
}
bool VCDiffCodeTableWriter::Init(size_t dictionary_size) {
dictionary_size_ = dictionary_size;
if (!instruction_map_) {
if (code_table_data_ == &VCDiffCodeTableData::kDefaultCodeTableData) {
instruction_map_ = VCDiffInstructionMap::GetDefaultInstructionMap();
} else {
instruction_map_ = new VCDiffInstructionMap(*code_table_data_, max_mode_);
}
if (!instruction_map_) {
return false;
}
}
if (!address_cache_.Init()) {
return false;
}
target_length_ = 0;
last_opcode_index_ = -1;
return true;
}
void VCDiffCodeTableWriter::WriteHeader(
OutputStringInterface* out,
VCDiffFormatExtensionFlags format_extensions) {
if (format_extensions == VCD_STANDARD_FORMAT) {
out->append(reinterpret_cast<const char*>(&kHeaderStandardFormat),
sizeof(kHeaderStandardFormat));
} else {
out->append(reinterpret_cast<const char*>(&kHeaderExtendedFormat),
sizeof(kHeaderExtendedFormat));
}
}
void VCDiffCodeTableWriter::EncodeInstruction(VCDiffInstructionType inst,
size_t size,
unsigned char mode) {
if (!instruction_map_) {
VCD_DFATAL << "EncodeInstruction() called without calling Init()"
<< VCD_ENDL;
return;
}
if (last_opcode_index_ >= 0) {
const unsigned char last_opcode =
instructions_and_sizes_[last_opcode_index_];
if ((inst == VCD_ADD) &&
(code_table_data_->inst1[last_opcode] == VCD_ADD)) {
VCD_WARNING << "EncodeInstruction() called for two ADD instructions"
" in a row" << VCD_ENDL;
}
OpcodeOrNone compound_opcode = kNoOpcode;
if (size <= UCHAR_MAX) {
compound_opcode = instruction_map_->LookupSecondOpcode(
last_opcode, static_cast<unsigned char>(inst),
static_cast<unsigned char>(size), mode);
if (compound_opcode != kNoOpcode) {
instructions_and_sizes_[last_opcode_index_] =
static_cast<unsigned char>(compound_opcode);
last_opcode_index_ = -1;
return;
}
}
compound_opcode = instruction_map_->LookupSecondOpcode(
last_opcode, static_cast<unsigned char>(inst), 0, mode);
if (compound_opcode != kNoOpcode) {
instructions_and_sizes_[last_opcode_index_] =
static_cast<unsigned char>(compound_opcode);
last_opcode_index_ = -1;
AppendSizeToString(size, &instructions_and_sizes_);
return;
}
}
OpcodeOrNone opcode = kNoOpcode;
if (size <= UCHAR_MAX) {
opcode = instruction_map_->LookupFirstOpcode(
static_cast<unsigned char>(inst), static_cast<unsigned char>(size),
mode);
if (opcode != kNoOpcode) {
instructions_and_sizes_.push_back(static_cast<char>(opcode));
last_opcode_index_ = static_cast<int>(instructions_and_sizes_.size() - 1);
return;
}
}
opcode = instruction_map_->LookupFirstOpcode(
static_cast<unsigned char>(inst), 0, mode);
if (opcode == kNoOpcode) {
VCD_DFATAL << "No matching opcode found for inst " << inst
<< ", mode " << mode << ", size 0" << VCD_ENDL;
return;
}
instructions_and_sizes_.push_back(static_cast<char>(opcode));
last_opcode_index_ = static_cast<int>(instructions_and_sizes_.size() - 1);
AppendSizeToString(size, &instructions_and_sizes_);
}
void VCDiffCodeTableWriter::Add(const char* data, size_t size) {
EncodeInstruction(VCD_ADD, size);
data_for_add_and_run_->append(data, size);
target_length_ += size;
}
void VCDiffCodeTableWriter::Copy(int32_t offset, size_t size) {
if (!instruction_map_) {
VCD_DFATAL << "VCDiffCodeTableWriter::Copy() called without calling Init()"
<< VCD_ENDL;
return;
}
int32_t encoded_addr = 0;
const unsigned char mode = address_cache_.EncodeAddress(
offset,
static_cast<VCDAddress>(dictionary_size_ + target_length_),
&encoded_addr);
EncodeInstruction(VCD_COPY, size, mode);
if (address_cache_.WriteAddressAsVarintForMode(mode)) {
VarintBE<int32_t>::AppendToString(encoded_addr, addresses_for_copy_);
} else {
addresses_for_copy_->push_back(static_cast<unsigned char>(encoded_addr));
}
target_length_ += size;
}
void VCDiffCodeTableWriter::Run(size_t size, unsigned char byte) {
EncodeInstruction(VCD_RUN, size);
data_for_add_and_run_->push_back(byte);
target_length_ += size;
}
size_t VCDiffCodeTableWriter::CalculateLengthOfSizeAsVarint(size_t size) {
return VarintBE<int32_t>::Length(static_cast<int32_t>(size));
}
void VCDiffCodeTableWriter::AppendSizeToString(size_t size, string* out) {
VarintBE<int32_t>::AppendToString(static_cast<int32_t>(size), out);
}
void VCDiffCodeTableWriter::AppendSizeToOutputString(
size_t size,
OutputStringInterface* out) {
VarintBE<int32_t>::AppendToOutputString(static_cast<int32_t>(size), out);
}
size_t VCDiffCodeTableWriter::CalculateLengthOfTheDeltaEncoding() const {
size_t length_of_the_delta_encoding =
CalculateLengthOfSizeAsVarint(target_length_) +
1 + CalculateLengthOfSizeAsVarint(separate_data_for_add_and_run_.size()) +
CalculateLengthOfSizeAsVarint(instructions_and_sizes_.size()) +
CalculateLengthOfSizeAsVarint(separate_addresses_for_copy_.size()) +
separate_data_for_add_and_run_.size() +
instructions_and_sizes_.size() +
separate_addresses_for_copy_.size();
if (add_checksum_) {
length_of_the_delta_encoding +=
VarintBE<int64_t>::Length(static_cast<int64_t>(checksum_));
}
return length_of_the_delta_encoding;
}
void VCDiffCodeTableWriter::Output(OutputStringInterface* out) {
if (instructions_and_sizes_.empty()) {
VCD_WARNING << "Empty input; no delta window produced" << VCD_ENDL;
} else {
const size_t length_of_the_delta_encoding =
CalculateLengthOfTheDeltaEncoding();
const size_t delta_window_size =
length_of_the_delta_encoding +
1 + CalculateLengthOfSizeAsVarint(dictionary_size_) +
CalculateLengthOfSizeAsVarint(0) +
CalculateLengthOfSizeAsVarint(length_of_the_delta_encoding);
out->ReserveAdditionalBytes(delta_window_size);
if (add_checksum_) {
out->push_back(VCD_SOURCE | VCD_CHECKSUM);
} else {
out->push_back(VCD_SOURCE);
}
AppendSizeToOutputString(dictionary_size_, out);
AppendSizeToOutputString(0, out);
AppendSizeToOutputString(length_of_the_delta_encoding, out);
const size_t size_before_delta_encoding = out->size();
AppendSizeToOutputString(target_length_, out);
out->push_back(0x00); AppendSizeToOutputString(separate_data_for_add_and_run_.size(), out);
AppendSizeToOutputString(instructions_and_sizes_.size(), out);
AppendSizeToOutputString(separate_addresses_for_copy_.size(), out);
if (add_checksum_) {
VarintBE<int64_t>::AppendToOutputString(static_cast<int64_t>(checksum_),
out);
}
out->append(separate_data_for_add_and_run_.data(),
separate_data_for_add_and_run_.size());
out->append(instructions_and_sizes_.data(),
instructions_and_sizes_.size());
out->append(separate_addresses_for_copy_.data(),
separate_addresses_for_copy_.size());
const size_t size_after_delta_encoding = out->size();
if (length_of_the_delta_encoding !=
(size_after_delta_encoding - size_before_delta_encoding)) {
VCD_DFATAL << "Internal error: calculated length of the delta encoding ("
<< length_of_the_delta_encoding
<< ") does not match actual length ("
<< (size_after_delta_encoding - size_before_delta_encoding)
<< VCD_ENDL;
}
separate_data_for_add_and_run_.clear();
instructions_and_sizes_.clear();
separate_addresses_for_copy_.clear();
if (target_length_ == 0) {
VCD_WARNING << "Empty target window" << VCD_ENDL;
}
}
if (!Init(dictionary_size_)) {
VCD_DFATAL << "Internal error: calling Init() to reset "
"VCDiffCodeTableWriter state failed" << VCD_ENDL;
}
}
bool VCDiffCodeTableWriter::VerifyDictionary(const char * ,
size_t ) const {
return true;
}
bool VCDiffCodeTableWriter::VerifyChunk(const char * ,
size_t ) const {
return true;
}
};