diff options
Diffstat (limited to 'sdch/open-vcdiff/src/headerparser.cc')
-rw-r--r-- | sdch/open-vcdiff/src/headerparser.cc | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/sdch/open-vcdiff/src/headerparser.cc b/sdch/open-vcdiff/src/headerparser.cc new file mode 100644 index 0000000..02f7f31 --- /dev/null +++ b/sdch/open-vcdiff/src/headerparser.cc @@ -0,0 +1,323 @@ +// 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()) { + LOG(DFATAL) << "Internal error: position advanced by " << number_of_bytes + << " bytes, current unparsed size " << UnparsedSize() + << LOG_ENDL; + position_ = end_; + return; + } + position_ += number_of_bytes; +} + +void ParseableChunk::SetPosition(const char* position) { + if (position < start_) { + LOG(DFATAL) << "Internal error: new data position " << position + << " is beyond start of data " << start_ << LOG_ENDL; + position_ = start_; + return; + } + if (position > end_) { + LOG(DFATAL) << "Internal error: new data position " << position + << " is beyond end of data " << end_ << LOG_ENDL; + position_ = end_; + return; + } + position_ = position; +} + +void ParseableChunk::FinishExcept(size_t number_of_bytes) { + if (number_of_bytes > UnparsedSize()) { + LOG(DFATAL) << "Internal error: specified number of remaining bytes " + << number_of_bytes << " is greater than unparsed data size " + << UnparsedSize() << LOG_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: + LOG(ERROR) << "Expected " << variable_description + << "; found invalid variable-length integer" << LOG_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: + LOG(ERROR) << "Expected " << variable_description + << "; found invalid variable-length integer" << LOG_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) { + LOG(ERROR) << "Value of " << variable_description << "(" << parsed_value + << ") is too large for unsigned 32-bit integer" << LOG_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) { + LOG(ERROR) << "Source segment length (" << *source_segment_length + << ") is larger than " << from_name << " (" << from_size + << ")" << LOG_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)) { + LOG(ERROR) << "Source segment position (" << *source_segment_position + << ") is past " << from_boundary_name + << " (" << from_size << ")" << LOG_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) { + LOG(ERROR) << "Source segment end position (" << source_segment_end + << ") is past " << from_boundary_name + << " (" << from_size << ")" << LOG_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) { + LOG(ERROR) << "Delta file contains VCD_TARGET flag, which is not " + "allowed by current decoder settings" << LOG_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: + LOG(ERROR) << "Win_Indicator must not have both VCD_SOURCE" + " and VCD_TARGET set" << LOG_ENDL; + return_code_ = RESULT_ERROR; + return false; + default: + return true; + } +} + +bool VCDiffHeaderParser::ParseWindowLengths(size_t* target_window_length) { + if (delta_encoding_start_) { + LOG(DFATAL) << "Internal error: VCDiffHeaderParser::ParseWindowLengths " + "was called twice for the same delta window" << LOG_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_) { + LOG(DFATAL) << "Internal error: VCDiffHeaderParser::GetDeltaWindowEnd " + "was called before ParseWindowLengths" << LOG_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)) { + LOG(ERROR) << "Secondary compression of delta file sections " + "is not supported" << LOG_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_) { + LOG(DFATAL) << "Internal error: VCDiffHeaderParser::ParseSectionLengths " + "was called before ParseWindowLengths" << LOG_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)) { + LOG(ERROR) << "The length of the delta encoding does not match " + "the size of the header plus the sizes of the data sections" + << LOG_ENDL; + return_code_ = RESULT_ERROR; + return false; + } + return true; +} + +} // namespace open_vcdiff |