// Copyright 2008 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.
#include <config.h>
#include "headerparser.h"
#include "logging.h"
#include "varint_bigendian.h"
#include "vcdiff_defs.h"
namespace open_vcdiff {
// *** Methods for ParseableChunk
void ParseableChunk::Advance(size_t number_of_bytes) {
if (number_of_bytes > UnparsedSize()) {
VCD_DFATAL << "Internal error: position advanced by " << number_of_bytes
<< " bytes, current unparsed size " << UnparsedSize()
<< VCD_ENDL;
position_ = end_;
return;
}
position_ += number_of_bytes;
}
void ParseableChunk::SetPosition(const char* position) {
if (position < start_) {
VCD_DFATAL << "Internal error: new data position " << position
<< " is beyond start of data " << start_ << VCD_ENDL;
position_ = start_;
return;
}
if (position > end_) {
VCD_DFATAL << "Internal error: new data position " << position
<< " is beyond end of data " << end_ << VCD_ENDL;
position_ = end_;
return;
}
position_ = position;
}
void ParseableChunk::FinishExcept(size_t number_of_bytes) {
if (number_of_bytes > UnparsedSize()) {
VCD_DFATAL << "Internal error: specified number of remaining bytes "
<< number_of_bytes << " is greater than unparsed data size "
<< UnparsedSize() << VCD_ENDL;
Finish();
return;
}
position_ = end_ - number_of_bytes;
}
// *** Methods for VCDiffHeaderParser
VCDiffHeaderParser::VCDiffHeaderParser(const char* header_start,
const char* data_end)
: parseable_chunk_(header_start, data_end - header_start),
return_code_(RESULT_SUCCESS),
delta_encoding_length_(0),
delta_encoding_start_(NULL) { }
bool VCDiffHeaderParser::ParseByte(unsigned char* value) {
if (RESULT_SUCCESS != return_code_) {
return false;
}
if (parseable_chunk_.Empty()) {
return_code_ = RESULT_END_OF_DATA;
return false;
}
*value = static_cast<unsigned char>(*parseable_chunk_.UnparsedData());
parseable_chunk_.Advance(1);
return true;
}
bool VCDiffHeaderParser::ParseInt32(const char* variable_description,
int32_t* value) {
if (RESULT_SUCCESS != return_code_) {
return false;
}
int32_t parsed_value =
VarintBE<int32_t>::Parse(parseable_chunk_.End(),
parseable_chunk_.UnparsedDataAddr());
switch (parsed_value) {
case RESULT_ERROR:
VCD_ERROR << "Expected " << variable_description
<< "; found invalid variable-length integer" << VCD_ENDL;
return_code_ = RESULT_ERROR;
return false;
case RESULT_END_OF_DATA:
return_code_ = RESULT_END_OF_DATA;
return false;
default:
*value = parsed_value;
return true;
}
}
// When an unsigned 32-bit integer is expected, parse a signed 64-bit value
// instead, then check the value limit. The uint32_t type can't be parsed
// directly because two negative values are given special meanings (RESULT_ERROR
// and RESULT_END_OF_DATA) and could not be expressed in an unsigned format.
bool VCDiffHeaderParser::ParseUInt32(const char* variable_description,
uint32_t* value) {
if (RESULT_SUCCESS != return_code_) {
return false;
}
int64_t parsed_value =
VarintBE<int64_t>::Parse(parseable_chunk_.End(),
parseable_chunk_.UnparsedDataAddr());
switch (parsed_value) {
case RESULT_ERROR:
VCD_ERROR << "Expected " << variable_description
<< "; found invalid variable-length integer" << VCD_ENDL;
return_code_ = RESULT_ERROR;
return false;
case RESULT_END_OF_DATA:
return_code_ = RESULT_END_OF_DATA;
return false;
default:
if (parsed_value > 0xFFFFFFFF) {
VCD_ERROR << "Value of " << variable_description << "(" << parsed_value
<< ") is too large for unsigned 32-bit integer" << VCD_ENDL;
return_code_ = RESULT_ERROR;
return false;
}
*value = static_cast<uint32_t>(parsed_value);
return true;
}
}
// A VCDChecksum represents an unsigned 32-bit value returned by adler32(),
// but isn't a uint32_t.
bool VCDiffHeaderParser::ParseChecksum(const char* variable_description,
VCDChecksum* value) {
uint32_t parsed_value = 0;
if (!ParseUInt32(variable_description, &parsed_value)) {
return false;
}
*value = static_cast<VCDChecksum>(parsed_value);
return true;
}
bool VCDiffHeaderParser::ParseSize(const char* variable_description,
size_t* value) {
int32_t parsed_value = 0;
if (!ParseInt32(variable_description, &parsed_value)) {
return false;
}
*value = static_cast<size_t>(parsed_value);
return true;
}
bool VCDiffHeaderParser::ParseSourceSegmentLengthAndPosition(
size_t from_size,
const char* from_boundary_name,
const char* from_name,
size_t* source_segment_length,
size_t* source_segment_position) {
// Verify the length and position values
if (!ParseSize("source segment length", source_segment_length)) {
return false;
}
// Guard against overflow by checking source length first
if (*source_segment_length > from_size) {
VCD_ERROR << "Source segment length (" << *source_segment_length
<< ") is larger than " << from_name << " (" << from_size
<< ")" << VCD_ENDL;
return_code_ = RESULT_ERROR;
return false;
}
if (!ParseSize("source segment position", source_segment_position)) {
return false;
}
if ((*source_segment_position >= from_size) &&
(*source_segment_length > 0)) {
VCD_ERROR << "Source segment position (" << *source_segment_position
<< ") is past " << from_boundary_name
<< " (" << from_size << ")" << VCD_ENDL;
return_code_ = RESULT_ERROR;
return false;
}
const size_t source_segment_end = *source_segment_position +
*source_segment_length;
if (source_segment_end > from_size) {
VCD_ERROR << "Source segment end position (" << source_segment_end
<< ") is past " << from_boundary_name
<< " (" << from_size << ")" << VCD_ENDL;
return_code_ = RESULT_ERROR;
return false;
}
return true;
}
bool VCDiffHeaderParser::ParseWinIndicatorAndSourceSegment(
size_t dictionary_size,
size_t decoded_target_size,
bool allow_vcd_target,
unsigned char* win_indicator,
size_t* source_segment_length,
size_t* source_segment_position) {
if (!ParseByte(win_indicator)) {
return false;
}
unsigned char source_target_flags =
*win_indicator & (VCD_SOURCE | VCD_TARGET);
switch (source_target_flags) {
case VCD_SOURCE:
return ParseSourceSegmentLengthAndPosition(dictionary_size,
"end of dictionary",
"dictionary",
source_segment_length,
source_segment_position);
case VCD_TARGET:
if (!allow_vcd_target) {
VCD_ERROR << "Delta file contains VCD_TARGET flag, which is not "
"allowed by current decoder settings" << VCD_ENDL;
return_code_ = RESULT_ERROR;
return false;
}
return ParseSourceSegmentLengthAndPosition(decoded_target_size,
"current target position",
"target file",
source_segment_length,
source_segment_position);
case VCD_SOURCE | VCD_TARGET:
VCD_ERROR << "Win_Indicator must not have both VCD_SOURCE"
" and VCD_TARGET set" << VCD_ENDL;
return_code_ = RESULT_ERROR;
return false;
default:
return true;
}
}
bool VCDiffHeaderParser::ParseWindowLengths(size_t* target_window_length) {
if (delta_encoding_start_) {
VCD_DFATAL << "Internal error: VCDiffHeaderParser::ParseWindowLengths "
"was called twice for the same delta window" << VCD_ENDL;
return_code_ = RESULT_ERROR;
return false;
}
if (!ParseSize("length of the delta encoding", &delta_encoding_length_)) {
return false;
}
delta_encoding_start_ = UnparsedData();
if (!ParseSize("size of the target window", target_window_length)) {
return false;
}
return true;
}
const char* VCDiffHeaderParser::EndOfDeltaWindow() const {
if (!delta_encoding_start_) {
VCD_DFATAL << "Internal error: VCDiffHeaderParser::GetDeltaWindowEnd "
"was called before ParseWindowLengths" << VCD_ENDL;
return NULL;
}
return delta_encoding_start_ + delta_encoding_length_;
}
bool VCDiffHeaderParser::ParseDeltaIndicator() {
unsigned char delta_indicator;
if (!ParseByte(&delta_indicator)) {
return false;
}
if (delta_indicator & (VCD_DATACOMP | VCD_INSTCOMP | VCD_ADDRCOMP)) {
VCD_ERROR << "Secondary compression of delta file sections "
"is not supported" << VCD_ENDL;
return_code_ = RESULT_ERROR;
return false;
}
return true;
}
bool VCDiffHeaderParser::ParseSectionLengths(
bool has_checksum,
size_t* add_and_run_data_length,
size_t* instructions_and_sizes_length,
size_t* addresses_length,
VCDChecksum* checksum) {
ParseSize("length of data for ADDs and RUNs", add_and_run_data_length);
ParseSize("length of instructions section", instructions_and_sizes_length);
ParseSize("length of addresses for COPYs", addresses_length);
if (has_checksum) {
ParseChecksum("Adler32 checksum value", checksum);
}
if (RESULT_SUCCESS != return_code_) {
return false;
}
if (!delta_encoding_start_) {
VCD_DFATAL << "Internal error: VCDiffHeaderParser::ParseSectionLengths "
"was called before ParseWindowLengths" << VCD_ENDL;
return_code_ = RESULT_ERROR;
return false;
}
const size_t delta_encoding_header_length =
UnparsedData() - delta_encoding_start_;
if (delta_encoding_length_ !=
(delta_encoding_header_length +
*add_and_run_data_length +
*instructions_and_sizes_length +
*addresses_length)) {
VCD_ERROR << "The length of the delta encoding does not match "
"the size of the header plus the sizes of the data sections"
<< VCD_ENDL;
return_code_ = RESULT_ERROR;
return false;
}
return true;
}
} // namespace open_vcdiff