aboutsummaryrefslogtreecommitdiff
path: root/src/headerparser.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/headerparser.cc')
-rw-r--r--src/headerparser.cc323
1 files changed, 323 insertions, 0 deletions
diff --git a/src/headerparser.cc b/src/headerparser.cc
new file mode 100644
index 0000000..7a7b875
--- /dev/null
+++ b/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()) {
+ 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